一.交換兩個(gè)方法的實(shí)現(xiàn):
Runtime還可以交換兩個(gè)方法的實(shí)現(xiàn)妒御。
例如:Person有兩個(gè)方法:study、run镇饺。我們交換兩個(gè)方法的實(shí)現(xiàn)乎莉,調(diào)用study的時(shí)候執(zhí)行的是run、調(diào)用run的時(shí)候執(zhí)行的是study奸笤。
Person類:
.h:
@interface Person : NSObject
-(void)study;
-(void)run;
@end
.m類:
@implementation Person
-(void)study{
NSLog(@"study");
}
-(void)run{
NSLog(@"run");
}
@end
controller里面惋啃,對(duì)person的兩個(gè)方法的實(shí)現(xiàn)進(jìn)行交換:
#pragma mark 交換兩個(gè)方法的實(shí)現(xiàn)
-(void)exchangeImp{
Person * person = [[Person alloc] init];
//獲取兩個(gè)實(shí)例方法
Method studyMethod = class_getInstanceMethod([Person class], @selector(study));
Method runMethod = class_getInstanceMethod([Person class], @selector(run));
//交換兩個(gè)方法的實(shí)現(xiàn)
method_exchangeImplementations(studyMethod, runMethod);
//調(diào)用兩個(gè)方法
[person study];//打印run
[person run];//打印study ,交換成功
}
注意:
獲取方法:類方法用class_getClassMethod 监右,對(duì)象方法用class_getInstanceMethod边灭;
使用method_exchangeImplementations即可交換兩個(gè)方法的實(shí)現(xiàn)
二.Method Swizzle
Method Swizzle 被稱作黑魔法,就是攔截系統(tǒng)方法健盒,也可以說(shuō)成對(duì)系統(tǒng)的方法進(jìn)行替換绒瘦。
為什么叫黑魔法,因?yàn)樵谝粋€(gè)分類里面對(duì)一個(gè)系統(tǒng)方法的實(shí)現(xiàn)進(jìn)行改變之后扣癣,不需要引入任何頭文件惰帽,使用了這個(gè)系統(tǒng)方法的地方,所有實(shí)現(xiàn)都會(huì)隨之改變父虑。
例如该酗,我們可以把所有使用imageNamed:name的方法,加載的image都變?yōu)閟mile.png這張圖片士嚎。
首先呜魄,controller里面添加一張圖片烁焙,圖片上面添加的圖片名為test.png
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//黑魔法(Method Swizzle)
//添加一個(gè)ImageView到界面上
[self addImageView];
}
-(void)addImageView{
UIImageView * imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 29, 29)];
imageView.center = self.view.center;
[self.view addSubview:imageView];
//imageView的圖片名為test.png
imageView.image = [UIImage imageNamed:@"test.png"];
}
@end
新建UIImage分類UIImage+Handle,在load方法里面把imageNamed的實(shí)現(xiàn)替換為我們自己寫的YYImageNamed的實(shí)現(xiàn)耕赘。
YYImageNamed里面把圖片名改為smile.png.
.h:
#import <UIKit/UIKit.h>
@interface UIImage (Handle)
@end
.m:
#import "UIImage+Handle.h"
#import <objc/runtime.h>
@implementation UIImage (Handle)
+(void)load{
//獲取兩個(gè)方法
Method imageNamedMethod = class_getClassMethod([self class], @selector(imageNamed:));
Method newImageNamedMethod = class_getClassMethod([self class], @selector(YYImageNamed:));
//交換兩個(gè)方法的實(shí)現(xiàn)
method_exchangeImplementations(imageNamedMethod, newImageNamedMethod);
}
+(UIImage *)YYImageNamed:(NSString *)name{
//把所有的圖片名字改成smile.png
name = @"smile.png";
return [self YYImageNamed:name];
}
@end
我們?cè)赾ontroller里面不需要做任何處理骄蝇,原本該顯示test.png的位置,就變成了smile.png來(lái)顯示操骡。
1.Method Swizzle怎么做到的呢九火?
在分類的load方法里面,用YYImageNamed替換系統(tǒng)的imageNamed方法册招。
2.為什么在load里面替換方法實(shí)現(xiàn)岔激?
在oc中,運(yùn)行時(shí)會(huì)自動(dòng)調(diào)用每個(gè)類的load方法和initialize方法是掰。并且實(shí)現(xiàn)了這兩個(gè)方法才會(huì)被調(diào)動(dòng)虑鼎。
load在類初始加載時(shí)被調(diào)用;
initialize在第一次調(diào)用類的類方法/實(shí)例方法之前被調(diào)用键痛;
2.1了解load:
1炫彩、運(yùn)行的時(shí)候會(huì)把所有需要的類加載到內(nèi)存里面
2、如果類里面實(shí)現(xiàn)了load方法絮短,在類加載到內(nèi)存的時(shí)候就會(huì)調(diào)用load方法江兢,且只調(diào)用一次
3、所有類的load方法執(zhí)行順序:
1)按照Targets->Build Phases->Compile Source里面的順序執(zhí)行丁频;
2)如果有父類杉允,會(huì)先調(diào)用父類的load方法再調(diào)用子類的load方法;
3)一個(gè)類別的load方法是在自己的load方法之后執(zhí)行
2.2了解initialize:
1席里、當(dāng)類第一次調(diào)用類的類方法/實(shí)例方法之前被調(diào)用叔磷,也只會(huì)調(diào)用一次。eg:在[NSArray alloc] 的時(shí)候就被調(diào)用奖磁。
2改基、initialize用于對(duì)某一個(gè)類的初始化
3、如果存在繼承關(guān)系署穗,會(huì)先調(diào)用父類的initialize方法寥裂,再調(diào)用子類的initialize方法
2.3答案:
從上面我們可以看出嵌洼,運(yùn)行時(shí)都會(huì)調(diào)用每個(gè)類的load案疲、initialize方法,我們可以從這兩個(gè)方法里面進(jìn)行方法交換麻养,保證后面每次調(diào)用系統(tǒng)的方法時(shí)褐啡,系統(tǒng)方法的實(shí)現(xiàn)已經(jīng)是我們改變過(guò)的了。
2.4擴(kuò)展:那為什么要在load方法里面執(zhí)行方法交換鳖昌,不在initialize方法里面執(zhí)行呢?
因?yàn)镾wizzling影響到類的全局狀態(tài)备畦,我們要保證資源不能互相競(jìng)爭(zhēng)低飒。
使用load方法,就可以保證在調(diào)用類的任何一個(gè)方法之前懂盐,就把想要做的事情做了(方法交換)
假如我們?cè)趇nitialize里面進(jìn)行方法交換:
我們交換的是alloc方法褥赊,那一個(gè)類第一次被初始化的時(shí)候,調(diào)用alloc進(jìn)行初始化莉恼。此時(shí)系統(tǒng)就會(huì)調(diào)用initialize拌喉,initialize里面才進(jìn)行方法交換。這樣調(diào)用的就不是我們交換過(guò)的方法了俐银。資源存在競(jìng)爭(zhēng)問(wèn)題尿背。就變成我們還沒(méi)有交換的時(shí)候,就已經(jīng)被調(diào)用了捶惜。
3.為什么YYImageNamed里面return [self YYImageNamed:name];
田藐,這不會(huì)造成循環(huán)引用嗎?
回顧YYImageNamed里面吱七,我們是這么寫的汽久。
+(UIImage *)YYImageNamed:(NSString *)name{
//把所有的圖片名字改成smile.png
name = @"smile.png";
return [self YYImageNamed:name];
}
交換原理:
交換之前:
交換之后:
交換之后,系統(tǒng)的方法名已經(jīng)變成我們的方法名踊餐,所以最后其實(shí)調(diào)用的是系統(tǒng)的方法回窘,并不會(huì)循環(huán)。這樣就實(shí)現(xiàn)了系統(tǒng)方法攔截市袖。