runtime MethodSwizzle 實踐之 奇怪crash : [UIKeyboardLayoutStar release]: message sent to deallocated instance


 

情景: 使用MethodSwizzle 實現對數組、字典 等系統方法的安全校驗。顯然能達到預期效果,但實際發現當

鍵盤顯示的情況下  home app 進入后台,再單擊app  圖標 切換回前台時 發生crash :

 [UIKeyboardLayoutStar release]: message sent to deallocated instance

UIKeyboardLayoutStar 是鍵盤上的布局的視圖吧,

整個工程都在ARC下 構建,很奇怪,而且必須。

 

信息:

http://huang.sh/2015/02/%E4%B8%80%E4%B8%AA%E5%A5%87%E6%80%AA%E7%9A%84crash-uikeyboardlayoutstar-release/

http://code4app.com/ios/DurexKit%E5%AE%89%E5%85%A8%E5%B7%A5%E5%85%B7%E5%8C%85/5325b421933bf0463d8b49ec

 

其中都有提到DurexKit  原理都是一樣的,上面提到原因是替換了 NSArray的objectAtIndex: 方法,

不過在我的項目原因是替換了NSMutableArray 的objectAtIndex:( NSMutableArray和 NSArray 的objectAtIndex:都有替換,單獨替換 NSArray 的objectAtIndex:方法則不會引起crash) 

 

解決方案:給 添加非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];
}

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM