一、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