XTSafeCollection 1.0.5

XTSafeCollection 1.0.5

测试已测试
语言语言 Obj-CObjective C
许可证 MIT
发布最后发布2016年6月

wuwen维护。



  • 作者:
  • wuwen

背景

《NSArray》、《NSMutableArray》、《NSDictionary》、《NSMutableDictionary》是我们iOS开发中非常常用的类。当然,在享受这些类的便利的同时,它们也给我们带来了一些困扰。我们可能会不小心调用addObject时传入了一个nil,或者传入一个越界的索引到objectAtIndex。尤其是在数据基本上依赖于服务器返回的情况下,这类崩溃的出现几率大幅增加。最近项目上经常出现使用NSDictionarysetObject: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时,我尝试替换NSArratobjectAtIndex:,但我始终无法替换掉这个方法。后来,我发现了一个问题,我们使用的NSArrat或者NSMutableArray实际上并不是我们所看到的样子,它们是class clusterobjectAtIndex:并不是它们的方法,而是它们背后的concrete class__NSArrayI__NSArrayM 的方法。解决这个问题的关键在于找到了这个。

使用

直接将XTSafeCollection.hXTSafeCollection.m拖入工程,NSArrayNSMutableArrayNSDictionaryNSMutableDictionary这些类的API的调用方式与以前一样,无需进行修改。在示例中,我全部按照可能会引起崩溃的传统方式进行代码调用,以下是我的示例代码和输出:

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.
2015-08-25 18:10:23.934 XTSafeCollection[29067:443479] [__NSDictionaryM setObject:forKey:] NIL object.

版本:

  • 1.0.0
NSArray:
- (id)objectAtIndex:(NSUInteger)index;

NSMutableArray:
- (id)objectAtIndex:(NSUInteger)index;
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index;
- (void)addObject:(id)object;
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;

NSMutableDictionary:
- (void)setObject:(id)anObject forKey:(id <NSCopying>)aKey;
  • 1.0.2
NSArray:
+ (instancetype)arrayWithObjects:(const id [])objects count:(NSUInteger)cnt; ( @[] )

NSDictionary:
+ (instancetype)dictionaryWithObjects:(const id [])objects forKeys:(const id <NSCopying> [])keys count:(NSUInteger)cnt; ( @{} )

安装

pod "XTSafeCollection"

待办事项

兼容更多崩溃情况

已知问题

替换NSMuatbelArrayobjectAtIndex:可能会引起键盘展示状态切换至后台的崩溃,抛出

*** -[UIKeyboardLayoutStar release]: message sent to deallocated instance 0x7f883beac9c0

在这里http://huang.sh/2015/02/%E4%B8%80%E4%B8%AA%E5%A5%87%E6%80%AA%E7%9A%84crash-uikeyboardlayoutstar-release/找到了解决方法issue image