Swizzling 的應(yīng)用場景:
1)比如需要統(tǒng)計在每個界面停留時間屎媳。我們需要在
- (void)viewWillAppear:(BOOL)animated;
- (void)viewDidAppear:(BOOL)animated;
兩個方法中寫上統(tǒng)計用的方法环葵,但是按照常規(guī)的方法颗品,需要在每個界面加上統(tǒng)計代碼火本,這樣不僅繁瑣而且很有可能落掉一些頁面(這個時候你可能想使用基類沦童,但是很多項目并沒有繼承統(tǒng)一的一個或者幾個基類)允懂,所以這個時候就需要使用黑魔法 Swizzling空骚。
2)還可以用來打印每個頁面的類名巷送,這樣當你接手一個新項目的時候驶忌,可以使用Swizzling來打印出每個控制器的名稱,這樣就很容易的弄清項目的架構(gòu)笑跛。
先看一段代碼:
#import <objc/runtime.h>
@implementation UIViewController (Tracking)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(xxx_viewWillAppear:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
// When swizzling a class method, use the following:
// Class class = object_getClass((id)self);
// ...
// Method originalMethod = class_getClassMethod(class, originalSelector);
// Method swizzledMethod = class_getClassMethod(class, swizzledSelector);
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
#pragma mark - Method Swizzling
- (void)xxx_viewWillAppear:(BOOL)animated {
[self xxx_viewWillAppear:animated];
NSLog(@"viewWillAppear: %@", self);
}
@end
先來看代碼解釋:
1)+(void)load 和 +(void)initialize的區(qū)別
//Invoked whenever a class or category is added to the Objective-C runtime; implement this method to perform class-specific behavior upon loading.
//A class’s +load method is called after all of its superclasses’ +load methods.
//A category +load method is called after the class’s own +load method.
+(void)load
在Objective-C運行時載入類或者Category時被調(diào)用,這個方法對動態(tài)庫和靜態(tài)庫中的類或(Category)都有效.
//initialize is invoked only once per class. If you want to perform independent initialization for the class and for categories of the class, you should implement load methods.
+(void)initialize
+(void)initialize 會在運行時僅被觸發(fā)一次付魔,如果沒有向類發(fā)送消息的話,這個方法將不會被調(diào)用飞蹂。這個方法的調(diào)用是線程安全的抒抬。父類會比子類先收到此消息。
要點:
1晤柄、initialize和load擦剑,我們并不需要在這兩個方法的實現(xiàn)中使用super調(diào)用父類的方法妖胀。
2、load和initialize被調(diào)用一次是相對runtime而言 惠勒,你可以當作普通類方法多次調(diào)用赚抡。
3、類加載到系統(tǒng)的時候就用調(diào)用load方法纠屋,類首次使用的時候調(diào)用initialize方法涂臣。
4、load不像普通方法一樣遵從那套繼承規(guī)則售担,當每個類沒有實現(xiàn) load方法赁遗,不管各級超類是否實現(xiàn),系統(tǒng)都不會調(diào)用此類的load方法族铆。initialize與其他方法一樣岩四,如果每個類沒有實現(xiàn)initialize方法,而超類實現(xiàn)了哥攘,那么就會執(zhí)行超類的這個方法剖煌,所以通常會:
5、initialize和load的方法必須寫的精簡逝淹。
6耕姊、initialize中可以實現(xiàn)無法在編譯期初始化的全局變量,load的方法中可以實現(xiàn)swizzling的邏輯栅葡。
7茉兰、load的調(diào)用并不視為類的第一個方法完成,因為load中調(diào)用了當前類中的方法欣簇,就先去執(zhí)行initialize方法了规脸。
8、Runtime調(diào)用+(void)load時沒有autorelease pool
9醉蚁、load方法調(diào)用的順序:父類(Superclass)的方法優(yōu)先于子類(Subclass)的方法燃辖,類中的方法優(yōu)先于類別(Category)中的方法。
10网棍、所有類別(Category)中的load方法都會執(zhí)行黔龟。
11、最后一個類別(Category)中的initialize方法會覆蓋之前類別和類中的initialize方法滥玷。
2)dispatch_once
由于swizzling會改變?nèi)譅顟B(tài)氏身,所以我們使用的時候需要加倍小心,所以在運行時中加上 dispatch_once惑畴,
它確保代碼即使在多線程環(huán)境下也只會被執(zhí)行一次蛋欣。
3) SEL, Method如贷,IMP
SEL:SEL是一個方法在運行時的名字陷虎,OC中調(diào)用一個方法到踏,就是向?qū)ο蟀l(fā)送一個消息,通過SEL找到這個方法尚猿。
Method:
IMP:IMP就是方法的實現(xiàn)
Method swizzling 就是通過交換方法的實現(xiàn)
//Returns the implementation of a method. A function pointer of type IMP.
method_getImplementation
//Replaces the implementation of a method for a given class.The previous implementation of the method identified by name for the class identified by cls.
class_replaceMethod
//Exchanges the implementations of two methods.
method_exchangeImplementations
4) 在交換的方法里面實現(xiàn)自己的目的
- (void)xxx_viewWillAppear:(BOOL)animated
{
[self xxx_viewWillAppear:animated];
NSLog(@"viewWillAppear: %@", NSStringFromClass([self class]));
}
[self xxx_viewWillAppear:animated]; 在方法里面調(diào)用不會引起循環(huán)調(diào)用窝稿,因為這個是運行時已經(jīng)把xxx_viewWillAppear和viewWillAppear調(diào)換,調(diào)用viewWillApper是遵循調(diào)用父類的規(guī)則凿掂,避免出現(xiàn)意想不到的問題伴榔。
-(BOOL) isKindOfClass: classObj 判斷是否是這個類,包括這個類的子類和父類的實例庄萎;
-(BOOL) isMemberOfClass: classObj 判斷是否是這個類的實例,不包括子類或者父類踪少;
參考原文地址:
http://nshipster.com/method-swizzling/
參考文獻:
http://justsee.iteye.com/blog/1630979