美文网首页
iOS设计模式之迭代器模式下

iOS设计模式之迭代器模式下

作者: 点滴86 | 来源:发表于2024-06-30 00:25 被阅读0次

    迭代器模式

    在遍历的同时增删集合元素会发生什么?

    在通过迭代器来遍历集合元素的同时,增加或者删除集合中的元素,有可能会导致某个元素被重复遍历或遍历不到。不过,并不是所有情况下都会遍历出错,有的时候也可以正常遍历,所以,这种行为称为结果不可预期行为或者未决行为,也就是说,运行结果到底是对还是错,要视情况而定。

    @interface DMIteratorDesignPatterDemo : NSObject
    
    @end
    
    @implementation DMIteratorDesignPatterDemo
    
    - (void)test
    {
        DMMutableArray *tmpArray = [[DMMutableArray alloc] init];
        [tmpArray addObject:@"123"];
        [tmpArray addObject:@"456"];
        [tmpArray addObject:@"789"];
        
        DMMutableArrayIterator *iterator = [tmpArray iterator];
        [iterator next];
        [tmpArray removeObjectAtIndex:0];
    }
    
    @end
    

    在执行完DMMutableArrayIterator *iterator = [tmpArray iterator]; 时,数组中存储的是"123"、"456"、"789"三个元素,迭代器的游标mCursor指向元素"123"。当执行完[iterator next]; 的时候,游标mCursor指向元素"456",到这里都没有问题。
    为了保持数组存储数据的连续性,数组的删除操作会涉及元素的搬移。当执行到[tmpArray removeObjectAtIndex:0]; 的时候,会从数组中将元素"123"删除掉,"456"、"789"两个元素会依次往前搬移一位,这就会导致游标mCursor本来指向元素"456",现在变成了指向元素"789"。原本在执行完[iterator next]; 代码之后,还可以遍历到"456"、"789"两个元素,但在执行完[tmpArray removeObjectAtIndex:0]; 删除代码之后,只能遍历到"789"一个元素了,"456"遍历不到了。

    不过,如果代码删除的不是游标mCursor前面的元素以及游标mCursor所在位置的元素,而是游标mCursor后面的元素,这样就不会存在问题了,不会存在某个元素遍历不到的情况了。
    所以,在遍历的过程中删除集合元素,结果是不可预期的,有时候没问题,有时候就有问题,这个要视情况而定。
    在遍历的过程中删除集合元素,有可能会导致某个元素遍历不到,那在遍历的过程中添加集合元素,会发生什么情况呢?

    @interface DMIteratorDesignPatterDemo : NSObject
    
    @end
    
    @implementation DMIteratorDesignPatterDemo
    
    - (void)test
    {
        DMMutableArray *tmpArray = [[DMMutableArray alloc] init];
        [tmpArray addObject:@"123"];
        [tmpArray addObject:@"456"];
        [tmpArray addObject:@"789"];
        
        DMMutableArrayIterator *iterator = [tmpArray iterator];
        [iterator next];
        [tmpArray insertObject:@"abc" atIndex:0];
    }
    
    @end
    

    在执行完DMMutableArrayIterator *iterator = [tmpArray iterator]; 之后,数组中包含"123"、"456"、"789"三个元素,迭代器的游标mCursor指向元素"123"。当执行完[iterator next]; 的时候,游标mCursor指向元素"456",在执行完[tmpArray insertObject:@"abc" atIndex:0]; 之后,将"abc"插入到下标为0的位置,"123"、"456"、"789"三个元素依次往后移动一位。这个时候,游标mCursor又指向元素"123"。元素"123"被游标重复指向两次,也就是元素"123"存在被重复遍历的情况。
    跟删除情况类似,如果在游标的后面添加元素,就不会存在任何问题。所以,在遍历的同时添加集合元素也是一种不可预期行为。

    如何应对遍历时改变集合导致的未决行为?

    当通过迭代器来遍历集合的时候,增加、删除集合元素会导致不可预期的遍历结果。实际上,”不可预期“比直接出错更可拍,有的时候运行正确,有的时候运行错误,一些隐藏很深、很难debug的bug就是这么产生的。那如何才能避免出现这种不可预期的运行结果呢?
    有两种比较干脆利索的解决方案:一种是遍历的时候不允许增删元素,另一种是增删元素之后让遍历报错。
    实际上,第一种解决方案比较难实现,要确定遍历开始和结束的时间点。遍历开始的时间节点很容易获得。可以把创建迭代器的时间点作为遍历开始的时间点。但是,遍历结束的时间点很难确定。遍历到最后一个元素的时候结算结束?但是在实际的软件开发中,每次使用迭代器来遍历元素,并不一定非要把所有元素都遍历一遍。在迭代器类中定义一个新的方法 - (void)finishIteration 主动告诉容器迭代器使用完了,可以增删元素了,但是这要求程序员在使用迭代器之后要主动调用这个函数,增加了开发成本,还容易漏掉。
    实际上,第二种解决方法更加合理。增删元素之后,让遍历报错。
    怎么确定在遍历时候,集合有没有增删元素呢?在DMMutableArray中定义一个成员变量modCount,记录集合被修改的次数,集合每调用依次增加或者删除元素的方法,就会给modCount加1.当通过调用集合上的 - (DMMutableArrayIterator *)iterator 方法来创建迭代器的时候,会把modCount值传递给迭代器的expectedModCount成员变量,之后每次调用迭代器上的- (BOOL)hasNext 、- (void)next 、- (id)currentItem 方法,都会检查集合上的modCount是否等于expectedModCount,也就是看,在创建完迭代器之后,modCount是否改变过。
    如果两个值不相同,那就说明集合存储的元素已经改变了,要么增加了元素,要么删除了元素,之前创建的迭代器已经不能正确运行了,再继续使用就会产生不可预期的结果,直接抛出异常。

    @class DMMutableArrayIterator;
    
    @interface DMMutableArray<ObjectType> : NSObject
    
    @property (nonatomic, assign, readonly) NSUInteger count;
    
    - (NSInteger)getModCount;
    
    - (void)addObject:(ObjectType)anObject;
    
    - (instancetype)init;
    - (ObjectType)objectAtIndex:(NSUInteger)index;
    - (void)insertObject:(ObjectType)anObject atIndex:(NSUInteger)index;
    - (void)removeLastObject;
    - (void)removeObjectAtIndex:(NSUInteger)index;
    - (void)replaceObjectAtIndex:(NSUInteger)index withObject:(ObjectType)anObject;
    
    - (DMMutableArrayIterator *)iterator;
    
    @end
    
    @interface DMMutableArray ()
    
    @property (nonatomic, strong) NSMutableArray *mArray;
    
    @property (nonatomic, assign) NSInteger modCount;
    
    @end
    
    @implementation DMMutableArray
    
    - (instancetype)init
    {
        if (self = [super init]) {
            
        }
        return self;
    }
    
    - (id)objectAtIndex:(NSUInteger)index
    {
        return [self.mArray objectAtIndex:index];
    }
    
    - (void)addObject:(id)anObject
    {
        self.modCount += 1;
        [self.mArray addObject:anObject];
    }
    
    - (NSUInteger)count
    {
        return [self.mArray count];
    }
    
    - (NSInteger)getModCount
    {
        return self.modCount;
    }
    
    - (void)insertObject:(id)anObject atIndex:(NSUInteger)index
    {
        self.modCount += 1;
        [self.mArray insertObject:anObject atIndex:index];
    }
    
    - (void)removeLastObject
    {
        self.modCount += 1;
        [self.mArray removeLastObject];
    }
    
    - (void)removeObjectAtIndex:(NSUInteger)index
    {
        self.modCount += 1;
        [self.mArray removeObjectAtIndex:index];
    }
    
    - (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject
    {
        self.modCount += 1;
        [self.mArray replaceObjectAtIndex:index withObject:anObject];
    }
    
    - (NSMutableArray *)mArray
    {
        if (_mArray == nil) {
            _mArray = [[NSMutableArray alloc] init];
        }
        
        return _mArray;
    }
    
    - (DMMutableArrayIterator *)iterator
    {
        return [[DMMutableArrayIterator alloc] initWithDMMutableArray:self];
    }
    
    @end
    
    @interface DMMutableArrayIterator<ObjectType> : NSObject
    
    - (instancetype)initWithDMMutableArray:(DMMutableArray<ObjectType> *)array;
    
    - (BOOL)hasNext;
    
    - (void)next;
    
    - (ObjectType)currentItem;
    
    @end
    
    @interface DMMutableArrayIterator ()
    
    @property (nonatomic, assign) NSInteger mCursor;
    
    @property (nonatomic, strong) DMMutableArray *mArray;
    
    @property (nonatomic, assign) NSInteger expectedModCount;
    
    @end
    
    @implementation DMMutableArrayIterator
    
    - (instancetype)initWithDMMutableArray:(DMMutableArray *)array
    {
        if (self = [super init]) {
            self.mCursor = 0;
            self.mArray = array;
            self.expectedModCount = [array getModCount];
        }
        return self;
    }
    
    - (BOOL)hasNext
    {
        [self checkForComodification];
        BOOL bFlag = YES;
        if (self.mCursor >= [self.mArray count]) {
            bFlag = NO;
        }
        return bFlag;
    }
    
    - (void)next
    {
        [self checkForComodification];
        self.mCursor += 1;
    }
    
    - (id)currentItem
    {
        [self checkForComodification];
        return [self.mArray objectAtIndex:self.mCursor];
    }
    
    - (void)checkForComodification
    {
        if (self.expectedModCount != [self.mArray getModCount]) {
            @throw [NSException exceptionWithName:@"DMMutableArrayIteratorException" reason:@"集合元素改动了" userInfo:nil];
        }
    }
    
    @end
    

    相关文章

      网友评论

          本文标题:iOS设计模式之迭代器模式下

          本文链接:https://www.haomeiwen.com/subject/xtaucjtx.html