iOS Core Data dispatch_async background queuing crash - ios

I'm getting a non-reproduceable crash and I'm not sure why. I'm caching images on a background queue. The image names are properties on a Core Data NSManagedObject subclass, CCCard. At the same time, I have a collection view that is also accessing these CCCards.
Here is the relevant code and notes to follow.
//CCDeckViewController.m --------------------------------------
- (void)viewDidLoad {
// This will fetch the CCCard objects from Core Data.
self.cards = [[CCDataManager shared] cards];
[self cacheImages];
}
- (void)cacheCardImages {
// Because these are NSManagedObjects, I access them in the background
// via their object ID. So pull the IDs out here.
NSArray *cardIds = [self.cards valueForKey:#"objectID"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
for (NSManagedObjectID *cardId in cardIds) {
// Fetch the actual CCCard object.
CCCard *card = (CCCard *)[[CCDataManager shared] objectWithId:cardId];
// Cache the card's image if it's not already there.
NSString *key = [card thumbnailCacheKey];
if ([self.cardImageCache objectForKey:key]) {
continue;
}
CCDiscardableImage *discardable = [CCHelper decompressedImageForPath:key size:[card thumbnailSize] tintable:[card tintable]];
[self.cardImageCache setObject:discardable forKey:key];
}
});
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CCCard *card = self.cards[indexPath.item];
// This line calls the code that crashes.
UIColor *color = [card color];
// More code that returns the cell.
}
// CCCard.m --------------------------------------
- (UIColor *)color {
// Crash happens on this line.
if (self.colorId.integerValue == 0) {
// some code
}
}
Here are parts of the stack trace from the Crashlytics report I think are most relevant:
Thread #0: Crashed: com.apple.main-thread
EXC_BREAKPOINT 0x000000000000defe
0 CoreData 0x24c1b070 _sharedIMPL_pvfk_core + 247
1 CoreData 0x24c1b071 _sharedIMPL_pvfk_core + 248
2 myapp 0x4286f -[CCCard color] (CCCard.m:37)
3 myapp 0x40bbb -[CCDeckViewController collectionView:cellForItemAtIndexPath:] (CCDeckViewController.m:127)
Thread #4: com.apple.root.utility-qos
0 libsystem_pthread.dylib 0x2321920c RWLOCK_GETSEQ_ADDR
1 libsystem_pthread.dylib 0x23219295 pthread_rwlock_unlock + 60
2 libobjc.A.dylib 0x22cb8e01 rwlock_tt<false>::unlockRead() + 8
3 libobjc.A.dylib 0x22cb5af5 lookUpImpOrForward + 488
4 libobjc.A.dylib 0x22cb5903 _class_lookupMethodAndLoadCache3 + 34
5 libobjc.A.dylib 0x22cbbd7b _objc_msgSend_uncached + 26
6 CoreData 0x24c1d3ab _PFObjectIDFastEquals64 + 38
7 CoreFoundation 0x233eeed4 CFBasicHashFindBucket + 1820
8 CoreFoundation 0x233ee775 CFDictionaryGetValue + 116
9 CoreData 0x24c17037 _PFCMT_GetValue + 122
10 CoreData 0x24c16ec7 -[NSManagedObjectContext(_NSInternalAdditions) _retainedObjectWithID:optionalHandler:withInlineStorage:] + 58
11 CoreData 0x24c1b45d _PF_FulfillDeferredFault + 940
12 CoreData 0x24c1afcf _sharedIMPL_pvfk_core + 86
13 myapp 0x42991 -[CCCard imagePath] (CCCard.m:53)
14 myapp 0x41d5b __39-[CCDeckViewController cacheCardImages]_block_invoke (CCDeckViewController.m:295)
It's frustrating because it never happens during development, so can't test any theories. I'm trying to understand the code and crash report to figure out the problem now. My guess is that it has something to do with faulting because it accesses the CCCard object's instance method, but then crashes when trying to read the property from it. But why?

