While testing an app after the small application refactoring i have received an "Crashlytics" crash log. Was trying to change the Model to avoid double synchronization method call(at the same time) by sending all of the next calls(starting from the 2nd call) to the private serial queue. I have added the following code:
@interface SynchronizationModel ()
@property (nonatomic) dispatch_queue_t synchronizationQueue;
@end
@implementation SynchronizationModel
BOOL isSynchronizationNotCalled = YES;
#pragma mark - Synchronization
+ (dispatch_queue_t)synchronizationQueue {
static dispatch_queue_t queue;
static dispatch_once_t onceToken;
const char *queueID = "com.app.synchronizationModel.queue";
dispatch_once(&onceToken, ^{
queue = dispatch_queue_create(queueID, DISPATCH_QUEUE_SERIAL);
});
return queue;
}
+ (void)sychronizeOfflineObjects {
if (isSynchronizationNotCalled) {
NSLog(@"%d synchronization without a queue, flag notCalled =", isSynchronizationNotCalled);
isSynchronizationNotCalled = NO;
[self synchronizeOfflineObjectsNonAsynchronous];
} else {
NSLog(@"%d synchronization inside of a queue, flag notCalled =", isSynchronizationNotCalled);
isSynchronizationNotCalled = NO;
__weak typeof(self)weakSelf = self;
dispatch_async(self.synchronizationQueue, ^{
__strong typeof(weakSelf)strongSelf = weakSelf;
[strongSelf synchronizeOfflineObjectsNonAsynchronous];
});
}
// [self sychronizeOfflineObjects]; - uncomment for recursive test
isSynchronizationNotCalled = YES;
}
Tested it using a recursive call as you can see. It did work. But have the opportunity to try to run the app on a device personally.
This method is called in the very few places. Mostly in the one apparent Controller:
- (IBAction)buttonWasTapped:(id)sender {
if(self.buttonImage.layer.animationKeys.count == 0){
[self synchronizationMethod];
}
}
- (void)synchronizationMethod {
NSLog(@"startWorkWithServer - originally here is some code but it works perfect");
[SynchronzationModel sychronizeOfflineObjects];
} else {
[self startAnimation];
[self performSelector:@selector(delayStop) withObject:nil afterDelay:2.0];
if([webConnectionAllowed isEqualToString:@"NotAllowed"]){
[self showAlertWithTitle:nil message:NSLocalizedString(@"Connection is allowed", nil)];
}
}
}
Have sent the app to my Team Lead for the check out. He has launched an app on a device but in one moment (guess it has been working for some time) it turned down with a SIGABRT error message.
Crashlytics report returns:
#0. Crashed: com.twitter.crashlytics.ios.exception
0 App 0x18751d CLSProcessRecordAllThreads + 820509
1 App 0x18751d CLSProcessRecordAllThreads + 820509
2 App 0x187415 CLSProcessRecordAllThreads + 820245
3 App 0x17b34f CLSHandler + 770895
4 App 0x185dfb __CLSExceptionRecord_block_invoke + 814587
5 libdispatch.dylib 0x1c942083 _dispatch_client_callout + 22
6 libdispatch.dylib 0x1c94e33b _dispatch_barrier_sync_f_invoke + 50
7 App 0x1857fd CLSExceptionRecord + 813053
8 App 0x185625 CLSExceptionRecordNSException + 812581
9 App 0x18513b CLSTerminateHandler() + 811323
10 libc++abi.dylib 0x1c4f393f std::__terminate(void (*)()) + 78
11 libc++abi.dylib 0x1c4f3443 __cxa_rethrow + 90
12 libobjc.A.dylib 0x1c4ff1bb objc_exception_rethrow + 42
13 CoreFoundation 0x1d1a55a1 CFRunLoopRunSpecific + 596
14 CoreFoundation 0x1d1a5341 CFRunLoopRunInMode + 104
15 GraphicsServices 0x1e97cbfd GSEventRunModal + 156
16 UIKit 0x223b3e27 -[UIApplication _run] + 574
17 UIKit 0x223ae551 UIApplicationMain + 150
18 App 0x148daf main (main.m:14)
19 libdispatch.dylib 0x1c96f50b (Missing)
But since at least i do not have dSYM file (only .txt from Crashlytics) there`s no obvious way for me to symbolicate the crash log or to understand what causes the problem. Going to test it on my device, but not sure if it will crash either.
It looks that something happens with a Threads, but i am not sure exactly what is the clue. Can someone advice how to figure out place causing the problem.
One more addition: there is a few people working on a project (i am pretty new to it) and i am not even sure that the code placed above has turned on the error (but it is very likely) or it was even before my changes took place. I just aded a small method, calling method normally for the 1st time and sending all of the rest calls happens in the same time to the private serial queue. Since it worked recursively and this is quite small change and uses only one additional private serial queue it confuses me, seems a little too small for a real crash (with the Threads problem in a report).
Would appreciate any advice.
Seems i should have used the correct modifiers here. Adding the "strong" to the synchronizationQueue property solved this issue.
This post helped to find the solution.
What property should I use for a Dispatch Queue after ARC?
Before ARC the queues used to be treated like a non object types so the "assign" went a long way. Leaving it empty would work out because it was a default modifier for non object types.
Not after the ARC has been introduced the queues are treated like and Objective-c objects and we should use "strong" with them. To be mentioned, the "strong" now is a default property attribute for Objective-c objects, still not sure why it did`t work out in the case given above.