Ios 为什么我的NSMutableArray在某些索引上包含nil? 问题:

Ios 为什么我的NSMutableArray在某些索引上包含nil? 问题:,ios,objective-c,nsmutablearray,nsarray,mapbox,Ios,Objective C,Nsmutablearray,Nsarray,Mapbox,在我用对象填充一个NSMutableArray并尝试对其进行处理后,它在某些索引上包含(id)0x0。我认为在Objective-C中将nil添加到NSMutableArray中根本不可能,所以我想知道为什么会发生这种情况 这是什么时候发生的? “有时”很不幸。下载超过5000块瓷砖就可以复制,只是为了让数量足够高,有可能发生这种情况。即使有超过5000块瓷砖,它有时也会完美无缺 背景: 我的应用程序有一个按钮,可以开始下载特定地区的地图分幅。下载在后台线程中并行进行,并为下载的每个磁贴返回报告

在我用对象填充一个
NSMutableArray
并尝试对其进行处理后,它在某些索引上包含
(id)0x0
。我认为在Objective-C中将nil添加到
NSMutableArray
中根本不可能,所以我想知道为什么会发生这种情况

这是什么时候发生的? “有时”很不幸。下载超过5000块瓷砖就可以复制,只是为了让数量足够高,有可能发生这种情况。即使有超过5000块瓷砖,它有时也会完美无缺

背景: 我的应用程序有一个按钮,可以开始下载特定地区的地图分幅。下载在后台线程中并行进行,并为下载的每个磁贴返回报告

为了取消下载,在我的downloader singleton中,我有一个临时的
NSMutableArray
,它从下载的每个磁贴中保存一个哈希。取消后,我可以使用该哈希列表删除数据库中保存的每个磁贴

在下载过程中保存散列看起来很好,但当我真的想用它做任何事情时(我使用
[\u currentTileHashs copy]
将其更改为
NSArray
以提供给delete方法),它会在那一行抛出一个
NSInvalidArgumentException

-[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[3402]
当我使用调试器检查
\u currentTileHash
可变数组时,我确实看到一个或两个索引实际上为nil或
(id)0x0
。此屏幕截图说明了这一点:

相关代码: 这段代码来自每次下载磁贴时的回调,它会对磁贴进行散列,将其添加到散列数组中,并回调UI以获取进度:

- (void)tileCache:(RMTileCache *)tileCache didBackgroundCacheTile:(RMTile)tile withIndex:(NSUInteger)tileIndex ofTotalTileCount:(NSUInteger)totalTileCount {
    DebugLog(@"Cached tile %lu of %lu.", (unsigned long)tileIndex, (unsigned long)totalTileCount);
    if (_currentlyDownloading) {
        float progress = (float)tileIndex / (float)totalTileCount;

        NSDictionary *progressDict = @{@"progress" : [NSNumber numberWithFloat:progress],
                                       @"routeId" : _downloadingRoute.routeId};

        [_currentTileHashes addObject:[RMTileCache tileHash:tile]];

        [[NSNotificationCenter defaultCenter]
         postNotificationName:@"routeTileDownloaded"
         object:progressDict];
    }
}
这是对磁贴进行哈希处理的方式(来自):

在iOS 8.4、8.4.1(iPhone 6)和7.1(iPhone 4)上测试


如果有不清楚的地方,请随时询问更多说明。

NSMutableArray
不是线程安全的,因此从多个并发后台下载更新实例可能会导致阵列损坏—正如您所看到的

我建议在更新阵列时使用@synchronized来保护它-

- (void)tileCache:(RMTileCache *)tileCache didBackgroundCacheTile:(RMTile)tile withIndex:(NSUInteger)tileIndex ofTotalTileCount:(NSUInteger)totalTileCount {
    DebugLog(@"Cached tile %lu of %lu.", (unsigned long)tileIndex, (unsigned long)totalTileCount);
    if (_currentlyDownloading) {
        float progress = (float)tileIndex / (float)totalTileCount;

        NSDictionary *progressDict = @{@"progress" : [NSNumber numberWithFloat:progress],
                                       @"routeId" : _downloadingRoute.routeId};
        @synchronized(_currentTileHashes) {
            [_currentTileHashes addObject:[RMTileCache tileHash:tile]];
        }
        [[NSNotificationCenter defaultCenter]
         postNotificationName:@"routeTileDownloaded"
         object:progressDict];
    }
}

好吧,你可以在数组中
[NSNull null]
。是的,这是真的,但在调试器中是可见的,由我显式设置。这是关于在
NSMutableArray
中使用真正的nil,而无需我明确地将其放在那里。
NSMutableArray
不是线程安全的-如果您同时下载多个tile,您将遇到问题,除非您通过@synchromized或其他技术保护对数组的更新。我建议您避免崩溃如果(tile!=nil)[arr addObject:tile],则应放入
@iphone-如果对象正在被动态删除,那么零检查充其量是不安全的。谢谢,这修复了我看到的损坏!我不知道
NSMutableArray
不是线程安全的。
- (void)tileCacheDidCancelBackgroundCache:(RMTileCache *)tileCache {
    DebugLog(@"Finished canceling tile download");

    [tileCache removeAllCachedImagesForTileHashes:[_currentTileHashes copy]];

    [[NSNotificationCenter defaultCenter]
     postNotificationName:@"routeTileDownloadCanceled"
     object:nil];
}
- (void)tileCache:(RMTileCache *)tileCache didBackgroundCacheTile:(RMTile)tile withIndex:(NSUInteger)tileIndex ofTotalTileCount:(NSUInteger)totalTileCount {
    DebugLog(@"Cached tile %lu of %lu.", (unsigned long)tileIndex, (unsigned long)totalTileCount);
    if (_currentlyDownloading) {
        float progress = (float)tileIndex / (float)totalTileCount;

        NSDictionary *progressDict = @{@"progress" : [NSNumber numberWithFloat:progress],
                                       @"routeId" : _downloadingRoute.routeId};
        @synchronized(_currentTileHashes) {
            [_currentTileHashes addObject:[RMTileCache tileHash:tile]];
        }
        [[NSNotificationCenter defaultCenter]
         postNotificationName:@"routeTileDownloaded"
         object:progressDict];
    }
}