日常开发过程中,经常用到对数组NSArray,可变数组NSMutableArray的读取,写入操作;如果忘记进行非空或者越界判断,则,容易引起应用程序的运行异常,甚至崩溃;

另外一个就是,移动端为了更高效的执行程序,会用到多线程,那么,如何在多线程中,安全高效的使用可变数组;

非空与越界的解决方案:
1:使用方法交换(伪代码):

swizzling_exchangeMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:), @selector(arrayI_objectAtIndex:));

- (id)arrayI_objectAtIndex:(NSUInteger)index{
    if(index < self.count){
        return [self arrayI_objectAtIndex:index];
    }
    return nil;
}

分析

使用方法交换的优点:无缝对接系统的API,
缺点:由于使用的是方法交换,那么为了保证覆盖全面,不可避免的要对几乎所有的方法进行交换,增加工作量;若,同事或者第三方库的维护者,没有交换所有方法,那么使用者记不住或并不清楚那些方法进行了交换;增加了应用程序的安全风险;

2:使用category,对系统方法进行扩展

#import <Foundation/Foundation.h>
@interface NSArray (MicSafeNSArray)
- (NSUInteger)mic_safeIndexOfObject:(id)anObject;
@end

#import "NSArray+MicSafeNSArray.h"

@implementation NSArray (MicSafeNSArray)

- (NSUInteger)mic_safeIndexOfObject:(id)anObject {
    if (anObject) {
        return [self indexOfObject:anObject];
    }
    return -1;
}
@end

分析

对系统扩展mic_safeIndexOfObject方法,让使用者调用的时候,做到心里有数;如果发现公有模块代码,没有自己需要的方法,也可很方便无影响的扩展;
完成代码,查看下边的附件

多线程访问可变数组安全

线程安全问题的现象:
1:多线程同时写入和操作数组,造成数组的不准确性;
2:在多线程中进行移除操作,可能产生数组越界崩溃问题;
3:线程竞争地址,有可能产生野指针;

解决方向:
1:更改数组操作:使用异步派发+GCD栅栏方法;
2:读取数据使用 同步派发+并行队列;

示例代码(伪代码):

判断是否包含一个对象:

- (BOOL)containsObject:(id)anObject {
    __block BOOL isExist = NO;
    dispatch_sync(_syncQueue, ^{
        isExist = [_array containsObject:anObject];
    });
    return isExist;
}

插入一个对象:

- (void)insertObject:(id)anObject atIndex:(NSUInteger)index {
    dispatch_barrier_async(_syncQueue, ^{
        if (anObject && index < [_array count]) {
            [_array insertObject:anObject atIndex:index];
        }
    });
}

完整代码查看

#import <Foundation/Foundation.h>
@interface MicThreadSafeMutableArray : NSMutableArray
- (id)objectAtIndex:(NSUInteger)index;
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index;
@end

#import "MicThreadSafeMutableArray.h"
@interface MicThreadSafeMutableArray ()
@property (nonatomic, strong) dispatch_queue_t syncQueue;
@property (nonatomic, strong) NSMutableArray* array;
@end

@implementation MicThreadSafeMutableArray
- (instancetype)initCommon {
    self = [super init];
    if (self) {
        //%p 以16进制的形式输出内存地址,附加前缀0x
        NSString* uuid = [NSString stringWithFormat:@"com.huofar.array_%p", self];
        //注意:_syncQueue是并行队列
        _syncQueue = dispatch_queue_create([uuid UTF8String], DISPATCH_QUEUE_CONCURRENT);
    }
    return self;
}
- (instancetype)init {
    self = [self initCommon];
    if (self) {
        _array = [NSMutableArray array];
    }
    return self;
}
- (id)objectAtIndex:(NSUInteger)index {
    __block id obj;
    dispatch_sync(_syncQueue, ^{
        if (index < [_array count]) {
            obj = _array[index];
        }
     });
    return obj;
}
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index {
    dispatch_barrier_async(_syncQueue, ^{
        if (anObject && index < [_array count]) {
            [_array insertObject:anObject atIndex:index];
        }
    });
}
#pragma mark - 数据操作方法 (凡涉及更改数组中元素的操作,使用异步派发+栅栏块;读取数据使用 同步派发+并行队列)
@end