RunTime的SwizzLing為什么要這么寫你知道么?實(shí)例中結(jié)合的關(guān)聯(lián)對象功能

首先來看下什么是方法的交換,也是很多人口中的runtime黑魔法。方法交換,就是把Method的IMP指針給交換了。關(guān)于這兩Method和IMP到底是啥?請先看這里
本文的demo可以下載調(diào)試,不過代碼量很少,建議自己動手?jǐn)]一遍。參考鏈接

先直接上代碼吧:
- (void)textSwizzLing {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method originMethod = class_getInstanceMethod([self class],
@selector(orignMethod));
Method swizzMthod = class_getInstanceMethod([self class], @selector(swizzLingMethod));
BOOL didAdd = class_addMethod([self class], @selector(orignMethod),
method_getImplementation(swizzMthod), method_getTypeEncoding(swizzMthod));
//,把swiz的實(shí)現(xiàn)方法,添加給originmethod,如果沒有實(shí)現(xiàn)origin方法,則添加成功,否則添加失敗。
如果直接exchange的話,那么就會覆蓋掉父類的方法。,
if (didAdd) {
class_replaceMethod([self class],@selector(swizzLingMethod),
method_getImplementation(originMethod),method_getTypeEncoding(originMethod));
}else {
method_exchangeImplementations(originMethod, swizzMthod);
//如果origin的方法,本類中已經(jīng)實(shí)現(xiàn)了,直接交換就好了。
}
});
[self performSelector:@selector(orignMethod) withObject:nil];
}

整個邏輯是這樣的:把swizz的實(shí)現(xiàn),添加給origin,如果origin的方法,已經(jīng)在本類中實(shí)現(xiàn)了,那么直接exchange就可以了。 這是一種情況。因?yàn)閍ddMethod的官方文檔說,如果origin在本類有實(shí)現(xiàn),那么會添加失敗。

看了這一段代碼,其實(shí)很多教程教你這么寫,可是你知道為什么這么寫么?
如果didAdd失敗會怎樣?
下面分析一下第二種情況,請看代碼(or 自己寫一下):

 Class cls = [UILabel class];
    SEL originalSelector = @selector(willMoveToSuperview:);
    SEL swizzledSelector = @selector(myWillMoveToSuperview:);
    Method originalM = class_getInstanceMethod(cls, originalSelector);
    Method swizzedM = class_getInstanceMethod(cls, swizzledSelector);
                    NSLog(@"%p",method_getImplementation(swizzedM));
    NSLog(@"%p",method_getImplementation(originalM));
    BOOL add = class_addMethod(cls, originalSelector, method_getImplementation(swizzedM), method_getTypeEncoding(swizzedM));
    NSLog(@"%p",method_getImplementation(swizzedM));
    NSLog(@"%p",method_getImplementation(originalM));

    if (add) {
        class_replaceMethod(cls, swizzledSelector, method_getImplementation(originalM), method_getTypeEncoding(originalM));
            NSLog(@"%p",method_getImplementation(swizzedM));
        NSLog(@"%p",method_getImplementation(originalM));
    }else {
        method_exchangeImplementations(originalM, swizzedM);
    }

這個willMoveToSuperview:是父類中實(shí)現(xiàn)的。UILabel中并沒有實(shí)現(xiàn)這個函數(shù),所以,Add為YES,那么,通過Add把swizz的實(shí)現(xiàn)傳給了willMoveToSuperview。那么, class_replaceMethod(cls,swizzledSelector, method_getImplementation(originalM), method_getTypeEncoding(originalM));就是把origin的實(shí)現(xiàn)傳給swizzled這個函數(shù)名了?

兩個函數(shù)指向同一個地址

打印一下我發(fā)現(xiàn),可是這兩個函數(shù)都指向的同一個實(shí)現(xiàn)方法啊。意味著,origin確實(shí)指向了swizz的實(shí)現(xiàn),可是swizz的實(shí)現(xiàn)還是swizz的實(shí)現(xiàn)?郁悶?zāi)?,怎么會這樣?不應(yīng)該是交換么,說好的SwizzLing呢,那豈不是調(diào)用兩個都會執(zhí)行同一個函數(shù)。既然網(wǎng)上的資料說不清,那我還是看官方文檔吧

