导读
当对一个可变数组操作很频繁,并且在多个线程操作同一个可变数组时,发生数组越界等奔溃是很常见的.所以使用runtime,对其方法进行交换.然后在交换方法中对增,删,改,查等做保护机制就可以避免类似情况发生.
部分源码解读
1.NSArray分类
1.新建一个NSArray的分类,在其.h文件中声明一些方法
@interface NSArray (SafeAccess)
-(id)objectWithIndex:(NSUInteger)index;
- (NSString*)stringWithIndex:(NSUInteger)index;
- (NSNumber*)numberWithIndex:(NSUInteger)index;
- (NSDecimalNumber *)decimalNumberWithIndex:(NSUInteger)index;
- (NSArray*)arrayWithIndex:(NSUInteger)index;
- (NSDictionary*)dictionaryWithIndex:(NSUInteger)index;
- (NSInteger)integerWithIndex:(NSUInteger)index;
- (float)floatWithIndex:(NSUInteger)index;
- (NSDate *)dateWithIndex:(NSUInteger)index dateFormat:(NSString *)dateFormat;
// CG
- (CGFloat)CGFloatWithIndex:(NSUInteger)index;
// Returns a reversed Array
- (NSArray *)Reverse;
/**
Applies the callback to the elements of the given arrays.
@return an new array containing all the elements of receiver after applying the callback function to each one.
*/
- (NSArray *)map:(id (^)(id obj))block;
/**
Converts receiver to json string. return nil if an error occurs.
*/
- (NSString *)jsonString;
@end
2.其.m文件的实现
// 导入runtime.h文件
#import <objc/runtime.h>
在+(void)load方法中交换方法的实现
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 1 添加空对象
SEL safeAdd = @selector(GFS_safeAddObject:);
SEL unsafeAdd = @selector(addObject:);
Method originalSEL = class_getInstanceMethod(objc_getClass("__NSArrayM"), unsafeAdd);
Method swizzleSEL = class_getInstanceMethod(objc_getClass("__NSArrayM"), safeAdd);
BOOL didAddMethod = class_addMethod(self, unsafeAdd, method_getImplementation(swizzleSEL), method_getTypeEncoding(swizzleSEL));
if (didAddMethod) {
class_replaceMethod(self, safeAdd, method_getImplementation(originalSEL), method_getTypeEncoding(originalSEL));
}else{
method_exchangeImplementations(originalSEL, swizzleSEL);
}
// 2 数组越界
SEL safeObjcAtIndex = @selector(GFS_safeObjcAtIndex:);
SEL unsafeObjcAtIndex = @selector(objectAtIndex:);
Class arryClass = NSClassFromString(@"__NSArrayI");
Method originalObjcAtIndex = class_getInstanceMethod(arryClass, unsafeObjcAtIndex);
Method swizzleObjcAtIndex = class_getInstanceMethod(arryClass, safeObjcAtIndex);
method_exchangeImplementations(originalObjcAtIndex, swizzleObjcAtIndex);
});
}
两个交换方法的实现
- (id)GFS_safeObjcAtIndex:(NSUInteger)index{
if (self.count - 1 < index) {
NSAssert(NO, @"beyond the boundary");
return nil;
}else{
return [self GFS_safeObjcAtIndex:index];
}
}
- (void)GFS_safeAddObject:(id)object{
if (!object) {// objectt == nil
NSAssert(NO, @"added a nil object");
}else{
[self GFS_safeAddObject:object];
}
}
其他方法的实现
-(id)objectWithIndex:(NSUInteger)index{
if (index >= [self count]) {
return nil;
}
id value = self[(NSUInteger) index];
if (value == [NSNull null]) {
return nil;
}
return value;
}
- (NSString*)stringWithIndex:(NSUInteger)index{
id value = [self objectWithIndex:index];
if (value == nil || value == [NSNull null]){
return @"";
}
if ([value isKindOfClass:[NSString class]]) {
return (NSString*)value;
}
if ([value isKindOfClass:[NSNumber class]]) {
return [value stringValue];
}
return nil;
}
- (NSNumber*)numberWithIndex:(NSUInteger)index{
id value = [self objectWithIndex:index];
if ([value isKindOfClass:[NSNumber class]]) {
return (NSNumber*)value;
}
if ([value isKindOfClass:[NSString class]]) {
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
return [f numberFromString:(NSString*)value];
}
return nil;
}
- (NSDecimalNumber *)decimalNumberWithIndex:(NSUInteger)index{
id value = [self objectWithIndex:index];
if ([value isKindOfClass:[NSDecimalNumber class]]) {
return value;
} else if ([value isKindOfClass:[NSNumber class]]) {
NSNumber * number = (NSNumber*)value;
return [NSDecimalNumber decimalNumberWithDecimal:[number decimalValue]];
} else if ([value isKindOfClass:[NSString class]]) {
NSString * str = (NSString*)value;
return [str isEqualToString:@""] ? nil : [NSDecimalNumber decimalNumberWithString:str];
}
return nil;
}
- (NSArray*)arrayWithIndex:(NSUInteger)index{
id value = [self objectWithIndex:index];
if (value == nil || value == [NSNull null]){
return nil;
}
if ([value isKindOfClass:[NSArray class]]){
return value;
}
return nil;
}
- (NSDictionary*)dictionaryWithIndex:(NSUInteger)index{
id value = [self objectWithIndex:index];
if (value == nil || value == [NSNull null]){
return nil;
}
if ([value isKindOfClass:[NSDictionary class]]){
return value;
}
return nil;
}
- (NSInteger)integerWithIndex:(NSUInteger)index{
id value = [self objectWithIndex:index];
if (value == nil || value == [NSNull null]){
return 0;
}
if ([value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSNumber class]]){
return [value integerValue];
}
return 0;
}
- (float)floatWithIndex:(NSUInteger)index{
id value = [self objectWithIndex:index];
if (value == nil || value == [NSNull null]){
return 0;
}
if ([value isKindOfClass:[NSNumber class]] || [value isKindOfClass:[NSString class]]){
return [value floatValue];
}
return 0;
}
- (NSDate *)dateWithIndex:(NSUInteger)index dateFormat:(NSString *)dateFormat {
NSDateFormatter *formater = [[NSDateFormatter alloc]init];
formater.dateFormat = dateFormat;
id value = [self objectWithIndex:index];
if (value == nil || value == [NSNull null]){
return nil;
}
if ([value isKindOfClass:[NSString class]] && ![value isEqualToString:@""] && !dateFormat) {
return [formater dateFromString:value];
}
return nil;
}
- (CGFloat)CGFloatWithIndex:(NSUInteger)index{
id value = [self objectWithIndex:index];
CGFloat f = [value doubleValue];
return f;
}
- (NSArray *)Reverse{
return self.reverseObjectEnumerator.allObjects;
}
- (NSArray *)map:(id _Nonnull (^)(id _Nonnull))block{
__block NSMutableArray *array = [NSMutableArray arrayWithCapacity:[self count]];
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[array addObject:block(obj)];
}];
return array;
}
- (NSString *)jsonString{
if ([NSJSONSerialization isValidJSONObject:self]) {
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self options:0 error:&error];
NSString *json = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
return json;
}
return nil;
}
2.NSMutableArray分类
.h文件声明
@interface NSMutableArray(SafeAccess)
-(void)addObj:(id)i;
-(void)addString:(NSString*)i;
-(void)addBool:(BOOL)i;
-(void)addInt:(int)i;
-(void)addInteger:(NSInteger)i;
-(void)addCGFloat:(CGFloat)f;
@end
.m文件实现方法
- +(void)load声明交换方法
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
id obj = [[self alloc] init];
[obj swizzleMethod:@selector(addObject:) withMethod:@selector(safeAddObject:)];
[obj swizzleMethod:@selector(objectAtIndex:) withMethod:@selector(safeObjectAtIndex:)];
[obj swizzleMethod:@selector(removeObjectAtIndex:) withMethod:@selector(safeRemoveObjectAtIndex:)];
[obj swizzleMethod:@selector(replaceObjectAtIndex:withObject:) withMethod:@selector(safeReplaceObjectAtIndex:withObject:)];
[obj swizzleMethod:@selector(removeObjectsInRange:) withMethod:@selector(safeRemoveObjectsInRange:)];
[obj swizzleMethod:@selector(insertObject:atIndex:) withMethod:@selector(safeInsertObject:atIndex:)];
});
}
- (void)swizzleMethod:(SEL)origSelector withMethod:(SEL)newSelector
{
Class cls = [self class];
Method originalMethod = class_getInstanceMethod(cls, origSelector);
Method swizzledMethod = class_getInstanceMethod(cls, newSelector);
BOOL didAddMethod = class_addMethod(cls,
origSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(cls,
newSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
- 交换方法的实现
- (id)safeObjectAtIndex:(NSUInteger)index {
if (index >= self.count) {
return nil;
}
return [self safeObjectAtIndex:index];
}
- (void)safeAddObject:(id)anObject {
if (!anObject) {
return;
}
[self safeAddObject:anObject];
}
- (void)safeRemoveObjectAtIndex:(NSUInteger)index {
if (index >= [self count]) {
return;
}
return [self safeRemoveObjectAtIndex:index];
}
- (void)safeReplaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject {
if (index >= [self count]) {
return;
}
if (!anObject) {
return;
}
[self safeReplaceObjectAtIndex:index withObject:anObject];
}
- (void)safeRemoveObjectsInRange:(NSRange)range {
if (range.location > self.count) {
return;
}
if (range.length > self.count) {
return;
}
if ((range.location + range.length) > self.count) {
return;
}
return [self safeRemoveObjectsInRange:range];
}
- (void)safeInsertObject:(id)anObject atIndex:(NSUInteger)index {
if (index > self.count) {
return;
}
if (!anObject) {
return;
}
[self safeInsertObject:anObject atIndex:index];
}
- 其他方法的实现
-(void)addObj:(id)i{
if ( i!= nil) {
[self addObject:i];
}
}
-(void)addString:(NSString*)i{
if (i != nil) {
[self addObject:i];
}
}
-(void)addBool:(BOOL)i{
[self addObject:@(i)];
}
-(void)addInt:(int)i{
[self addObject:@(i)];
}
-(void)addInteger:(NSInteger)i{
[self addObject:@(i)];
}
-(void)addCGFloat:(CGFloat)f{
[self addObject:@(f)];
}
3.新建一个继承NSObjec的SyncMutableArray类,将其作为线程安全数组使用
原理: 将其所有操作,包括增,删,改,查全部放到同一个队列中同步执行,即可保证线程安全.
- SyncMutableArray.h文件中方法的声明
@interface SyncMutableArray : NSObject
- (void)addObject:(id)anObject;
- (void)addObjectsFromArray:(NSArray *)anArray;
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index;
- (id)popFirstObject;
- (id)firstObject;
- (id)lastObject;
- (id)popLastObject;
- (void)removeLastObject;
- (void)removeObjectAtIndex:(NSUInteger)index;
- (void)removeObjectsAtIndexes:(NSIndexSet *)indexes;
- (void)removeObject:(id)anObject;
- (void)removeAllObjects;
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;
- (NSInteger)count;
- (BOOL)containsObject:(id)anObject;
- (id)objectAtIndex:(NSUInteger)index;
- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block;
- (NSMutableArray *)getArray;
@end
SyncMutableArray.m文件的实现
- 先声明一个可变数组和队列
@interface SyncMutableArray ()
@property(nonatomic, strong) NSMutableArray *array;
@property(nonatomic, strong) dispatch_queue_t dispatchQueue;
@end
- 在其初始化方法中给变量赋值
- (instancetype)init {
if (self = [super init]) {
_array = [NSMutableArray new];
_dispatchQueue = dispatch_queue_create("com.banggood.banggoodSycmutableArray", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- 然后实现可变数组的所有方法,并且在方法中进行同步操作并做保护措施
- (void)addObject:(id)anObject {
dispatch_sync(_dispatchQueue, ^{
if (!anObject) return;
[_array addObject:anObject];
});
}
- (void)addObjectsFromArray:(id)anArray {
dispatch_sync(_dispatchQueue, ^{
if (!anArray) return;
[_array addObjectsFromArray:anArray];
});
}
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index {
dispatch_sync(_dispatchQueue, ^{
if (!anObject) return;
[_array insertObject:anObject atIndex:index];
});
}
- (id)popLastObject {
__block id object;
dispatch_sync(_dispatchQueue, ^{
object = [_array lastObject];
[_array removeLastObject];
});
return object;
}
- (void)removeLastObject {
dispatch_sync(_dispatchQueue, ^{
[_array removeLastObject];
});
}
- (id)popFirstObject {
__block id object;
dispatch_sync(_dispatchQueue, ^{
object = [_array firstObject];
[_array removeLastObject];
});
return object;
}
- (id)firstObject {
__block id object;
dispatch_sync(_dispatchQueue, ^{
object = [_array firstObject];
});
return object;
}
- (id)lastObject {
__block id object;
dispatch_sync(_dispatchQueue, ^{
object = [_array lastObject];
});
return object;
}
- (void)removeAllObjects {
dispatch_sync(_dispatchQueue, ^{
[_array removeAllObjects];
});
}
- (void)removeObjectAtIndex:(NSUInteger)index {
dispatch_sync(_dispatchQueue, ^{
[_array removeObjectAtIndex:index];
});
}
- (void)removeObject:(id)anObject {
dispatch_sync(_dispatchQueue, ^{
if (!anObject) return;
[_array removeObject:anObject];
});
}
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject {
dispatch_sync(_dispatchQueue, ^{
if (!anObject) return;
_array[index] = anObject;
});
}
- (void)removeObjectsAtIndexes:(NSIndexSet *)indexes {
dispatch_sync(_dispatchQueue, ^{
[_array removeObjectsAtIndexes:indexes];
});
}
- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block {
dispatch_sync(_dispatchQueue, ^{
[_array enumerateObjectsUsingBlock:block];
});
}
- (NSMutableArray *)getArray {
__block NSMutableArray *temp;
dispatch_sync(_dispatchQueue, ^{
temp = _array;
});
return temp;
}
- (NSInteger)count {
__block NSInteger returnObject = 0;
dispatch_sync(_dispatchQueue, ^{
returnObject = _array.count;
});
return returnObject;
}
- (BOOL)containsObject:(id)anObject {
__block BOOL returnObject = NO;
dispatch_sync(_dispatchQueue, ^{
if (!anObject || ![_array isKindOfClass:[NSMutableArray class]]) return;
returnObject = [_array containsObject:anObject];
});
return returnObject;
}
- (id)objectAtIndex:(NSUInteger)index {
__block id returnObject = nil;
dispatch_sync(_dispatchQueue, ^{
if (_array.count > 0) {
returnObject = [_array objectWithIndex:index];
} else {
returnObject = nil;
}
});
return returnObject;
}
- (NSString *)debugDescription {
return [NSString stringWithFormat:@"%@", _array];
}
使用
- 导入线程安全数组头文件
#import "SyncMutableArray.h"
- 然后写一个循环多次,多个子线程对可变数组进行增,删,改,查操作
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self syncMutableArray];
}
- (void)syncMutableArray {
NSDate *startDate = [[NSDate alloc] init];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
SyncMutableArray *safeArr = [[SyncMutableArray alloc] init];
for ( int i = 0; i < 1000; i ++) {
dispatch_async(queue, ^{
NSLog(@"添加第%d个",i);
[safeArr addObject:[NSString stringWithFormat:@"%d",i]];
});
dispatch_async(queue, ^{
NSLog(@"删除第%d个",i);
[safeArr removeObjectAtIndex:i];
});
dispatch_async(queue, ^{
NSLog(@"读取第%d个数据:%@",i,[safeArr objectAtIndex:i]);
});
}
NSDate *endDate = [[NSDate alloc] init];
double time = [endDate timeIntervalSince1970] - [startDate timeIntervalSince1970];
NSLog(@"执行时间:%f",time);
}
总结
要想使多个线程操作同一个可变数组,并且并发量比较大的时候不会发生crash.可以采用runtime + 线程安全数组来实现.