runtime是動態語言的一個特征,初學者在編寫程序的時候對這種運行時機制也是特別模糊。網上對這個東西的教程也是一大篇,所以具體的原理我在這里就不介紹了,我在這邊學了實際開發中用到的幾個小案例拿出來給童鞋找點感覺。
字典轉模型
字典轉模型是我們在實際開發中經常遇到的問題,我們可以自己一個一個把接口獲取下來的json數據賦值給model里的屬性,也可以采用網上流行的3方庫來進行裝換。
其實 實現這一個功能很簡單(大牛的3方庫,優化以及細節都考慮的很全面)。
我的基本思路是這樣:我首先將字典的keys取出來,之后將model里面的成員變量獲取到。對比key和成員變量的名稱,如果相同就將value 賦值給成員變量。
實際代碼:
聲明一個NSObject的類目:
#import "Extension.h"
#import <objc/runtime.h>
@implementation NSObject(Extension)
+ (id)dicToModel:(NSDictionary *)dict {
//創建調用對象
id per = [self new];
//要獲取model類的屬性
unsigned int outCount;
Ivar *vars = class_copyIvarList([self class], &outCount);
//遍歷傳過來的數組
for (id name1 in dict.allKeys) {
NSString *name = [NSString stringWithFormat:@"_%@",name1];
for (int i = 0; i < outCount; i++) {
const char *varName = ivar_getName(vars[i]);
NSString *runName = [NSString stringWithUTF8String:varName];
if ([runName isEqualToString:name]) {
//字典里的屬性和model里面的屬性 吻合則轉換
//用這個方法的話,基本上屬性 得特殊處理
// object_setIvar(per, vars[i], [dict objectForKey:name1]);
[per setValue:[dict objectForKey:name1] forKey:runName];
}
}
}
return per;
}
@end
聲明一個Person類:
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger *age;
@end
實際使用:
//字典轉模型
NSDictionary *dict = @{@"name":@"李雷",@"age":@18};
Person *per = [Person dicToModel:dict];
NSLog(@"%@=======%ld",per.name,per.age);
打印結果:
2016-03-07 16:10:07.068 DicToModel[3639:134187] 李雷=======18
使AlertView 像UIAlertViewController 一樣初始化只有直接處理點擊事件
我們在使用alertView的時候 如果想處理點擊方法 必須要實現代理方法,在代理方法里面處理點擊事件。如果在一個C上有多個alertView公用一個代理方法。這時候,對我們使用起來和閱讀都有一些麻煩。
而UIAlertViewController 可以初始化之后 設置點擊方法。我感覺這樣對代碼的閱讀直觀一些,就自己寫了一個類目來處理這些東西。
//使用
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"22" message:@"hah" delegate:nil cancelButtonTitle:@"cancel" otherButtonTitles:@"sure", nil];
[alert handleSureAction:^(NSInteger index) {
if (index == 1) {
NSLog(@"確定被點擊");
}else {
NSLog(@"取消被點擊");
}
}];
[alert show];
為alertView 新加的類目
//
// UIAlertView+ActionBlock.h
// classCluster
//
// Created by DQ on 16/4/8.
// Copyright ? 2016年 DQ. All rights reserved.
//
#import <UIKit/UIKit.h>
typedef void (^handleAction)(NSInteger index) ;
@interface UIAlertView (ActionBlock)<UIAlertViewDelegate>
- (void) handleSureAction:(handleAction) handle ;
@end
//
// UIAlertView+ActionBlock.m
// classCluster
//
// Created by DQ on 16/4/8.
// Copyright ? 2016年 DQ. All rights reserved.
//
#import "UIAlertView+ActionBlock.h"
#import <objc/runtime.h>
static void *KAlertViewActionKey = "alertViewKey";
@implementation UIAlertView (ActionBlock)
- (void)handleSureAction:(handleAction)handle {
self.delegate = self;
objc_setAssociatedObject(self, KAlertViewActionKey, handle, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
handleAction handle = objc_getAssociatedObject(self, KAlertViewActionKey);
handle(buttonIndex);
}
@end
引入這個類目之后,就可以愉快的處理點擊事件了。
======更新
method swizzling 全局修改字體
//
// UILabel+ChangeFont.m
// methodSwizzling-changeFont
//
// Created by DQ on 16/4/28.
// Copyright ? 2016年 DQ. All rights reserved.
//
#import "UILabel+ChangeFont.h"
#import <objc/runtime.h>
@implementation UILabel (ChangeFont)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL oriSEL = @selector(willMoveToSuperview:);
SEL swizzlingSEL = @selector(myMovetoSuperView:);
Method oriMethod = class_getInstanceMethod([self class], oriSEL);
Method swizzlingMethod = class_getInstanceMethod([self class], swizzlingSEL);
BOOL isadd = class_addMethod([self class], oriSEL,method_getImplementation(swizzlingMethod), method_getTypeEncoding(swizzlingMethod));
if (isadd) {
class_replaceMethod([self class], swizzlingSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
}else {
method_exchangeImplementations(oriMethod, swizzlingMethod);
}
});
}
- (void)myMovetoSuperView:(UIView *)superView {
[self myMovetoSuperView:superView];
self.font = [UIFont fontWithName:@"Aladdin" size:30];
}
這邊有一個疑問就是,為什么不是直接交換方法,而是判斷有沒有添加?
百度南峰子博客下面評論有答案
1.didAddMethod一直為NO,是因為你當前類重載了父類的viewWillAppear方法。如果當前類沒有重寫父類的方法class_addMethod會把新的IMP替換掉父類的IMP,也就是重寫了父類方法,并且返回yes;如果當前類重寫了父類方法,就不在替換IMP,直接返回NO。
這是oc原話:
- @note class_addMethod will add an override of a superclass's implementation,
- but will not replace an existing implementation in this class.
- To change an existing implementation, use method_setImplementation.
如果說的有什么不對,還請提出,大家共同學習。最后感謝博主的無私分享。好人一生平安!
2.添加是為了重寫父類的 viewWillAppear 方法,如果你自己已經手動重寫了 viewWillAppear 方法,class_addMethod() 就會添加失敗,返回 NO。
如果返回 NO ,說明已經重寫,就直接交換。
如果返回 YES, 說明添加成功,也就是你之前沒有重寫 viewWillAppear 方法。因為在添加方法里面是將 originalSelector 與 swizzledMethod 的 IMP 綁定在一起了,所以接下來只用將 swizzledSelector 與 originalMethod 的 IMP 綁定就可以了。也就實現了交換。