Runtime的應(yīng)用

一、Runtime交換方法 method_exchangeImplementations

第三方框架或者系統(tǒng)原生方法功能 不能滿足我們的時(shí)候,我們可以在保持系統(tǒng)原有方法功能的基礎(chǔ)上,添加額外的功能

1.1、無(wú)侵入式埋點(diǎn)(運(yùn)行時(shí)方法替換,hook系統(tǒng)方法)
#import "UIViewController+BigDataExtend.h"
#import <objc/runtime.h>

@implementation UIViewController (BigDataExtend)

+ (void)load {
    Method method1 = class_getInstanceMethod(self, @selector(viewWillDisappear:));
    Method method2 = class_getInstanceMethod(self, @selector(bigdata_viewWillDisappear:));
    method_exchangeImplementations(method1, method2);
}

- (void)bigdata_viewWillDisappear:(BOOL)animated {
    // 插入相關(guān)代碼,
    // 再執(zhí)行原方法
    [self bigdata_viewWillDisappear:animated];
}

@end
1.2、數(shù)組插入空對(duì)象崩潰,進(jìn)行錯(cuò)誤判斷(感興趣的同學(xué)可以去弄一下字典的判斷)
#import "NSMutableArray+Extend.h"
#import <objc/runtime.h>

@implementation NSMutableArray (Extend)

+ (void)load {
    // 類簇:NSStirng、NSArray、NSDictionary,真實(shí)類型是其他類型
    Class cls = NSClassFromString(@"__NSArrayM");
    Method method1 = class_getInstanceMethod(cls, @selector(insertObject:atIndex:));
    Method method2 = class_getInstanceMethod(cls, @selector(wj_insertObject:atIndex:));
    method_exchangeImplementations(method1, method2);
}

- (void)wj_insertObject:(id)anObject atIndex:(NSUInteger)index {
    if (anObject == nil) return;

    // 只有對(duì)象存在才會(huì)去執(zhí)行系統(tǒng)方法
    [self wj_insertObject:anObject atIndex:index];
}

@end
1.3、手機(jī)字體適配
#import "UIFont+Extend.h"
#import <objc/runtime.h>

@implementation UIFont (Extend)

+ (void)load {
    Method method1 = class_getInstanceMethod(self, @selector(systemFontOfSize:));
    Method method2 = class_getInstanceMethod(self, @selector(wj_systemFontOfSize:));
    method_exchangeImplementations(method1, method2);
}

+ (UIFont *)wj_systemFontOfSize:(CGFloat)fontSize {
    // 這里可根據(jù)手機(jī)屏幕尺寸制定系數(shù)
    const CGFloat modulus = 1.5;
    [self wj_systemFontOfSize:fontSize * modulus];
}

@end

1.4、攔截所有button點(diǎn)擊事件
#import "UIControl+Extend.h"
#import <objc/runtime.h>

@implementation UIControl (Extend)

+ (void)load {
    Method method1 = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
    Method method2 = class_getInstanceMethod(self, @selector(wj_sendAction:to:forEvent:));
    method_exchangeImplementations(method1, method2);
}

- (void)wj_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
//    if ([self isKindOfClass:[UIButton class]]) {
//        // 攔截了所有按鈕的事件
//    }

    // 重新實(shí)現(xiàn)系統(tǒng)自帶方法
    [self wj_sendAction:action to:target forEvent:event];
}

@end

二、給系統(tǒng)分類動(dòng)態(tài)添加屬性

給系統(tǒng)的類添加額外屬性的時(shí)候,可以使用runtime動(dòng)態(tài)添加屬性方法
給一個(gè)類聲明屬性,其實(shí)本質(zhì)就是給這個(gè)類添加關(guān)聯(lián),并不是直接把這個(gè)值的內(nèi)存添加到內(nèi)存空間

- (void)dynamicAddObject {
    // 動(dòng)態(tài)創(chuàng)建一個(gè)Person類
    NSString *ocObjectName = @"Person";
    const char *cObjectName = [ocObjectName cStringUsingEncoding:NSUTF8StringEncoding];

    Class newClass = objc_allocateClassPair([NSObject class], cObjectName, 0);

    // 動(dòng)態(tài)添加成員變量
    class_addIvar(newClass, "_age", 4, 1, @encode(int));
    class_addIvar(newClass, "_weight", 4, 1, @encode(int));

    class_addMethod(newClass, @selector(run), (IMP)run, "v@:");

    /*
     *  class_addIvar 只能在objc_registerClassPair之前調(diào)用
     *  不支持將實(shí)例變量添加到已有的類
     */
    objc_registerClassPair(newClass);

    id person = [[newClass alloc] init];
    [person setValue:@10 forKey:@"_age"];
    [person setValue:@20 forKey:@"_weight"];

    [person run];

    NSLog(@"%@ -- %@", [person valueForKey:@"_age"], [person valueForKey:@"_weight"]);
    
    // 在不需要這個(gè)類時(shí)釋放
    objc_disposeClassPair(newClass);
}

