iOS Crash during background data processing when adding NSOperations to NSOperationQueue - ios

My app is getting a 'random' crash when doing some background data processing. I am adding NSOperations (subclassed) to an array, then adding that array of NSOperations to a NSOperationQueue.
I am seeing 3-4 errors which all point to the this line in the stack trace:
[_operationQueue addOperations:_operationsArray waitUntilFinished:YES];
Here is how I am creating my NSOperations (LibSyncOperation) and adding them to _operationsArray, which eventually gets added to the _operationQueue
LibSyncOperation *libSyncOperation = [[LibSyncOperation alloc] initWithURL:path];
if(!libSyncOperation.isExecuting && !libSyncOperation.isFinished)
{
[_operationsArray addObject:libSyncOperation];
TFLog(#"Operation is *NOT* Executing, add to OperationQueue");
}
else
{
TFLog(#"Operation is already Executing, do not add to OperationQueue");
}
Here are some of the stack traces:
*** -[NSOperationQueue addOperations:waitUntilFinished:]: 3 (of 4) operations are finished, executing, or already in a queue, and cannot be enqueued
CoreFoundation-[NSException initWithCoder:]
0 CoreFoundation 0x18835f09c __exceptionPreprocess
1 libobjc.A.dylib 0x1947b5d78 objc_exception_throw
2 CoreFoundation 0x18835efdc -[NSException initWithCoder:]
3 Foundation 0x188ec1864 __addOperations
4 Foundation 0x188ec1bfc -[NSOperationQueue addOperations:waitUntilFinished:]
5 Tower-iSales-Tab 0x100223b54 __70-[NetworkManager checkForDataFilesShouldEmptyQueue:isInitialDownload:]_block_invoke_2183 in NetworkManager.m on Line 437
6 libdispatch.dylib 0x194d84420 _dispatch_call_block_and_release
7 libdispatch.dylib 0x194d843e0 _dispatch_client_callout
8 libdispatch.dylib 0x194d8b3fc _dispatch_root_queue_drain
9 libdispatch.dylib 0x194d8b638 _dispatch_worker_thread2
10 libsystem_pthread.dylib 0x194f19918 _pthread_wqthread
11 libsystem_pthread.dylib 0x194f197a8 start_wqthread
Another:
*** Collection <__NSMallocBlock__: 0x1784402a0> was mutated while being enumerated.
CoreFoundation
0 CoreFoundation 0x186226f50
1 libobjc.A.dylib 0x192c041fc objc_exception_throw
2 CoreFoundation 0x186226984
3 Foundation 0x186d91928 -[NSOperationQueue addOperations:waitUntilFinished:]
4 Tower-iSales-Tab 0x1002bbb54 __70-[NetworkManager checkForDataFilesShouldEmptyQueue:isInitialDownload:]_block_invoke_2183 in NetworkManager.m on Line 437
5 libdispatch.dylib 0x1931dc014 _dispatch_call_block_and_release
6 libdispatch.dylib 0x1931dbfd4 _dispatch_client_callout
7 libdispatch.dylib 0x1931e32b8 _dispatch_root_queue_drain
8 libdispatch.dylib 0x1931e34fc _dispatch_worker_thread2
9 libsystem_pthread.dylib 0x1933716bc _pthread_wqthread
10 libsystem_pthread.dylib 0x19337154c start_wqthread
Last One:
-[__NSBlockVariable__ countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance 0x1782499c0
CoreFoundation__methodDescriptionForSelector
0 CoreFoundation 0x18835f09c __exceptionPreprocess
1 libobjc.A.dylib 0x1947b5d78 objc_exception_throw
2 CoreFoundation 0x188363d14 __methodDescriptionForSelector
3 CoreFoundation 0x188361a7c ___forwarding___
4 CoreFoundation 0x1882814ac __forwarding_prep_0___
5 Foundation 0x188ec1ca0 -[NSOperationQueue addOperations:waitUntilFinished:]
6 Tower-iSales-Tab 0x1002afb54 __70-[NetworkManager checkForDataFilesShouldEmptyQueue:isInitialDownload:]_block_invoke_2183 in NetworkManager.m on Line 437
7 libdispatch.dylib 0x194d84420 _dispatch_call_block_and_release
8 libdispatch.dylib 0x194d843e0 _dispatch_client_callout
9 libdispatch.dylib 0x194d8b3fc _dispatch_root_queue_drain
10 libdispatch.dylib 0x194d8b638 _dispatch_worker_thread2
11 libsystem_pthread.dylib 0x194f19918 _pthread_wqthread
12 libsystem_pthread.dylib 0x194f197a8 start_wqthread
Is there some obvious reason this crash would occur? I understand what the error message is saying, that NSOperations that are executing or finished cannot be enqueued again, the thing is, my _operationsArray is re-allocating anytime the update process runs, to ensure there are no NSOperations that were already enqueued hanging around.
One thing I am not doing, is re-allocating the NSOperationQueue each time, I figure that could be the cause, but since I cannot reproduce the error on my end (the users are experiencing this), I would like to know for sure. Heres the code for my NSOperationQueue (note this is all in a singleton class)
if(!_operationQueue)
_operationQueue = [[NSOperationQueue alloc]init];
[_operationQueue setMaxConcurrentOperationCount:1];
Could this be the culprit?
Any advice is appreciated. Thanks.
EDIT
As requested, heres the main for my LibSyncOperation:
-(void)main
{
if (![self isCancelled])
{
[self willChangeValueForKey:#"isExecuting"];
executing = YES;
NSURLRequest *downloadRequest = [NSURLRequest requestWithURL:downloadURL cachePolicy:nil timeoutInterval:9999999];
NSURLConnection *downloadConnection = [[NSURLConnection alloc] initWithRequest:downloadRequest delegate:self];
[downloadConnection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[downloadConnection start];
if (downloadConnection)
{
do
{
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
} while (!downloadDone);
[self terminateOperation];
}
else
{
[self terminateOperation];
}
}
else
{
[self terminateOperation];
}
}

Related

Crash llibobjc.A.dylib objc_release + 36

I use crashlytics to get the crashes of my app, i am currently Crashlytics version: 3.9.3. Some users are getting a crash that I cannot seem to repro on my machine. This is the logs from Fabric:
Crashed: com.apple.root.default-qos
EXC_BAD_ACCESS KERN_INVALID_ADDRESS 0x00004a4b9a43bec8
#37. Crashed: com.apple.root.default-qos
0 libobjc.A.dylib 0x7fff6b43d184 objc_release + 36
1 MyApp 0x10805ad2d __66-[AppManager getCountriesDelegate:]_block_invoke (AppManager.m:1412)
2 libdispatch.dylib 0x7fff6c006591 _dispatch_call_block_and_release + 12
3 libdispatch.dylib 0x7fff6bffed50 _dispatch_client_callout + 8
4 libdispatch.dylib 0x7fff6c00bc61 _dispatch_queue_override_invoke + 880
5 libdispatch.dylib 0x7fff6c000941 _dispatch_root_queue_drain + 515
6 libdispatch.dylib 0x7fff6c0006ed _dispatch_worker_thread3 + 101
7 libsystem_pthread.dylib 0x7fff6c2c31ca _pthread_wqthread + 1387
8 libsystem_pthread.dylib 0x7fff6c2c2c4d start_wqthread + 13
- (void) getCountriesDelegate:(id<AppManagerDelegate>)delegate
{
__weak __typeof(id<AppManagerDelegate>) weakDelegate = delegate;
__weak __typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //my function definition
});
There are multiples reasons why you can get a EXC_BAD_ACCESS KERN_INVALID_ADDRESS error code. Appel's documentation says:
EXC_BAD_ACCESS/KERN_INVALID_ADDRESS — This is caused by the thread
accessing unmapped memory. It may be triggered by either a data access
or an instruction fetch; the Thread State section describes how to
tell the difference.
You should look deeply at your class AppManager. As the crashlog said's the crash occurs at line 1412 of file AppManager.m in the method getCountriesWithRespectToPurposeAndFavoriteDelegate:.

Core Data : executeFetchRequest from performBlockAndWait block creates a crash

I am using a performBlockAndWait call on managedObjectContext created using NSPrivateQueueConcurrancyType . In this call I do executeFetchRequest and that is creating a crash. I think it's related to core data multithreading.
- (void)getAllModelObjectsKindOfMethod {
[context performBlockAndWait:^{
// prepares the fetchRequest
fetchedRecords = [context executeFetchRequest:fetchRequest error:&fetchRequestError];
// processes the fetched objects
}];
[context reset];
}
Here is the crash log received from crashlytics
12. Crashed: com.apple.root.default-qos
0 libobjc.A.dylib 0x187bd41a0 objc_retain + 16 1
libobjc.A.dylib 0x187bd4218 objc_storeStrong + 44
2 Release 0x1005dcfe0 -[MyManager
getAllModelObjectsKindOfMethod] (MyManager.m:1214) 3
--------------- 4 --- call stack upto the MyManager class
9. NSManagedObjectContext 0x1747ce4c0
0 libsystem_kernel.dylib 0x1881518e8 __ulock_wait + 8 1
libdispatch.dylib 0x18802177c _dispatch_ulock_wait +
48 2 libdispatch.dylib 0x1880218a4
_dispatch_thread_event_wait_slow + 36 3 libdispatch.dylib 0x18801f4f0 _dispatch_barrier_sync_f_slow + 236 4 CoreData
0x18b4cd03c _perform + 232 5 CoreData
0x18b4dd8b8 -[NSManagedObjectContext(_NestedContextSupport)
executeRequest:withContext:error:] + 176 6 CoreData
0x18b42d5e0 -[NSManagedObjectContext executeFetchRequest:error:] +
580 7 Live-no-store 0x10057cfac
__169+[MyManager getAllModelObjectsKindOfMethod]_block_invoke (MyManager.m:1294) 8 CoreData 0x18b4d2214
developerSubmittedBlockToNSManagedObjectContextPerform + 152 9
libdispatch.dylib 0x18800e9a0 _dispatch_client_callout +
16 10 libdispatch.dylib 0x18801bee0
_dispatch_barrier_sync_f_invoke + 84 11 CoreData 0x18b4d211c -[NSManagedObjectContext performBlockAndWait:] + 304
12 ...... call stack from project's internal classes
I have also received below hint from crashlytics .

__NSCFNumber unrecognized selector sent to instance - Weird behavior

I'm having a really strange problem with an object of my class, inside a block a have a weak reference of my View Controller, part of the code in the block is
__weak typeof(self) weakSelf = self;
[weakSelf performSelector:#selector(reloadItems) withObject:nil afterDelay:0];
[weakSelf.view setUserInteractionEnabled:YES];
if (weakSelf.shouldPresentNotification) {
[[LLAPI sharedInstance] presentLatestNotification];
weakSelf.shouldPresentNotification = NO;
}
The problem is that probably a couple of my users have experienced this crash
[__NSCFNumber shouldPresentNotification]: unrecognized selector sent to instance 0xbb3c1937855f68da
Now, I understand that the crash report is telling me that weakSelf is an NSNumber object, but that doesn't really make any sense, and if that is true why would the crash be raised only until the variable "shouldPresentNotification" is accessed?
EDIT:
Here is the full stack trace:
Thread : Fatal Exception: NSInvalidArgumentException
0 CoreFoundation 0x0000000182154f5c __exceptionPreprocess
1 libobjc.A.dylib 0x0000000196d4bf80 objc_exception_throw
2 CoreFoundation 0x000000018215bc6c
__methodDescriptionForSelector
3 CoreFoundation 0x0000000182158c14 ___forwarding___
4 CoreFoundation 0x000000018205cdcc _CF_forwarding_prep_0
5 Laclud 0x00000001001109c4 __38-[LLMainVC processLacludSession]_block_invoke424 (LLMainVC.m:467)
6 Laclud 0x000000010011055c -[LLMainVC processLacludSession] (LLMainVC.m:516)
7 Laclud 0x000000010013ff30 __32-[LLGFVC sendFeedback]_block_invoke (LLGiveFeedbackVC.m:137)
8 Laclud 0x000000010014a40c __41-[LLAPI rateLonJet:like:success:failure:]_block_invoke (LLAPI.m:189)
9 Laclud 0x000000010034349c __66-[RKObjectRequestOperation setCompletionBlockWithSuccess:failure:]_block_invoke236 (RKObjectRequestOperation.m:481)
10 libdispatch.dylib 0x00000001975457b0 _dispatch_call_block_and_release
11 libdispatch.dylib 0x0000000197545770 _dispatch_client_callout
12 libdispatch.dylib 0x000000019754ae20 _dispatch_main_queue_callback_4CF
13 CoreFoundation 0x000000018210c258 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
14 CoreFoundation 0x000000018210a0c0 __CFRunLoopRun
15 CoreFoundation 0x0000000182038dc0 CFRunLoopRunSpecific
16 GraphicsServices 0x000000018d18c088 GSEventRunModal
17 UIKit 0x0000000187712f44 UIApplicationMain
18 Laclud 0x0000000100178900 main (main.m:15)
19 libdyld.dylib 0x00000001975768b8 start
Any help appreciated.
Thanks

AVPlayerPeriodicCaller crash

I’m getting crash, and I can’t figure out what is causing it. Here what I’m getting from crash report:
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000c
Triggered by Thread: 0
Thread 0 Crashed:
0 AVFoundation 0x2f271946 -[AVPlayerPeriodicCaller _effectiveRateChanged] + 418
1 AVFoundation 0x2f270fc8 __43-[AVPlayerTimelyCaller _timebaseDidChange:]_block_invoke + 232
2 libdispatch.dylib 0x3b04ed50 _dispatch_call_block_and_release + 8
3 libdispatch.dylib 0x3b04ed3c _dispatch_client_callout + 20
4 libdispatch.dylib 0x3b0516be _dispatch_main_queue_callback_4CF + 274
5 CoreFoundation 0x3039b674 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 4
6 CoreFoundation 0x30399f40 __CFRunLoopRun + 1304
7 CoreFoundation 0x303047a4 CFRunLoopRunSpecific + 520
8 CoreFoundation 0x30304586 CFRunLoopRunInMode + 102
9 GraphicsServices 0x352716ce GSEventRunModal + 134
10 UIKit 0x32c6388c UIApplicationMain + 1132
11 my-app 0x00029dd4 main (main.m:16)
12 libdyld.dylib 0x3b063ab4 start + 0
As I understand it’s coming from addPeriodicTimeObserverForInterval:queue:usingBlock:, so I’m always checking for block and queue to be correct. What else can cause this?
Thanks in advance.
UPDATE:
Here is method that calls addPeriodicTimeObserverForInterval:queue:usingBlock:
- (void)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(dispatch_queue_t)queue usingBlock:(void (^)(CMTime time))block
{
[block copy];
dispatch_async(self.operationQueue, ^{
//This method just makes sure, that self.player not nil
[self someMethodcompletionHandler:^{
if (self.observer) {
[self.player removeTimeObserver:self.observer];
self.observer = nil;
}
if (block && queue) {
self.observer = [self.player addPeriodicTimeObserverForInterval:interval
queue:queue
usingBlock:block];
}
}];
});
}
UPDATE 2:
I figured it out. But I don’t know, why that works. So my question, why it works? Why self.operationQueue better than just queue?
self.observer = [self.player addPeriodicTimeObserverForInterval:interval
queue:self.operationQueue
usingBlock:^(CMTime time){
dispatch_async(queue, ^{
block(time);
});
}];
Official AVPlayer documentation states:
Releasing the observer object without invoking removeTimeObserver: will result in undefined behavior
This means that you should keep the value returned from addPeriodicTimeObserverForInterval:queue:usingBlock: and before the observer block is released make sure that you call removeTimeObserver:
Also note that block objects are allocated on the stack by default (not the heap) so to be sure that your block is not released when the frame ends you should retain the block by calling copy.
To read more about block memory management see here.

Core Data crashing when doing count request using performBlock

My iOS app is crashing when doing two calls to countForFetchRequest at the same time on the same private queue NSManagedObjectContext using performBlock.
I setup my childContext like this
_childContext = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_childContext setParentContext:self.managedObjectContext];
And this is my performBlock that calls countForFetchRequest
[self.childContext performBlock:^{
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"History"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"url == %#", url];
NSError *error = nil;
NSUInteger num = [self.childContext countForFetchRequest:fetchRequest error:&error];
if(error != nil){
NSLog(#"Error getting count for history url %# %#", url, error);
return;
}
if(num > 0){ // Already have this in the history, don't re-add it
return;
}
History *history = (History *)[NSEntityDescription insertNewObjectForEntityForName:#"History" inManagedObjectContext:self.childContext];
history.url = url;
history.title = title;
if(![self.childContext save:&error]){
NSLog(#"Error occurred saving history item %# %# %#", title, url, error);
}
}];
And here is the crash log:
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x3a427350 __pthread_kill + 8
1 libsystem_c.dylib 0x3a39e11e pthread_kill + 54
2 libsystem_c.dylib 0x3a3da96e abort + 90
3 libc++abi.dylib 0x39978d4a abort_message + 70
4 libc++abi.dylib 0x39975ff4 default_terminate() + 20
5 libobjc.A.dylib 0x39f29a74 _objc_terminate() + 144
6 libc++abi.dylib 0x39976078 safe_handler_caller(void (*)()) + 76
7 libc++abi.dylib 0x39976110 std::terminate() + 16
8 libc++abi.dylib 0x39977594 __cxa_rethrow + 84
9 libobjc.A.dylib 0x39f299cc objc_exception_rethrow + 8
10 CoreData 0x31e868e0 -[NSManagedObjectContext(_NSInternalAdditions) _countWithNoChangesForRequest:error:] + 764
11 CoreData 0x31e833fe -[NSManagedObjectContext countForFetchRequest:error:] + 1062
12 CoreData 0x31e92470 __82-[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:]_block_invoke_0 + 460
13 libdispatch.dylib 0x3a345d26 _dispatch_barrier_sync_f_slow_invoke + 82
14 libdispatch.dylib 0x3a3404b4 _dispatch_client_callout + 20
15 libdispatch.dylib 0x3a3451b8 _dispatch_main_queue_callback_4CF$VARIANT$mp + 220
16 CoreFoundation 0x3205cf36 __CFRunLoopRun + 1286
17 CoreFoundation 0x31fcfeb8 CFRunLoopRunSpecific + 352
18 CoreFoundation 0x31fcfd44 CFRunLoopRunInMode + 100
19 GraphicsServices 0x35b992e6 GSEventRunModal + 70
20 UIKit 0x33ee52fc UIApplicationMain + 1116
21 Accountable2You Mobile 0x000e5d28 0xe4000 + 7464
22 Accountable2You Mobile 0x000e5cc4 0xe4000 + 7364
Thread 1 name: Dispatch queue: NSManagedObjectContext Queue
Thread 1:
0 libsystem_kernel.dylib 0x3a416f04 semaphore_wait_trap + 8
1 libdispatch.dylib 0x3a3462fc _dispatch_thread_semaphore_wait$VARIANT$mp + 8
2 libdispatch.dylib 0x3a34487c _dispatch_barrier_sync_f_slow + 96
3 CoreData 0x31e82df2 _perform + 166
4 CoreData 0x31e921c6 -[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:] + 238
5 CoreData 0x31e86770 -[NSManagedObjectContext(_NSInternalAdditions) _countWithNoChangesForRequest:error:] + 396
6 CoreData 0x31e833fe -[NSManagedObjectContext countForFetchRequest:error:] + 1062
7 Accountable2You Mobile 0x000ee7be 0xe4000 + 42942
8 CoreData 0x31e86072 developerSubmittedBlockToNSManagedObjectContextPerform_privateasync + 66
9 libdispatch.dylib 0x3a344eca _dispatch_queue_drain$VARIANT$mp + 138
10 libdispatch.dylib 0x3a344dbc _dispatch_queue_invoke$VARIANT$mp + 36
11 libdispatch.dylib 0x3a34591a _dispatch_root_queue_drain + 182
12 libdispatch.dylib 0x3a345abc _dispatch_worker_thread2 + 80
13 libsystem_c.dylib 0x3a375a0e _pthread_wqthread + 358
14 libsystem_c.dylib 0x3a3758a0 start_wqthread + 4
Am I using the performBlock correctly?
Edit: More details
The performBlock is being called in a webViewDidFinishLoad:webView delegate method. I have multiple UIWebViews and when they finish loading they are calling the delegate method which is in turn calling the performBlock to see if it needs to add the url to the core data database.
You can encapsulate the method causing the crash in:
#synchronized(self) {
[object yourMethod];
}
This will make sure that even while multithreading, the piece of code inside will be running just once at a time ... other threads will just have to wait. Frankly, this will work only if the cause of the crash is the concurrency.

Resources