如今看起來Runtime篇整理的少了,有時間再完善下,將就着看吧數組
Objective-C將不少靜態語言在編譯和連接時期作的工做放在了Runtime運行時處理,能夠說Runtime就是Objective-C的幕後工做者。bash
任何方法調用的本質,就是發送了一個消息(用Runtime發送消息,OC底層實現經過Runtime實現)。markdown
對象根據方法編號SEL去隱射表查找對應的方法實現。併發
有時候咱們須要對類的方法進行修改,可是又沒法拿到源碼,咱們即可以經過Runtime來交換方法實現。函數
+ (void)load { //獲取實例方法實現 Method method1 = class_getInstanceMethod(self, @selector(show)); Method method2 = class_getInstanceMethod(self, @selector(ln_show)); //獲取類方法實現 // Method method3 = class_getClassMethod(self, @selector(show)); // Method method4 = class_getClassMethod(self, @selector(ln_show)); //交換兩個方法的實現 method_exchangeImplementations(method1, method2); //將method1的實現換成method2 // method_setImplementation(method1, method_getImplementation(method2)); } - (void)show { NSLog(@"show person"); } - (void)ln_show { NSLog(@"show person exchange"); } 複製代碼
實際上並無產生真正的成員變量,經過關聯對象來實現,具體參考分類。spa
除了可使用KVC實現外,還能夠經過Runtime實現,就是取出全部ivars遍歷賦值。但實際狀況通常比較複雜:指針
+ (instancetype)modelWithDic:(NSDictionary *)dic { /* 1.初始化實例對象 */ id object = [[self alloc] init]; /** 2.獲取ivars class_copyIvarList: 獲取類中的全部成員變量 Ivar:成員變量 第一個參數:表示獲取哪一個類中的成員變量 第二個參數:表示這個類有多少成員變量,傳入一個Int變量地址,會自動給這個變量賦值 返回值Ivar *:指的是一個ivar數組,會把全部成員屬性放在一個數組中,經過返回的數組就能所有獲取到。 count: 成員變量個數 */ unsigned int count = 0; Ivar *ivarList = class_copyIvarList(self, &count); /* 3.遍歷賦值 */ for(int i = 0; i < count; i++) { //獲取ivar屬性 Ivar ivar = ivarList[i]; //獲取屬性名 NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)]; //去掉成員變量的下劃線 NSString *key = [ivarName substringFromIndex:1]; //獲取dic中對應值 id value = dic[ivarName]; //若是值存在,則賦值 if(value) { [object setValue:value forKey:ivarName]; } } return object; } 複製代碼
若是一個類方法很是多,加載類到內存的時候也比較耗費資源,須要給每一個方法生成映射表,可使用動態給某個類,添加方法解決。code
- (void)viewDidLoad { [super viewDidLoad]; Person *p = [[Person alloc] init]; // 默認person,沒有實現run:方法,能夠經過performSelector調用,可是會報錯。 // 動態添加方法就不會報錯 [p performSelector:@selector(run:) withObject:@10]; } @implementation Person // 沒有返回值,1個參數 // void,(id,SEL) void aaa(id self, SEL _cmd, NSNumber *meter) { NSLog(@"跑了%@米", meter); } // 任何方法默認都有兩個隱式參數,self,_cmd(當前方法的方法編號) // 何時調用:只要一個對象調用了一個未實現的方法就會調用這個方法,進行處理 // 做用:動態添加方法,處理未實現 + (BOOL)resolveInstanceMethod:(SEL)sel { // [NSStringFromSelector(sel) isEqualToString:@"run"]; if (sel == NSSelectorFromString(@"run:")) { // 動態添加run方法 // class: 給哪一個類添加方法 // SEL: 添加哪一個方法,即添加方法的方法編號 // IMP: 方法實現 => 函數 => 函數入口 => 函數名(添加方法的函數實現(函數地址)) // type: 方法類型,(返回值+參數類型) v:void @:對象->self :表示SEL->_cmd class_addMethod(self, sel, (IMP)aaa, "v@:@"); return YES; } return [super resolveInstanceMethod:sel]; } @end 複製代碼
在實現encodeObject
和decodeObjectForKey
方法中,咱們通常須要把每一個屬性都要寫一遍,這樣很麻煩,咱們能夠經過Runtime來自動化。orm
- (void)encodeWithCoder:(NSCoder *)aCoder { unsigned int count = 0; Ivar *ivarList = class_copyIvarList([self class], &count); for(int i = 0; i < count; i++) { Ivar ivar = ivarList[i]; NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)]; id value = [self valueForKey:ivarName]; [aCoder encodeObject:value forKey:ivarName]; } free(ivarList); } - (id)initWithCoder:(NSCoder *)aDecoder { if(self == [super init]) { unsigned int count = 0; Ivar *ivarList = class_copyIvarList([self class], &count); for(int i = 0; i < count; i++) { Ivar ivar = ivarList[i]; NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)]; id value = [aDecoder decodeObjectForKey:ivarName]; [self setValue:value forKey:ivarName]; } free(ivarList); } return self; } 複製代碼
還有更簡便的方法,抽象成宏,參考網上資料。對象
unsigned int count = 0;
//獲取屬性列表
Ivar *propertyList = class_copyPropertyList([self class], &count);
//獲取方法列表
Method *methodList = class_copyMethodList([self class], &count);
//獲取成員變量列表
Ivar *ivarList = class_copyIvarList([self class], &count);
//獲取協議列表
__unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);
//獲取類方法
Method method1 = class_getClassMethod([self class], @selector(run));
//獲取實例方法
Method method2 = class_getInstanceMethod([self class], @selector(tempRun));
//添加方法
class_addMethod([self class], @selector(run), method_getImplementation(method2), method_getTypeEncoding(method2));
//替換方法
class_replaceMethod;
//交換方法
method_exchangeImplementations;
複製代碼
當類被引進項目的時候會執行load函數(在main函數開始以前),與這個類是會被用到無關,每一個類的load函數只會被調用一次。因爲load函數是自動加載的,不須要調用父類的load函數。
這個方法會在類接收到第一次消息時調用。因爲是系統調用,也不須要調用父類方法。