void run(id self, SEL _cmd){
    NSLog(@"動(dòng)態(tài)方法調(diào)用");
}

三、字典轉(zhuǎn)模型

字典轉(zhuǎn)模型 KVC 實(shí)現(xiàn)

KVC 字典轉(zhuǎn)模型弊端:必須保證,模型中的屬性和字典中的key 一一對(duì)應(yīng)。
如果不一致,就會(huì)調(diào)用[ setValue:forUndefinedKey:] 報(bào)key找不到的錯(cuò)。
分析:模型中的屬性和字典的key不一一對(duì)應(yīng),系統(tǒng)就會(huì)調(diào)用setValue:forUndefinedKey:報(bào)錯(cuò)。
解決:重寫對(duì)象的setValue:forUndefinedKey:,把系統(tǒng)的方法覆蓋,就能繼續(xù)使用KVC,字典轉(zhuǎn)模型了。

字典轉(zhuǎn)模型 Runtime 實(shí)現(xiàn)

思路:利用運(yùn)行時(shí),遍歷模型中所有屬性,根據(jù)模型的屬性名,去字典中查找key,取出對(duì)應(yīng)的值,給模型的屬性賦值(從提醒:字典中取值,不一定要全部取出來(lái));提供一個(gè)NSObject分類,專門字典轉(zhuǎn)模型,以后所有模型都可以通過(guò)這個(gè)分類實(shí)現(xiàn)字典轉(zhuǎn)模型。
考慮情況:
1.當(dāng)字典的key和模型的屬性匹配不上。
2.模型中嵌套模型(模型屬性是另外一個(gè)模型對(duì)象)。
3.數(shù)組中裝著模型(模型的屬性是一個(gè)數(shù)組,數(shù)組中是一個(gè)個(gè)模型對(duì)象)。

注解:
根據(jù)上面的三種特殊情況,先是字典的key和模型的屬性不對(duì)應(yīng)的情況。不對(duì)應(yīng)有兩種,一種是字典的鍵值大于模型屬性數(shù)量,這時(shí)候我們不需要任何處理,因?yàn)閞untime是先遍歷模型所有屬性,再去字典中根據(jù)屬性名找對(duì)應(yīng)值進(jìn)行賦值,多余的鍵值對(duì)也當(dāng)然不會(huì)去看了;另外一種是模型屬性數(shù)量大于字典的鍵值對(duì),這時(shí)候由于屬性沒(méi)有對(duì)應(yīng)值會(huì)被賦值為nil,就會(huì)導(dǎo)致crash,我們只需加一個(gè)判斷即可??紤]三種情況下面一一注解

MJExtension 字典轉(zhuǎn)模型實(shí)現(xiàn)

底層也是對(duì) runtime 的封裝,才可以把一個(gè)模型中所有屬性遍歷出來(lái)。(我之所以看不懂,是MJ封裝了很多層而已_.)。

四、利用關(guān)聯(lián)對(duì)象(AssociatedObject)給分類添加屬性

為一個(gè)已有的類(比如說(shuō)不想影響其文件結(jié)構(gòu))、第三方庫(kù)提供的類增加幾個(gè)property,已經(jīng)是十分常見且需要的操作了,有人會(huì)單獨(dú)起草一份category.m文件,也有人直接繼承。
category的好處就是:
1、能減少類文件的數(shù)量提高編譯速度;
2、也是為了代碼結(jié)構(gòu)更加清晰。作者:強(qiáng)子ly

例:SDWebImage 使用url作為key進(jìn)行關(guān)聯(lián)

static char imageURLKey;
objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

摘自不知名劉先生 https://mp.weixin.qq.com/s?__biz=MzUxMjAzMTI4Mw==&mid=2247483832&idx=1&sn=e9eb1ca026bc322e8e772467fdcb2749&chksm=f96beee4ce1c67f294ceb32b2de46df705977c52cc14a95d1a88da8f611c5c8ba96a568a378a&token=911032679&lang=zh_CN#rd、
強(qiáng)子ly http://www.itdecent.cn/p/0326e2f13ce6

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

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

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