iOS之利用runtime,避免可變數組和可變字典為nil或者數組越界導致的崩潰


NSArray、NSMutableArray、NSDictionary、NSMutableDictionary、是我們的在iOS開發中非常常用的類。當然,在享受這些類的便利的同時,它們也給我們帶來一些困擾。粗心我們可能會調用addObject:傳入一個nil, 也有可能是會objectAtIndex:傳入一個越界的index。尤其是在數據基本依賴於服務端返回的的情況,這種crash大幅增加。最近項目上經常出現NSDictionary的setObject:forKey:的nil object的崩潰。我們希望為這種崩潰找一個解決辦法。

解決方案

函數包裝

我們希望能夠用一個統一的方法解決粗心的程序員可能傳入的nil object。我們最先想到的想法是對這些函數進行一個包裝,比如objectAtIndex,我們寫一個如下的函數

- (id)safeObjectAtIndex:(NSUInteger)index {   
    if (index >= self.count) {
        return nil;
    }
    return [self objectAtIndex:index];
}

以后所有調用objectAtIndex的地方統統替換為safeObjectAtIndex。 不過,這顯然不是我想要的,我不希望改變現有的調用方式,大家可能是通過[]或objectAtIndex(不推薦)的方式獲取數組元素,如果要做替換的話改動勢必非常大,而且不便於以后的移植。我們希望代碼調用objectAtIndex的時候,能夠被我們先捕獲到,進行處理之后再調用Cocoa的這個方法。Objective-C作為一門動態語言,有強大的動態加載的能力,提供了Method swizzling實現這樣的功能。現在,黑魔法起飛。

Method swizzling

關於Method swizzling的具體實現,不打算多說,這里談一下我遇到的問題。最初在做Method swizzling的時候,我嘗試去替換NSArrat的objectAtIndex:,但我始終沒有辦法替換掉這個方法。后來,搞了半天才發現,我們使用的NSAarray或者NSMutableArray並不是我們所看到樣子,它們是class cluster。objectAtIndex:並不是他們的方法,而是他們背后的concrete class: __NSArrayI __NSArrayM的方法。解決了這個問題,剩下的就很簡單了。

 

使用

直接把XTSafeCollection.hXTSafeCollection.m拖入工程,NSArrayNSMutableArrayNSDictionaryNSMutableDictionary這些類的API以前是怎么調用的,還怎么寫,完全不用修改。Demo里,我全部以傳統的會引起crash的方式調用代碼,以下是我的Demo的代碼和輸出

NSArray *array = @[@"a", @"b"];
NSMutableArray *mutableArray = [@[@"aa", @"bb"] mutableCopy];
     
// Object at index
NSLog(@"%@", array[10]);
NSLog(@"%@", mutableArray[100]);
     
// add object
[mutableArray addObject:nil];
     
// Insert object
[mutableArray insertObject:nil atIndex:0];
[mutableArray insertObject:@"cc" atIndex:10];
     
// Replace object
[mutableArray replaceObjectAtIndex:0 withObject:nil];
[mutableArray replaceObjectAtIndex:10 withObject:@"cc"];
     
// Dictionary
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionary];
mutableDictionary[nil] = @"1";
mutableDictionary[@"1"] = nil;
2015-08-25 18:10:23.932 XTSafeCollection[29067:443479] [__NSArrayI objectAtIndex:] index {10} beyond bounds [0...1]
2015-08-25 18:10:23.933 XTSafeCollection[29067:443479] (null)
2015-08-25 18:10:23.933 XTSafeCollection[29067:443479] [__NSArrayM objectAtIndex:] index {100} beyond bounds [0...1]
2015-08-25 18:10:23.933 XTSafeCollection[29067:443479] (null)
2015-08-25 18:10:23.934 XTSafeCollection[29067:443479] [__NSArrayM addObject:], NIL object.
2015-08-25 18:10:23.934 XTSafeCollection[29067:443479] [__NSArrayM insertObject:atIndex:] NIL object.
2015-08-25 18:10:23.934 XTSafeCollection[29067:443479] [__NSArrayM insertObject:atIndex:] index {10} beyond bounds [0...1].
2015-08-25 18:10:23.934 XTSafeCollection[29067:443479] [__NSArrayM replaceObjectAtIndex:withObject:] NIL object.
2015-08-25 18:10:23.934 XTSafeCollection[29067:443479] [__NSArrayM replaceObjectAtIndex:withObject:] index {10} beyond bounds [0...1].
2015-08-25 18:10:23.934 XTSafeCollection[29067:443479] [__NSDictionaryM setObject:forKey:] NIL key.

 

安裝

源碼: https://github.com/wuwen1030/XTSafeCollection

已知問題

替換NSMuatbelArray的objectAtIndex:引起鍵盤展示狀態態切換后台的崩潰,拋出*** -[UIKeyboardLayoutStar release]: message sent to deallocated instance 0x7f883beac9c0 在這里找到了解決方法


免責聲明!

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



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