在iOS中,数组是无法通过迭代器删除的,无论是enumerateObjectsUsingBlock迭代器还是for in,抑或是NSEnumerator都无能为力,当然 for循环更不行了,对于java编程的同学来说无法接受啊。
当然办法还是有的是,不然我不会在这里费口舌。
我们主要有两种方式来删除数组,NSPredicate与while循环,但是性能稍有不同。当然这里也会自定义迭代器删除,也是一种不错的选择。
一.使用NSPredicate删除数组元素
1.1 NSArray全部删除
NSArray * aArr = @[@1,@"A",@2.0f,@"B",@"3",@4,@5,@"6"];
aArr = [aArr filteredArrayUsingPredicate:[NSPredicate predicateWithValue:NO]];
1.2 NSArray选择性删除
NSArray * aArr = @[@1,@"A",@2.0f,@"B",@"3",@4,@5,@"6"];
aArr = [aArr filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id _Nonnull evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
if([evaluatedObject isKindOfClass:[NSString class]])
{
return NO;
}
return YES;
}]];
注意:以上是NSArray的删除方式,注意,他并不是直接删除NSArray的元素,而是删除的是拷贝后的数组,因此,这种删除我们必须通过接收返回值来得到新的数组集合
如果是NSMutableArray,我们可以使用新增的方法,这种方式是直接删除,但是原理和NSArray一致,都是从内部将新的集合复制给素组
1.3 NSMutableArray
NSArray * aArr = @[@1,@"A",@2.0f,@"B",@"3",@4,@5,@"6"];
NSMutableArray * bArr = [NSMutableArray arrayWithArray:aArr];
[bArr filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id _Nonnull evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
NSLog(@"count=%ld",[bArr indexOfObject:evaluatedObject]);
if([evaluatedObject isKindOfClass:[NSString class]])
{
return NO;
}
return YES;
}]];
关于NSPredicate删除数组是一种可以选择的方法,我们从Block中打印 count发现,原数组元素基本不会改变,因此我们推测,NSPredicate过滤运行流程如下:
①NSPredicate是复制原数组,复制到一个新的集合,假定称为集合CopyA
②通过Block过滤器过滤出集合CopyA中的元素元素到另一个数组集合,假定这个集合为集合CopyB
③返回过滤数组集合CopyB,释放集合CopyA,完成赋值到初始集合
在整个过程中,出现了过多的数组元素引用和数组赋值,造成使用内存过大,如果NSArray存储的是小数据,而不是多媒体或者大文本,可以不用忌讳。但是如果涉及到大图片,我们这种删除显然是有代价的,也就是可能出现OOM问题。
二.使用while循环遍历删除(推荐)
对于while循环,他的作用对象只能是NSMutableArray,因此,对于NSArray我们需要适当的转换成NSMutableArray。
2.1选择性清除方案(迭代删除)
NSArray * aArr = @[@1,@"A",@2.0f,@"B",@"3",@4,@5,@"6"];
//数组转换
NSMutableArray * bArr = [NSMutableArray arrayWithArray:aArr];
aArr = [aArr filteredArrayUsingPredicate:[NSPredicate predicateWithValue:NO]]; //释放元素内存
aArr = nil;
int i = 0;
while(bArr.count>i)
{
NSObject * itemObject = [bArr objectAtIndex:i];
if([itemObject isKindOfClass:[NSString class]])
{
[bArr removeObject:itemObject]; //释放
itemObject = nil;
}else{
i++;
}
}
aArr = bArr;
这种方式效率显而易见,不仅消耗内存少,而且效率相对较高,当然我们选择逆序迭代的时候结果也是一样。
NSArray * aArr = @[@1,@"A",@2.0f,@"B",@"3",@4,@5,@"6"];
//数组转换
NSMutableArray * bArr = [NSMutableArray arrayWithArray:aArr];
aArr = [aArr filteredArrayUsingPredicate:[NSPredicate predicateWithValue:NO]]; //释放元素内存
aArr = nil;
int i = 0;
while(bArr.count>i)
{
NSObject * lastObject = [bArr lastObject];
if([itemObject isKindOfClass:[NSString class]])
{
[bArr removeLastObject]; //释放
itemObject = nil;
}else{
i++;
}
}
aArr = bArr;
特别的,对于迭代全部删除,如NSArray<UIImage * > * 数组中的图片压缩存储,为了及时释放内存,这种优势会体现的更加明显
NSArray * aArr = @[@1,@"A",@2.0f,@"B",@"3",@4,@5,@"6"];
//数组转换
NSMutableArray * bArr = [NSMutableArray arrayWithArray:aArr];
aArr = [aArr filteredArrayUsingPredicate:[NSPredicate predicateWithValue:NO]]; //释放元素内存
aArr = nil;
while(bArr.count>0)
{
NSObject * lastObject = [bArr lastObject];
// 压缩,存储你的图片
[bArr removeLastObject]; //释放就图片
}
三.自定义迭代器(推荐)
NSArray 自带迭代器,但删除操作会引起Crash,因此我们需要自定义自己的迭代器,实现可删除迭代器
Iterator.h文件 |
#import <Foundation/Foundation.h>
@protocol Iterator <NSObject>
@required
/**
* 判断数组是否还有元素
*/
-(BOOL) hasNext;
/**
* 返回数组中的元素(索引为0的元素),必须先使用hasNext判断,否则可能出现数组越界异常
*/
-(NSObject *) next;
/**
* 删除数组中的元素(索引为0的元素),必须先使用hasNext判断,否则可能出现数组越界异常
*/
-(void) remove;
@end
NSMutableArray+Helper.h类目文件 |
#import <Foundation/Foundation.h>
#import "Iterator.h"
@interface NSMutableArray(Helper)
/**
*
* 返回一个迭代器
*/
-(id<Iterator> ) iterator;
@end
NSMutableArray+Helper.m类目文件 |
#import "NSMutableArray+Helper.h"
/**
*
*实现私有类实现
*/
@interface NSMutableArrayIterator:NSObject<Iterator>
/**
*自定义初始化方法
* @param array 目标数组,NSMutableArray 类型
*/
-(instancetype) initWidthMutableArray:(NSMutableArray *) array;
/**
* 持有的目标数组
*/
@property (nonatomic,strong) NSMutableArray * array;
@property (nonatomic,assign) int nextIndex;
@property (nonatomic,assign) int currentIndex;
@end
@implementation NSMutableArrayIterator
-(instancetype) initWidthMutableArray:(NSMutableArray *) array
{
if(self=[super init])
{
_array = array;
_nextIndex = 0;
_currentIndex = 0;
}
return self;
}
-(BOOL) hasNext
{
return _array!=nil && _array.count>_currentIndex;
}
-(NSObject *) next
{
_nextIndex ++ ;
_currentIndex = _nextIndex - 1;
if(_currentIndex>(_array.count-1))
{
NSException *e = [NSException
exceptionWithName: @"索引越界"
reason: @"迭代索引错误,不能大于元素数量"
userInfo: nil];
@throw e;
}
return [_array objectAtIndex:(_currentIndex)];
}
-(void) remove
{
if(_currentIndex<0)
{
NSException *e = [NSException
exceptionWithName: @"状态异常"
reason: @"迭代索引错误,不能小于0"
userInfo: nil];
@throw e;
}
[_array removeObjectAtIndex:(_currentIndex)];
if (_currentIndex< _nextIndex )
{
_nextIndex--;
_nextIndex = _nextIndex>0?_nextIndex:0;
}
_currentIndex= -1;
}
@end
@implementation NSMutableArray(Helper)
-(id<Iterator>) iterator{
NSMutableArrayIterator * iteratorArray = [[NSMutableArrayIterator alloc] initWidthMutableArray:self];
return iteratorArray;
}
@end
到这一步,我们完成了整个实现,因此,对于NSArray数组删除,我们需要将其转为NSMutableArray进行删除
这里使用 Windows版本的LLVM+GNUstep编译运行Objective-C代码