You are violating thread confinement.
Per the Core Data documentation, a NSManagedObjectContext and any NSManagedObject associated with it must only be accessed on the queue that it is assigned to.
Your dispatch_async violates that rule and needs to be corrected. There is no longer a design where you can use Core Data in a dispatch_async like that.
If you want the processing to be asynchronous then you should spawn a private NSManagedObjectContext that is a child of your main NSManagedObjectContext and then execute a -performBlock: against it. That will give you background processing that is configured properly.
Also, I highly recommend turning on -com.apple.CoreData.ConcurrencyDebug 1 as that will catch issues like this during development.

Related

Crashlytics Crash with NSOperationQueue (QOS: UNSPECIFIED)

I'm getting a crash, reported in Crashlytics, and I have no idea how to reproduce the error, It happens randomly so it's hard to debug it with Xcode. Any ideas?
Crashed: NSOperationQueue 0x280419200 (QOS: UNSPECIFIED)
0 libobjc.A.dylib 0x22c471430 objc_retain + 16
1 CoreFoundation 0x22d2b5888 __CFBasicHashAddValue + 1480
2 CoreFoundation 0x22d1e64ac CFDictionarySetValue + 260
3 Foundation 0x22dd04888 _encodeObject + 732
4 myAPI 0x1062b44b0 -[DataCore encodeWithCoder:] (DataCore.m:236)
5 myAPI 0x1062909c4 -[DataHandle encodeWithCoder:] (DataHandle.m:53)
6 Foundation 0x22dd04aa8 _encodeObject + 1276
7 Foundation 0x22dc69c6c +[NSKeyedArchiver archivedDataWithRootObject:] + 168
8 myAPI 0x106288a34 __77+[CachableObject addObjectToCache:withCacheName:withTTL:withCompletionBlock:]_block_invoke (CachableObject.m:162)
9 Foundation 0x22dd198bc NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK + 16
10 Foundation 0x22dc21ab8 -[NSBlockOperation main] + 72
11 Foundation 0x22dc20f8c -[__NSOperationInternal _start:] + 740
12 Foundation 0x22dd1b790 __NSOQSchedule_f + 272
13 libdispatch.dylib 0x22ccc16c8 _dispatch_call_block_and_release + 24
14 libdispatch.dylib 0x22ccc2484 _dispatch_client_callout + 16
15 libdispatch.dylib 0x22cc6582c _dispatch_continuation_pop$VARIANT$mp + 412
16 libdispatch.dylib 0x22cc64ef4 _dispatch_async_redirect_invoke + 600
17 libdispatch.dylib 0x22cc71a18 _dispatch_root_queue_drain + 376
18 libdispatch.dylib 0x22cc722c0 _dispatch_worker_thread2 + 128
19 libsystem_pthread.dylib 0x22cea517c _pthread_wqthread + 472
20 libsystem_pthread.dylib 0x22cea7cec start_wqthread + 4
Here is what the code in DataCore.m looks like
- (void)encodeWithCoder:(NSCoder *)coder {
[super encodeWithCoder:coder];
[coder encodeObject:programFormatPlayInfo forKey:#"ProgramFormatPlayInfo"];
[coder encodeObject:bigScreenPlayInfo forKey:#"BigScreenPlayInfo"];
[coder encodeObject:pivotHandle forKey:#"PivotHandle"];
[coder encodeInteger:pivotDataLinkId forKey:#"PivotDataLinkId"];
[coder encodeInteger:viewContextId forKey:#"ViewContextId"];
[coder encodeBool:suppressImagePivot forKey:#"SuppressImagePivot"];
[coder encodeObject:attributeIds forKey:#"AttributeIds"];
[coder encodeObject:self.overflow forKey:#"Overflow"];
[coder encodeObject:self.cacheNameWithUser forKey:#"CacheNameWithUser"];
[coder encodeObject:self.metaData forKey:#"Metadata"];
}
and here is where I try to add an object to the cache,
not sure if it's failing to decode or something related to the background queue.
+ (void)addObjectToCache:(CachableObject*)object withCacheName:(NSString*)cacheName withTTL:(CacheTime)cacheTimeSeconds withCompletionBlock:(void(^)()) block {
CachableObject* theObject = object;
[_backgroundQueue addOperationWithBlock:^{
#autoreleasepool {
#try {
NSString * path = [CachableObject pathForCachedObject:cacheName];
NSDate * date = [NSDate date];
[object setCacheDate:date];
[object setTtlSeconds:[NSNumber numberWithInteger:cacheTimeSeconds]];
[object setApiVersion:APIVERSION];
// Add to NSCache
[[CachableObject objectCache] setObject:theObject forKey:cacheName];
// Add to file system
NSError* err = nil;
NSData * data = [NSKeyedArchiver archivedDataWithRootObject:theObject];
if (data) {
[data writeToFile:path options:NSDataWritingAtomic error:&err];
}
// Add to dynamic cache
unwrapObjectAndComplyWithClass(object, [DataHandle class], ^(id unwrappedObject) {
DataHandle *objectUnwrapped = unwrappedObject;
DataFrame *objectFrame = objectUnwrapped.frame;
for (NSString *eachDependencyName in objectFrame.dependencies) {
[[VVIDynamicCacheManager sharedManager]addDependencyToStore:eachDependencyName withCacheName:cacheName];
}
}, ^{
/*Not a data handle*/
});
} #catch (NSException* ex) {
NSLog(#"CachableObject: got an exception %#", ex);
} #finally {
if (block) {
block();
}
}
}
}];
}
Some thoughts here.
First, you're using #try/#catch. I believe that NSKeyedArchiver throws exceptions when it should actually return NSError objects. So, perhaps that's why you are doing it. But, you have to keep in mind that none of Apple's frameworks are guaranteed to be exception-safe. This means that catching exceptions can leave Apple's code (and, of course, your own) in inconsistent states that will prevent it from working correctly in the future.
I would strongly recommend either removing the #catch, or scoping it extremely tightly around the NSKeyedArchiver code, if that's why you are using it. This could be unintentionally introducing a variety of other bugs into your app.
Now, on to the specific crash. It's occurring in the runtime's memory management code. This points very strongly at heap corruption. This means you've got a pointer that doesn't point to a valid Objective-C object in memory. This can happen for many reasons, and is extremely common. The most common cause is known as a dangling pointer. However, it could also be caused by an over-release. And, I wouldn't be at all surprised if it is possible to use an #catch to trigger an over-release. (I know, I'm harping, but I've seen so many issues caused by this pattern)
What I generally recommend in these situations:
Look for other crashes that look heap-corruption-related
Try out Zombies in Instruments
Try malloc scribble, or guardmalloc, two other good memory debugging tools
It's hard, and often even impossible, to reason about heap corruption. Replicating the bug can also be impossible, as memory corruption is typically not deterministic.
So, just try to find and fix as many issues as you can. It's totally possible that one of them is responsible for a variety of crashes, one of which could be this one.

Threading crash in Google Analytics for iOS

Using the Google Analytics library for iOS version 3.0.9, (and 3.0.x in general), we see a lot of crashes like the one below.
They appeared to occur randomly.
Exception Type: SIGBUS
Exception Codes: BUS_ADRALN at 0xe756c0
Crashed Thread: 19
Thread 19 Crashed:
0 libsystem_platform.dylib 0x39e02e18 0x39e01000 + 7704
1 CoreFoundation 0x2bfd0d6f 0x2bf92000 + 257391
2 CoreFoundation 0x2c0716a3 0x2bf92000 + 915107
3 CoreFoundation 0x2c070417 0x2bf92000 + 910359
4 CoreFoundation 0x2bfd4a0b 0x2bf92000 + 272907
5 Communicator 0x00714541 +[GAIUsageTracker trackTarget:action:] + 193
6 Communicator 0x007049b3 -[GAITrackerImpl send:] + 47
7 Communicator 0x00157b8d __66+[xxx]_block_invoke (xxx.m:305)
8 libdispatch.dylib 0x39ca58cb 0x39ca4000 + 6347
9 libdispatch.dylib 0x39caeda3 0x39ca4000 + 44451
10 libdispatch.dylib 0x39cafcd7 0x39ca4000 + 48343
11 libsystem_pthread.dylib 0x39e06e31 0x39e06000 + 3633
It turns out that the GAI library is not as thread-safe as it claims. We were able to fix our Google Analytics crashes by serializing the calls.
(void (^)(void))block = {
// your analytics call, eg
[[[GAI sharedInstance] defaultTracker] send:....];
};
static dispatch_queue_t theQueue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
theQueue = dispatch_queue_create("Analytics Queue", DISPATCH_QUEUE_SERIAL);
});
dispatch_async(theQueue, block);
So all calls to the tracker are executed in code blocks serialized on the same queue ("theQueue" above), which forces only one code block to execute at a time.

iOS screen become black little by little

I'm implementing an iOS app. and found that sometimes the screen became black little by little.
for example, in a view controller, there are a collection view, a button, a page controller, and sometimes I found the collection view became black(or invisible), only black background is shown, after 1-2 seconds, the button is gone, then the whole screen is black. but if I put the app into background, then bring it back, everything is ok.
here is the code:
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSTimeInterval timeLeft = [UIApplication sharedApplication].backgroundTimeRemaining - 1.0;
if(timeLeft &lt 0)
{
completionHandler(UIBackgroundFetchResultNoData);
}
//create new thread to do something, and sleep at current thread
...create new threads
[NSThread sleep:timeLeft];
completionHandler(UIBackgroundFetchResultNewData);
});
}
the issue could be reproduced if I repeat the following actions several times:
Do some thing
Put app to background(press home button)
Bring app back to foreground
repeat 1 - 3
we found following error in organizer for our app:
: CoreAnimation: warning, deleted thread with uncommitted CATransaction; set CA_DEBUG_TRANSACTIONS=1 in environment to log backtraces.
after added , I got the following log:
Myapp[11496] : CoreAnimation: warning, deleted thread with uncommitted CATransaction; created by:
0 QuartzCore 0x31ca6a75 + 268
1 QuartzCore 0x31ca6929 + 224
2 QuartzCore 0x31cabddb + 86
3 QuartzCore 0x31cab9f7 + 150
4 UIKit 0x3203f919 + 344
5 UIKit 0x320bb11b + 138
6 UIKit 0x322b0ebf + 218
7 UIKit 0x322b1169 + 104
8 UIKit 0x322b1735 + 36
9 Myapp 0x002e538d __61-[AppDelegate application:performFetchWithCompletionHandler:]_block_invoke + 632
10 libdispatch.dylib 0x3a487d53 + 10
11 libdispatch.dylib 0x3a48d689 + 228
12 libdispatch.dylib 0x3a48d8dd + 56
13 libsystem_pthread.dylib 0x3a5b8c17 _pthread_wqthread + 298
14 libsystem_pthread.dylib 0x3a5b8adc start_wqthread + 8
my questions is:
how could application:performFetchWithCompletionHandler cause animation issue?
to answer questions:
1. I'm sure that phone is not going to sleep
2. source code. sorry the project is too big
Check that you are working with UI on main thread only, this should be the case.
Edit: - I've seen this one, although never used it(written by Peter Steinberger).
Also this answer can help avoiding several more problems.
The thread created to perform your fetch invokes a call to update your ui, when it finishes performing whatever task it is doing, and its no longer needed it is deallocated. UIUpdates MUST be performed on the main thread or they might not be performed right away.
Have you tried dispatching to the main thread whatever ui update you are performing there?
It's hard to know what you your completion handler is doing, but if it's updating UI try to dispatch them on the main thread
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSTimeInterval timeLeft = [UIApplication sharedApplication].backgroundTimeRemaining - 1.0;
if(timeLeft < 0)
{
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(UIBackgroundFetchResultNoData);
});
}
//create new thread to do something, and sleep at current thread
...create new threads
[NSThread sleep:timeLeft];
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(UIBackgroundFetchResultNewData);
});
});
}

