如今看起來Runtime篇整理的少了,有時間再完善下,將就着看吧數組
Objective-C將不少靜態語言在編譯和連接時期作的工做放在了Runtime運行時處理,能夠說Runtime就是Objective-C的幕後工做者。bash
任何方法調用的本質,就是發送了一個消息(用Runtime發送消息,OC底層實現經過Runtime實現)。併發
對象根據方法編號SEL去隱射表查找對應的方法實現。函數
有時候咱們須要對類的方法進行修改,可是又沒法拿到源碼,咱們即可以經過Runtime來交換方法實現。ui
+ (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函數。
這個方法會在類接收到第一次消息時調用。因爲是系統調用,也不須要調用父類方法。