runtime在平時簡單的開發中很少用到,但這個涉及到底層,做iOS的不懂runtime就等于功夫只學了招式沒有修煉內功,最多只能是個高手,懂的api多點,但運行機制不通。api學不完,但底層運行機制是不變的。所有今天主要學習了下runtime。自己寫的代碼
我先看了一個視頻 iOS runtime 分享 總結下里面講了兩個知識點。
1 交換方法實現
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class cls = [UILabel class];
SEL original = @selector(willMoveToSuperview:);
SEL swizzled = @selector(myWillMoveToSuperview:);
Method originalMethod = class_getInstanceMethod(cls, original);
Method swizzledMethod = class_getInstanceMethod(cls, swizzled);
//添加自定義的方法到類里面
BOOL didAddMethod = class_addMethod(cls, original, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
/**
swizzled 是存在于 UILabel 的方法列表中
original 是存在于 UILabel 的父類方法列表中,不存在在UILabel中。
所以class_addMethod 是給UILabel添加一個選擇器,相當于重寫了父類的方法。
重寫的方法名,追加另外一個函數的實現。
*/
if (didAddMethod) {
//方法已經添加成功 替換掉原來的方法實現
class_replaceMethod(cls, swizzled, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}else{
//沒有添加成功 改變兩個函數的實現
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
- (void)myWillMoveToSuperview:(nullable UIView *)newSuperview{
[self myWillMoveToSuperview:newSuperview];
if (newSuperview) {
self.backgroundColor = [UIColor yellowColor];
[self setFont:[UIFont systemFontOfSize:13]];
}
}
method_exchangeImplementations 改變兩個方法的實現
很多人可能會認為上面的myWillMoveToSuperview:會循環執行,其實不會的。我來捋一捋。交換地址后 系統會調用willMoveToSuperview,可這時候willMoveToSuperview 指向了 myWillMoveToSuperview,于是就執行
{
[self myWillMoveToSuperview:newSuperview];
if (newSuperview) {
self.backgroundColor = [UIColor yellowColor];
[self setFont:[UIFont systemFontOfSize:13]];
}
}
這時候代碼調用 myWillMoveToSuperview 其實是調用的
[self willMoveToSuperview:newSuperview];
然后接下來執行判斷等代碼。
這里面有很多彎不好繞,要靜下心好好捋一下。
2 消息轉發
//c語言的函數 Obj-C的方法(method)就是一個至少需要兩個參數(self,_cmd)的C函數
//Since the function must take at least two arguments—self and _cmd, the second and third characters must be “@:” (the first character is the return type).
void minus(id self,SEL _cmd,NSNumber *val){
NSLog(@"%0.2f",[[self valueForKey:@"value"] floatValue] - [val floatValue]);
}
//第一步 沒有方法 添加方法實現
+ (BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(minus:)) {
//v@:f 返回值為void @: the second and third characters must be “@:”
class_addMethod([self class], sel, (IMP) minus, "v@:f");
return YES;
}
return [super resolveInstanceMethod:sel];
}
//第二步 指向新的方法接收者
- (id)forwardingTargetForSelector:(SEL)aSelector{
if (aSelector == @selector(uppercaseString)) {
return @"hello world";
}
return [super forwardingTargetForSelector:aSelector];
}
簡單介紹下消息轉發。輕松學習之一--Objective-C消息轉發
第一步,調用一個對象的方法,進入對象方法列表中尋找,找到執行。找不到進入父類尋找。直到找到,找不到進入下一步。
第二步,給開發者一個機會 動態的給對象添加一個方法去執行。若不處理進入下一步.
第三步,進入本步說明這個對象不處理此方法的調用,這時可以讓開發者指定一個能處理此方法的對象。形象的說明就是 這件事小明沒能力做,但任務已經派發了,他認為小剛能做,就把這個任務讓小剛去做。若不處理進入下一步。
第四步,進入這一步說明問題很大了,不過系統還是給開發者最后一個機會,重新指定一個人,指定一個任務,重新包裝一下去執行。就好像這件事沒人管沒人執行,但任務下來了,得找個應急的臨時工頂一下,隨便做點啥都行,讓領導知道有人干活就好。如果臨時工也沒有找,那肯定要出大事了。系統崩潰、閃退、報錯。