情景: 使用MethodSwizzle 實現對數組、字典 等系統方法的安全校驗。顯然能達到預期效果,但實際發現當ios
鍵盤顯示的狀況下 home app 進入後臺,再單擊app 圖標 切換回前臺時 發生crash :數組
[UIKeyboardLayoutStar release]: message sent to deallocated instance安全
UIKeyboardLayoutStar 是鍵盤上的佈局的視圖吧,app
整個工程都在ARC下 構建,很奇怪,並且必須。佈局
信息:ui
其中都有提到DurexKit 原理都是同樣的,上面提到緣由是替換了 NSArray的objectAtIndex: 方法,code
不過在個人項目緣由是替換了NSMutableArray 的objectAtIndex:( NSMutableArray和 NSArray 的objectAtIndex:都有替換,單獨替換 NSArray 的objectAtIndex:方法則不會引發crash) blog
解決方案:給 添加非ARC 支持,並改寫實現
有提到:。。貌似 arc 有時也不必定可靠。
----------------------2015.3.23-----更新--0.0--繼續填坑啊----------------------------------
話說使用 語法糖初始化 數組和 字典 真的好方便。。。@{....} @[...] 可是很容易埋雷。。大多數裏面存的都是變量 在代碼裏。so , 也須要校驗 。
最初使用 MethodSwizzle 很嗨皮啊,首先涉及可變參數在替換的方法裏 試圖使用NSInvocation 來解決 傳遞多個參數的問題,最後 莫名crash 。。你能夠試試。
再說說數組和字典裏的類簇(工廠模式):
http://blog.sunnyxx.com/2014/12/18/class-cluster/
後來發現若是你用語法糖 初始化數組 crash 信息以下:
'*** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from xx
解決方案使用靜態方法 + objc_msgSend 來實現:
#import <objc/message.h> #define CurrentClass objc_getClass("__NSArrayI") static id array_safe_initWithObjects(id self, SEL _cmd, const id* objects,unsigned int count){ id orignialResult = nil; @try { orignialResult = objc_msgSend(self, @selector(array_safe_initWithObjects:count:),objects,count); } @catch (NSException *exception) { DDLogDebug(@"=__NSPlaceholderArray===BUSTED!"); } return orignialResult ; } @implementation NSArray (SafeCheck) +(void)load{ //添加 [CurrentClass swizzleInstanceSelector:@selector(objectAtIndex:) withNewSelector:@selector(safeObjectsAtIndex:)]; [objc_getClass("__NSPlaceholderArray") swizzleSelector: @selector(initWithObjects:count:) withNewSelector:@selector(array_safe_initWithObjects:count:) andNewIMP:(IMP)&array_safe_initWithObjects]; }
+ (void) swizzleSelector:(SEL)originalSelector withNewSelector:(SEL)newSelector andNewIMP:(IMP)imp{ Method originMethod = class_getInstanceMethod(self, originalSelector); const char * methodEncodeType = method_getTypeEncoding(originMethod); BOOL methodAdded = class_addMethod(self, newSelector, imp, methodEncodeType); if (methodAdded) { Method newMethod = class_getInstanceMethod(self,newSelector); method_exchangeImplementations(newMethod, originMethod); }else{ DDLogDebug(@"=====faile="); } }
在運行時中會維護 selector 和 IMP 對應關係表。
同理字典也同樣呢能夠使用這種方法 添加語法糖校驗。
#import <objc/message.h> static id dic_safe_initWithObjects(id self, SEL _cmd, const id* objects, const id* keys, unsigned int count) { id orignialResult = nil; @try { orignialResult = objc_msgSend(self, @selector(dic_safe_initWithObjects:forKeys:count:), objects, keys, count); } @catch (NSException *exception) { DDLogDebug(@"__NSPlaceholderDictionary===BUSTED!"); } return orignialResult; } @implementation NSDictionary (SafeCheck) +(void)load{ [objc_getClass("__NSPlaceholderDictionary") swizzleSelector:@selector(initWithObjects:forKeys:count:) withNewSelector:@selector(dic_safe_initWithObjects:forKeys:count:) andNewIMP:(IMP)&dic_safe_initWithObjects]; }