Problem:
After I filled in the NSMutableArray object and tried to do something with it, it contains (id)0x0 for some indices. I thought adding nil to NSMutableArray is not possible at all in Objective-C, so I wonder why this is happening.
When does this happen?
"Sometimes," unfortunately. It is played, loading over ~ 5000 tiles to get a high enough amount to get a chance. Even with over 5,000 tiles, sometimes it goes flawlessly.
Context:
In my application there is a button that starts the download for map fragments for a specific region. Download takes place in parallel in the background thread, and reports are returned for each download fragment.
To allow cancellation of loading, in my bootloader of the same name I have a temporary NSMutableArray that saves a hash from each downloaded fragment. After canceling, I can use this list of hashes to delete all stored fragments in the database.
Saving hashes during loading seems fine, but when I really want to do something with it (I use [_currentTileHashes copy] to change it to NSArray to give the delete method), it throws an NSInvalidArgumentException on this line:
-[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[3402]
When I use the debugger to check the _currentTileHashes variable array, I really see that one or two of the indexes are actually null or (id)0x0 . This screenshot illustrates this:

Relevant Code:
This code from the callback for each download of the fragment, where it hashes the fragment, adds it to the hash array and calls the user interface for progress:
- (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]; } }
This erases the hashing (this is from the Mapbox iOS SDK ):
+ (NSNumber *)tileHash:(RMTile)tile { return [NSNumber numberWithUnsignedLongLong:RMTileKey(tile)]; } uint64_t RMTileKey(RMTile tile) { uint64_t zoom = (uint64_t)tile.zoom & 0xFFLL; // 8bits, 256 levels uint64_t x = (uint64_t)tile.x & 0xFFFFFFFLL; // 28 bits uint64_t y = (uint64_t)tile.y & 0xFFFFFFFLL; // 28 bits uint64_t key = (zoom << 56) | (x << 28) | (y << 0); return key; }
And finally, the code in which the exception occurs:
- (void)tileCacheDidCancelBackgroundCache:(RMTileCache *)tileCache { DebugLog(@"Finished canceling tile download"); [tileCache removeAllCachedImagesForTileHashes:[_currentTileHashes copy]]; [[NSNotificationCenter defaultCenter] postNotificationName:@"routeTileDownloadCanceled" object:nil]; }
Tested on iOS 8.4, 8.4.1 (iPhone 6) and 7.1 (iPhone 4)
Feel free to ask for more clarification if something is unclear.