Method Swizzling是runtime應用的體現,那么關于Method Swizzling的應用和注意事項在下面做簡單的總結:
Method Swizzling原理:
每個類都維護一個方法(Method)列表,Method則包含SEL和其對應IMP的信息,方法交換做的事情就是把SEL和IMP的對應關系斷開,并和新的IMP生成對應關系。
交換前:Asel->AImp Bsel->BImp
交換后:Asel->BImp Bsel->AImp
Method Swizzling用途:
1、面向切面編程: 數據統計;比如為了統計viewwillappear調用的次數,我們可以在基類(其他VC繼承的類)的VC里面,添加如下代碼就可以統計viewwillappear被調用的次數:
+(void)load{//load方法在main()函數執行前就被執行//確保里面的方法被執行一次
??? static dispatch_once_t onceToken;????
dispatch_once(&onceToken, ^{???????
?[self swizzingClass:[self class] originSel:@selector(viewWillAppear:) newSel:@selector(custom_viewWillAppear:)];???
?});
}
+(void)swizzingClass:(Class)class? originSel:(SEL)originSel? newSel:(SEL)newSel{??? Method originM = class_getInstanceMethod(class, originSel);????
Method newM = class_getInstanceMethod(class, newSel);???
?IMP newImp = method_getImplementation(newM);???
?BOOL addMethodSuccess = class_addMethod(class, newSel, newImp, method_getTypeEncoding(newM));????
if (addMethodSuccess) {???????
?class_replaceMethod(class, originSel, newImp, method_getTypeEncoding(newM));??? }else{????????
method_exchangeImplementations(originM, newM);???
?}??
?}
-(void)custom_viewWillAppear:(BOOL)animate{??
? [super viewWillAppear:animate];????
NSLog(@"%@========%s",[self class],__func__);
}
2、數組越界問題 。
法一 ? 通過分類強化 :
@implementation?UIView?(safe)
-?(BOOL)containsObjectAtIndex:(NSInteger)index?{
return?index?>=?0?&&?index?
}
-?(id)objectNilAtIndex:(NSInteger)index{
return?[self?containsObjectAtIndex:index]???[self?objectAtIndex:index]?:?nil;
}
@end
法二 ?使用Method sizzling
@implementation NSArray (StrengThen)
+?(void)load{
static?dispatch_once_t?onceToken;
dispatch_once(&onceToken,?^{
@autoreleasepool?{
[objc_getClass("__NSArray0")?swizzleMethod:@selector(objectAtIndex:)?swizzledSelector:@selector(emptyObjectIndex:)];
[objc_getClass("__NSArrayI")?swizzleMethod:@selector(objectAtIndex:)?swizzledSelector:@selector(arrObjectIndex:)];
[objc_getClass("__NSArrayM")?swizzleMethod:@selector(objectAtIndex:)?swizzledSelector:@selector(mutableObjectIndex:)];
[objc_getClass("__NSArrayM")?swizzleMethod:@selector(insertObject:atIndex:)?swizzledSelector:@selector(mutableInsertObject:atIndex:)];
}
});
}
-?(id)emptyObjectIndex:(NSInteger)index{
return?nil;}
-?(id)arrObjectIndex:(NSInteger)index{
if?(index?>=?self.count?||?index?<?0)?{
return?nil;
return?[self?arrObjectIndex:index];
}
-?(id)mutableObjectIndex:(NSInteger)index{
if?(index?>=?self.count?||?index?<?0)?{
return?nil;
}
return?[self?mutableObjectIndex:index];
}
-?(void)mutableInsertObject:(id)object?atIndex:(NSUInteger)index{
if?(object)?{
[self?mutableInsertObject:object?atIndex:index];
}
?}
?3、給全局圖片名稱添加后綴,比如你的工程所有的圖片都更新了,以前都叫xxx.png現在叫xxx_new.png那么如果我們在工程中一張一張改名字比較麻煩,所以這個時候可以用“黑魔法”來達到相應的效果。(注意這個方法使用過后三方SDK里面引用的圖片可能也會被改變,所以要謹慎使用,綜合考慮下SDK和自己的圖片數量占比,如果真的想使用就可以在三方SDK中的Bundle圖片資源中,修改三方圖片的名字)。
Method Swizzling注意事項
1、對自己使用Method Swizzling的地方要及時告訴同伴,否則就會在他人調用到此塊方法的時候就會不知所以然。
2、盡量少用Method Swizzling。雖然Method Swizzling可以讓我們高效地解決某些問題,但是如果應用不得當,可能會引發一系列問題。
3、swizzling 需要在 + (void)load{}中使用:
? ? ?在+(void)load{}方法中實現,這樣可以保證方法一定會調用且不會出現異常;使用dispatch_once來執行方法交換,這樣可以保證只運行一次。load 和initialize區別:load是只要類所在文件被引用就會被執行,而initialize是在類或者其子類的第一個方法被調用前調用。所以只有當此類沒有被引用進項目時,才不會調用+(void)load{}方法;如果類文件被引用進來,但是沒有使用,那么initialize也不會被調用;而此時+(void)load{}方法會被調用(在main()函數之前)。