一、交換兩個方法的實現(xiàn)既忆,攔截系統(tǒng)自帶的方法調用功能
需要用到的方法 <objc/runtime.h>
獲得某個類的類方法
Method class_getClassMethod(Class cls , SEL name)
獲得某個類的實例對象方法
Method class_getInstanceMethod(Class cls , SEL name)
交換兩個方法的實現(xiàn)
void method_exchangeImplementations(Method m1 , Method m2)
案例1:方法簡單的交換
創(chuàng)建一個Person類柠座,類中實現(xiàn)以下兩個類方法邑雅,并在.h 文件中聲明
+ (void)run {
NSLog(@"跑");
}
+ (void)study {
NSLog(@"學習");
}
控制器中調用,則先打印跑妈经,后打印學習
[Person run];
[Person study];
下面通過runtime 實現(xiàn)方法交換淮野,類方法用class_getClassMethod ,對象方法用class_getInstanceMethod
// 獲取兩個類的類方法
Method m1 = class_getClassMethod([Person class], @selector(run));
Method m2 = class_getClassMethod([Person class], @selector(study));
// 開始交換方法實現(xiàn)
method_exchangeImplementations(m1, m2);
// 交換后吹泡,先打印學習骤星,再打印跑!
[Person run];
[Person study];
案例2:攔截系統(tǒng)方法
需求:比如iOS6 升級 iOS7 后需要版本適配爆哑,根據(jù)不同系統(tǒng)使用不同樣式圖片(擬物化和扁平化)洞难,如何通過不去手動一個個修改每個UIImage的imageNamed:方法就可以實現(xiàn)為該方法中加入版本判斷語句?
步驟:
1揭朝、為UIImage建一個分類(UIImage+Category)
2队贱、在分類中實現(xiàn)一個自定義方法流强,方法中寫要在系統(tǒng)方法中加入的語句挥转,比如版本判斷
+ (UIImage *)xh_imageNamed:(NSString *)name {
double version = [[UIDevice currentDevice].systemVersion doubleValue];
if (version >= 7.0) {
// 如果系統(tǒng)版本是7.0以上,使用另外一套文件名結尾是‘_os7’的扁平化圖片
name = [name stringByAppendingString:@"_os7"];
}
return [UIImage xh_imageNamed:name];
}
3、分類中重寫UIImage的load方法为严,實現(xiàn)方法的交換(只要能讓其執(zhí)行一次方法交換語句明郭,load再合適不過了)
+ (void)load {
// 獲取兩個類的類方法
Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:));
Method m2 = class_getClassMethod([UIImage class], @selector(xh_imageNamed:));
// 開始交換方法實現(xiàn)
method_exchangeImplementations(m1, m2);
}
注意:自定義方法中最后一定要再調用一下系統(tǒng)的方法谅畅,讓其有加載圖片的功能谱煤,但是由于方法交換,系統(tǒng)的方法名已經(jīng)變成了我們自定義的方法名(有點繞嘉抓,就是用我們的名字能調用系統(tǒng)的方法索守,用系統(tǒng)的名字能調用我們的方法),這就實現(xiàn)了系統(tǒng)方法的攔截抑片!
利用以上思路卵佛,我們還可以給 NSObject 添加分類,統(tǒng)計創(chuàng)建了多少個對象蓝丙,給控制器添加分類级遭,統(tǒng)計有創(chuàng)建了多少個控制器,特別是公司需求總變的時候渺尘,在一些原有控件或模塊上添加一個功能挫鸽,建議使用該方法!
二鸥跟、在分類中設置屬性丢郊,給任何一個對象設置屬性
眾所周知,分類中是無法設置屬性的医咨,如果在分類的聲明中寫@property 只能為其生成get 和 set 方法的聲明枫匾,但無法生成成員變量,就是雖然點語法能調用出來拟淮,但程序執(zhí)行后會crash干茉,有人會想到使用全局變量呢?比如這樣
int _age;
- (int )age {
return _age;
}
- (void)setAge:(int)age {
_age = age;
}
但是全局變量程序整個執(zhí)行過程中內存中只有一份很泊,我們創(chuàng)建多個對象修改其屬性值都會修改同一個變量角虫,這樣就無法保證像屬性一樣每個對象都擁有其自己的屬性值。這時我們就需要借助runtime為分類增加屬性的功能了
需要用到的方法 <objc/runtime.h>
set方法委造,將值value 跟對象object 關聯(lián)起來(將值value 存儲到對象object 中)
參數(shù) object:給哪個對象設置屬性
參數(shù) key:一個屬性對應一個Key戳鹅,將來可以通過key取出這個存儲的值,key 可以是任何類型:double昏兆、int 等枫虏,建議用char 可以節(jié)省字節(jié)
參數(shù) value:給屬性設置的值
參數(shù)policy:存儲策略 (assign 、copy 、 retain就是strong)
void objc_setAssociatedObject(id object , const void *key ,id value ,objc_AssociationPolicy policy)
利用參數(shù)key 將對象object中存儲的對應值取出來
id objc_getAssociatedObject(id object , const void *key)
步驟:
1隶债、創(chuàng)建一個分類腾它,比如給任何一個對象都添加一個name屬性,就是NSObject添加分類(NSObject+Category)
2燃异、先在.h 中@property 聲明出get 和 set 方法携狭,方便點語法調用
@property(nonatomic,copy)NSString *name;
3继蜡、在.m 中重寫set 和 get 方法回俐,內部利用runtime 給屬性賦值和取值
char nameKey;
- (void)setName:(NSString *)name {
// 將某個值跟某個對象關聯(lián)起來,將某個值存儲到某個對象中
objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
return objc_getAssociatedObject(self, &nameKey);
}