官方文檔

終于找到了罪魁禍?zhǔn)?,注意看最后一句話,大概意思就是去這個類的父類中去找這個方法。

現(xiàn)在明白了,class_getInstanceMethod原來拿到的是父類中的方法,后面這句
class_replaceMethod(cls,swizzledSelector, method_getImplementation(originalM), method_getTypeEncoding(originalM));
是把父類的實(shí)現(xiàn),覆蓋了swizz的實(shí)現(xiàn),所以兩個打印出來的地址會是一樣.所以在打印 NSLog(@"%p",method_getImplementation(originalM));
的時候,還是打印的父類中的實(shí)現(xiàn),并不是現(xiàn)在新加的實(shí)現(xiàn)。

那么現(xiàn)在稍微多了解一點(diǎn),class_getInstanceMethod會先在子類找還是先在父類找?(是想知道,如果我在子類中重寫了父類中的方法,那么這個函數(shù)是拿到的子類中的還是父類中的)

 SEL originalSelector = @selector(willMoveToSuperview:);
    Method originalM = class_getInstanceMethod(cls, originalSelector);
    Method supperM = class_getInstanceMethod([UIView class], originalSelector);
    NSLog(@"%p",method_getImplementation(originalM));
    NSLog(@"%p",method_getImplementation(supperM));

并且實(shí)現(xiàn)這個方法:

- (void)willMoveToSuperview:(UIView *)view {
NSLog(@"1");

}
然后打印發(fā)現(xiàn),兩個的地址不一樣,所以這個方法class_getInstanceMethod,是先在本類中找實(shí)現(xiàn),找不到再去父類找。證明,在子類中找到了,就不會去父類中找了。


現(xiàn)在理解了為什么要這樣寫,那結(jié)合關(guān)聯(lián)對象試著給label寫個屬性。

先介紹三個方法
 1,objc_setAssociatedObject(self, @selector(isSystemColor), @(isSystemColor),
 OBJC_ASSOCIATION_RETAIN_NONATOMIC);

第一個參數(shù)是要關(guān)聯(lián)的對象。第二個是一個能夠唯一辨識的key(建議用@selector(get方法)),第三個參數(shù)是要關(guān)聯(lián)的值,第四個是關(guān)聯(lián)的方案。這個根據(jù)枚舉的定義,也表達(dá)的很清楚了,基本上和@property的屬性類型對應(yīng)。

 2,  objc_getAssociatedObject(self, _cmd);

第一個參數(shù)要拿哪個對象的關(guān)聯(lián)對象
第二個參數(shù),是這個關(guān)聯(lián)參數(shù)的key值,看了這里的都知道,每個都有兩個隱藏參數(shù),一個是self,一個是_cmd,也就是@selector()

3,objc_removeAssociatedObjects,基本不用,會移除self的所有關(guān)。如果要移除,直接傳nil就可以移除了。

這里,我理解關(guān)聯(lián)對象的意思,就是把這個數(shù)值以一種字典的形式存起來了,需要的時候再用key值取出來。

而存儲方案,就是關(guān)聯(lián)方案,不過我也沒有測到到底這幾種在ARC下的不同,以及對象的移除時機(jī),所以這里先不做介紹。

demo中用的是給label改變背景顏色,一般項(xiàng)目中會用到改變字體,不過背景顏色來得明顯一些。這里結(jié)合關(guān)聯(lián)對象,是因?yàn)閷?shí)際項(xiàng)目中,有的label不需要改變字體,所以我們需要傳入一個值判斷是否設(shè)為統(tǒng)一的字符,并且又改最少的代碼,這種少數(shù)的label,就給這個對象設(shè)為NO就可以了。
具體代碼可以參看demo
最后不要忘記移除關(guān)聯(lián)對象

objc_setAssociatedObject(self, @selector(isSystemColor), nil, 
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容