NSGenericException reason Collection <NSConcreteMapTable: xxx>

this is the error that i see when present SKScene, this error occurs randomly and are not able to replicate
* Terminating app due to uncaught exception 'NSGenericException', reason: '* Collection < NSConcreteMapTable: 0x1459da60 > was mutated while being enumerated.'
what's happen?
tell me if you need any other info
thanks
EDIT:
*** First throw call stack:
(
0 CoreFoundation 0x025601e4 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x022298e5 objc_exception_throw + 44
2 CoreFoundation 0x025efcf5 __NSFastEnumerationMutationHandler + 165
3 Foundation 0x01e47f03 -[NSConcreteMapTable countByEnumeratingWithState:objects:count:] + 66
4 CoreFoundation 0x0253d77f -[__NSFastEnumerationEnumerator nextObject] + 143
5 SpriteKit 0x01d009f2 +[SKTextureAtlas(Internal) findTextureNamed:] + 232
6 SpriteKit 0x01cf709c __26-[SKTexture loadImageData]_block_invoke + 1982
7 SpriteKit 0x01d34d09 _Z14SKSpinLockSyncPiU13block_pointerFvvE + 40
8 SpriteKit 0x01cf6898 -[SKTexture loadImageData] + 228
9 SpriteKit 0x01cf65d9 __51+[SKTexture preloadTextures:withCompletionHandler:]_block_invoke + 241
10 libdispatch.dylib 0x02b117b8 _dispatch_call_block_and_release + 15
11 libdispatch.dylib 0x02b264d0 _dispatch_client_callout + 14
12 libdispatch.dylib 0x02b14eb7 _dispatch_root_queue_drain + 291
13 libdispatch.dylib 0x02b15127 _dispatch_worker_thread2 + 39
14 libsystem_c.dylib 0x02de1e72 _pthread_wqthread + 441
15 libsystem_c.dylib 0x02dc9daa start_wqthread + 30
)
libc++abi.dylib: terminating with uncaught exception of type NSException
I get the same exception on occasion. It's been around for a while and I've been trying to pinpoint it for weeks.
My suspicion is that it may occur due to preloading textures, either manually or triggered automatically by Sprite Kit while at the same time some other code causes textures to be loaded or accessed.
I have reduced my preloadTextures: calls to a single one but I still get the issue, just less often. I have tried to performSelector:onMainThread: whenever I run a selector that accesses or loads images (or just might internally) from within a completionBlock or other code that runs on a different thread.
I haven't had this crash the entire day today after I moved my user interface code to the main thread (it was called from a completion handler). I can't say 100% for sure whether this fixed it though.
I hope this helps a little. There's definitely something finicky going on, and if you do po 0x1459da60 (in lldb's command window, using the address provided by the exception) you'll see that it is the SKTextureAtlas texture list that is being modified. I hope that helps you pinpoint where the issue is coming from on your side.
From what I can tell this a sprite kit bug in the sprite kit method:
preloadTextures: withCompletionHandler:
The only way I was able to fix this was by removing this method completely.
According to apple docs the textures also get loaded if you access the size property.
So my workaround is just to do exactly that:
for (SKTexture *texture in self.texturesArray) {
texture.size;
}
It's not pretty but it works!
I had the same problem, when I tried to preload two simple animations. I tried to preload the animations in a dictionary and have them ready to be called via a string key. Here is what I tried
-(void)setupAnimDict {
animDict = [[NSMutableDictionary alloc] init];
[animDict setObject:[self animForName:#"blaze" frames:4] forKey:#"blaze"];
[animDict setObject:[self animForName:#"flame" frames:4] forKey:#"flame"];
}
-(SKAction *)animForName:(NSString *)name frames:(int)frames {
NSArray *animationFrames = [self setupAnimationFrames:name base:name num:frames];
SKAction *animationAction = [SKAction animateWithTextures:animationFrames timePerFrame:0.10 resize:YES restore:NO];
return [SKAction repeatActionForever:animationAction];
}
-(NSArray *)setupAnimationFrames:(NSString *)atlasName base:(NSString *)baseFileName num:(int)numberOfFrames {
[self preload:baseFileName num:numberOfFrames];
NSMutableArray *frames = [NSMutableArray arrayWithCapacity:numberOfFrames];
SKTextureAtlas *atlas = [SKTextureAtlas atlasNamed:atlasName];
for (int i = 0; i < numberOfFrames; i++) {
NSString *fileName = [NSString stringWithFormat:#"%#%01d.png", baseFileName, i];
[frames addObject:[atlas textureNamed:fileName]];
}
return frames;
}
-(void)preload:(NSString *)baseFileName num:(int)numberOfFrames {
NSMutableArray *frames = [NSMutableArray arrayWithCapacity:numberOfFrames];
for (int i = 0; i < numberOfFrames; i++) {
NSString *fileName = [NSString stringWithFormat:#"%#%01d.png", baseFileName, i];
[frames addObject:[SKTexture textureWithImageNamed:fileName]];
}
[SKTexture preloadTextures:frames withCompletionHandler:^(void){}];
}
When I called the setupDict method I sometimes got the same error as you. The problem was that preloading of my two animations run into each other. I got rid of the error by changing the
[SKTexture preloadTextures:frames withCompletionHandler:^(void){}];
to
if ([baseFileName isEqualToString:#"blaze"]) {
[SKTexture preloadTextures:frames withCompletionHandler:^{
[self setupFlame];
}];
} else {
[SKTexture preloadTextures:frames withCompletionHandler:^(void){}];
}
so that the first preloading was done before I attempted to preload the other.
I don't know if this is your problem, but if it is let us know.
Same thing still happening for me in Xcode 6.3 beta / Swift 1.2. Here is a temporary fix that has worked for me.
SKTextureAtlas.preloadTextureAtlases([SKTextureAtlas(named: "testAtlas")], withCompletionHandler: {
dispatch_async(dispatch_get_main_queue(), {
handler()
})
})
I actually wrapped this in a function so that all preloads route through it. That way if it gets fixed on the SpriteKit side, or if there are major flaws with this approach, I can remove the dispatch.

tableView crashes on end up with more than 16 items

This is very confusing.
I have a UITableView, which updates and works fine until it gets more than 16 items then it crashes when trying to endUpdates after calling insertRowsAtIndexPaths.
The NSIndexPaths being added are all valid. -numberOfRowsInSection returns the correct number. It is not throwing an error related to the data set, rather it crashes with
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
when endUpdates is called on the tableView.
The data source is all there, the NSIndexPaths are fine. the code works fine between 0 and 16 rows, but when I add a 17th it crashes. Additionally if I start it with 22 items it works fine, when I add the 23rd it crashes... if I call reload data instead of doing the update and insert process it works fine, so it's nothing to do with the data itself, and it shouldn't be anything to do with how I'm inserting the rows since it works through 16...
I'm completely perplexed. Here is my update method. It is being called on the main thread at all times.
- (void)updateConversation:(NSNotification*)notification
{
NSDictionary *updateInfo = [notification userInfo];
//NSLog(#"Got update %#", updateInfo);
if ([[updateInfo objectForKey:#"success"] integerValue] == YES) {
[self updateConversationUI];
int addedStatementCount = [[updateInfo objectForKey:#"addedStatementCount"] intValue];
if (addedStatementCount > 0) {
//[self.tableView reloadData];
[self.tableView beginUpdates];
int previousStatmentCount = [[updateInfo objectForKey:#"previousStatmentCount"] intValue];
NSLog(#"owner %i, Was %i, now %i, change of %i", self.owner, previousStatmentCount, (int)self.conversation.statements.count, addedStatementCount);
NSMutableArray *rowPaths = [[NSMutableArray alloc] init];
for (int i = previousStatmentCount; i < previousStatmentCount + addedStatementCount; i++) {
NSIndexPath *path = [NSIndexPath indexPathForRow:i inSection:0];
[rowPaths addObject:path];
}
[self.tableView insertRowsAtIndexPaths:rowPaths withRowAnimation:UITableViewRowAnimationBottom];
[self.tableView endUpdates];
[self.tableView scrollToRowAtIndexPath:[rowPaths lastObject] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
}
}
The rest of the crash past [self.tableView endUpdates] is UITableView
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
*** First throw call stack:
(
0 CoreFoundation 0x000000010189b795 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x00000001015fe991 objc_exception_throw + 43
2 CoreFoundation 0x0000000101852564 -[__NSArrayM insertObject:atIndex:] + 820
3 UIKit 0x0000000100317900 __46-[UITableView _updateWithItems:updateSupport:]_block_invoke691 + 173
4 UIKit 0x00000001002b5daf +[UIView(UIViewAnimationWithBlocks) _setupAnimationWithDuration:delay:view:options:factory:animations:start:animationStateGenerator:completion:] + 460
5 UIKit 0x00000001002b6004 +[UIView(UIViewAnimationWithBlocks) animateWithDuration:delay:options:animations:completion:] + 57
6 UIKit 0x00000001003174cb -[UITableView _updateWithItems:updateSupport:] + 2632
7 UIKit 0x0000000100312b18 -[UITableView _endCellAnimationsWithContext:] + 11615
8 Dev App 0x0000000100006036 -[ConversationViewController updateConversation:] + 998
9 CoreFoundation 0x00000001018f121c __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 12
10 CoreFoundation 0x000000010185370d _CFXNotificationPost + 2381
11 Dev App 0x00000001000055ac -[ConversationManager postNotification:] + 92
12 Foundation 0x0000000101204557 __NSThreadPerformPerform + 227
13 CoreFoundation 0x000000010182aec1 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
14 CoreFoundation 0x000000010182a792 __CFRunLoopDoSources0 + 242
15 CoreFoundation 0x000000010184661f __CFRunLoopRun + 767
16 CoreFoundation 0x0000000101845f33 CFRunLoopRunSpecific + 467
17 GraphicsServices 0x00000001039a23a0 GSEventRunModal + 161
18 UIKit 0x0000000100261043 UIApplicationMain + 1010
19 Dev App 0x0000000100003613 main + 115
20 libdyld.dylib 0x0000000101f2a5fd start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
This seems like a bug in the OS, the stack would indicate that it's something to do with the animation block but it happens no matter what I set the animation to, including none.
And to re-state. This works through 16 items, then inserting items past that causes a crash. It is also called at initial setup to load data, and it will load any number of items there, including well over 16. But when called again, purely as an update from 16 or more to something higher it will crash.
Most bizarre issue I've yet to encounter with table views...
under iOS 7 on device and simulator, latest Xcode for reference.
It seems that the problem is caused by my call of scrollToRowAtIndexPath (even though it crashes before it gets there...) combined with implementing tableView:estimatedHeightForRowAtIndexPath: by removing the row height estimate the crash went away... still seems like a bug in the animation system for tables to me. Thankfully I don't need the estimated row height, I had forgotten I had implemented it (trying to play nice by iOS 7 bites me again).
Place strategic breakpoints when you are incrementing the statement count, go through the loop of adding the rows as many times as you need to, and locate the statement that it's causing the crash, at that moment take a look at your objects and look for nil values as the error you are having it's trying to insert a nil object(or un-existent) from an Array.
I would personally recommend you to go through this loop entirely while keeping an exe for the path and previous statement count .
NSIndexPath *path = [NSIndexPath indexPathForRow:i inSection:0];
in my case the tableView:heightForHeaderInSection: method has been missing,
hadn't implemented the tableView:estimatedHeightForRowAtIndexPath method so this also could be solving the problem if you aren't using tableView:estimatedHeightForRowAtIndexPath:
Your problem is that you don't update the dataSource the tableView is using.
if you insert a row, you need to insert the appropriate object to the array the tableView is using.
Goodluck!

Resources