Method Swizzling是一個改變selector的實際實現的一個方法。比如有A,B方法,通過Method Swizzling方法可以實現調用A方法時,實際上是在調用B方法,實現了偷梁換柱的黑魔法。
使用場景:完全改變一個方法的實現
比如viewDidAppear.方法有:
1.繼承后重寫(不要調用super方法)。
2.使用分類實現重名方法,那么在分類會覆蓋原類中得方法。
3.使用method swizzling。
示例
更改UIImage的imageNamed
方法,當傳入的image為空是打印出錯。
#import <objc/runtime.h>
@implementation UIImage (Swizzle)
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
Class class = [self class];
// SEL:獲取方法編號,根據SEL就能去對應的類找方法
SEL originalSelector = @selector(imageNamed:);
SEL swizzledSelector = @selector(custom_imageNamed:);
// 獲取系統的方法(注意,如果是實例方法,使用class_getInstanceMethod)
Method originalMethod = class_getClassMethod(class, originalSelector);
// 獲取自定義方法
Method swizzledMethod = class_getClassMethod(class, swizzledSelector);
// 交換方法實現
method_exchangeImplementations(originalMethod, swizzledMethod);
});
}
+ (UIImage *)custom_imageNamed:(NSString*)imageName{
/**
* 調用[UIImage imageNamed:imageName]
說明:因為custom_imageNamed和imageNamed互相交換.所以下面方法調用的實際上是imageNamed:方法,不會造成死循環
*/
UIImage *image = [self custom_imageNamed:imageName];
if (!image) {
NSLog(@"圖片是空得!");
}
return image;
}
@end
注意點
1.Swizzling要再+ (void)load或者+(void) initialize里實現。
說明:由于method swizzling 會影響到類的全局狀態,所以要避免在并發處理中出現競爭的情況。+load方法是在程序啟動時調用,可取。而+initialize是在第一次初始化使用類時候調用,雖然也是個選擇,但是如果一直沒有使用這個,那么也就不會使用method swizzling了。
2.Swizzling在dispatch_once中執行
說明:原子性可以保證代碼只執行一次,而swizzling可以滿足需要。
3.私有方法也能替換