runtime簡稱運行時原朝,也就是在運行時候的一些機制梳虽。對于oc函數(shù),屬于動態(tài)調(diào)用過程哥捕,在編譯的時候并不能決定真正調(diào)用哪些函數(shù),只有在真正運行的時候才會根據(jù)函數(shù)的名稱找到對應的函數(shù)來調(diào)用嘉熊。
交換兩個方法的實現(xiàn)遥赚,攔截系統(tǒng)自帶的方法調(diào)用功能
導入<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(@"學習");
}
在viewcontroller中引用凫佛,導入<objc/runtime.h>讲坎,通過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];
上面可以看出毫炉,交換了方法的實現(xiàn)瓮栗。
2.攔截系統(tǒng)方法
一般是寫個類的分類,然后在分類中重寫該類的一個方法瞄勾,最后在load方法中交換一下自定義的方法和系統(tǒng)的方法费奸。當然了,自定義方法中最后一定要再調(diào)用一下系統(tǒng)的方法进陡。
1.為UIImage建一個分類(UIImage+Category)
2愿阐、在分類中實現(xiàn)一個自定義方法,方法中寫要在系統(tǒng)方法中加入的語句四濒,比如版本判斷
+ (UIImage *)junjie_imageNamed:(NSString *)name {
double version = [[UIDevice currentDevice].systemVersion doubleValue];
if (version >= 7.0) {
// 如果系統(tǒng)版本是7.0以上换况,使用另外一套文件名結(jié)尾是‘_os7’的扁平化圖片
name = [name stringByAppendingString:@"_os7"];
}
return [UIImage junjie_imageNamed:name];}
3、分類中重寫UIImage的load方法盗蟆,實現(xiàn)方法的交換
+(void)load {
// 獲取兩個類的類方法
Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:));
Method m2 = class_getClassMethod([UIImage class], @selector(junjie_imageNamed:));
// 開始交換方法實現(xiàn)
method_exchangeImplementations(m1, m2);
}
分類中設置屬性,給任何一個對象設置屬性
在分類中舒裤,是沒有屬性的喳资,也就是說,你在分類的.h文件中@property只能創(chuàng)建get和set方法的聲明腾供,但無法生成屬性變量仆邓,雖然在分類的.m中我們可以設置個全局變量,代碼如下:
NSString *_ssex;
- (NSString *)Mysex {
return _ssex;
}
- (void)setMysex:(NSString *)Mysex {
_ssex = Mysex;
}
在viewdidload中
UIImage *image = [UIImage new];
image.Mysex = @"男生";
_MyImage = [UIImage new];
_MyImage.Mysex = @"女生";
NSLog(@"image == %@, _MyImage == %@", image.Mysex, _MyImage.Mysex);
會發(fā)現(xiàn)打印結(jié)果都是女生伴鳖,這是因為全局變量程序整個執(zhí)行過程中內(nèi)存中只有一份节值,我們創(chuàng)建多個對象修改其屬性值都會修改同一個變量,這樣就無法保證像屬性一樣每個對象都擁有其自己的屬性值榜聂。所以這時候就需要借用runtime為分類添加屬性的功能了搞疗。
還是在剛才的分類中,代碼如下:
NSString *_ssex;
- (NSString *)Mysex {
// return _ssex;
return objc_getAssociatedObject(self, &_ssex);
}
- (void)setMysex:(NSString *)Mysex {
// _ssex = Mysex;
//將某個值跟某個對象關(guān)聯(lián)起來须肆,將某個值存儲到某個對象中
objc_setAssociatedObject(self, &_ssex, Mysex, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
運行viewdidload匿乃,發(fā)現(xiàn)第一個輸出男生,第二個輸出女生了豌汇。
方法解惑:
void objc_setAssociatedObject(id object , const void *key ,id value ,objc_AssociationPolicy policy)
set方法幢炸,將值value 跟對象object 關(guān)聯(lián)起來(將值value 存儲到對象object 中)
參數(shù) object:給哪個對象設置屬性
參數(shù) key:一個屬性對應一個Key,將來可以通過key取出這個存儲的值拒贱,key 可以是任何類型:double宛徊、int 等佛嬉,以后建議用char 可以節(jié)省字節(jié),我這里不改了
參數(shù) value:給屬性設置的值
參數(shù)policy:存儲策略 (assign 闸天、copy 暖呕、 retain就是strong)
id objc_getAssociatedObject(id object , const void *key)
利用參數(shù)key將對象object中存儲的對應值取出來。
獲得一個類的所有成員變量号枕,并對其操作缰揪。
Ivar *ivars = class_copyIvarList(Class cls , unsigned int *outCount)
ivars:返回值,存放所有獲取到的屬性葱淳。
cls:哪個類钝腺。
outCount:放一個接收值的地址,用來存放屬性的個數(shù)赞厕。
unsigned int outCount = 0;
Ivar *ivars = class_copyIvarList([Person class], &outCount);
NSLog(@"count == %d", outCount);
for (int i = 0; i < outCount; i++) {
Ivar ivar = ivars[i]; //取出i位置對應的成員變量
const char *name = ivar_getName(ivar);
const char *type = ivar_getTypeEncoding(ivar);
NSLog(@"成員變量名:%s, 成員變量類型:%s", name, type);
}
free(ivars); //釋放內(nèi)存
通過上面的代碼我們可以獲取到自定義類中所有成員變量的名字和類型艳狐。
但是有啥用啊。皿桑。毫目。網(wǎng)上有的作者就這么一提,獲取到又不能操作有什么用诲侮。后來發(fā)現(xiàn)了這個方法object_setIvar镀虐,他可以設置值,接下來我來教大家怎么用沟绪。
比如說我們之前在Person類中定義了私有成員變量_name刮便,NSString類型的。
在Person類中定義兩個方法绽慈,如下:
- (void)getName:(NSString *)junjie {
_name = junjie;
NSLog(@"他的名字叫:%@",_name);
}
- (void)printfName {
NSLog(@"我的name is %@", _name);
}
這兩個方法用到了私有變量_name
在viewdidload中恨旱,作如下操作
unsigned int outCount;
Ivar *IvarArray = class_copyIvarList([Person class], &outCount);
[person getName:@"feifei"];
[person printfName];
//操作后
object_setIvar(person, IvarArray[1], @"123");// 我在這里是第二個元素
[person printfName];
打印結(jié)果如下:
可見我們在外面利用runtime,直接將私有變量的值給修改了坝疼,是不是感覺很吊搜贤。。钝凶。
當然了仪芒,有set就有g(shù)et,object_getIvar方法腿椎,他可以獲取到上面set設置的值(一般出現(xiàn)在一個類中)桌硫,有興趣的童鞋可以去研究研究。