app modifying the autolayout engine from a background thread [duplicate] - ios

This question already has answers here:
Getting a "This application is modifying the autolayout engine from a background thread" error?
(21 answers)
Closed 6 years ago.
I am almost done migrating an iOS app of mine to Swift 3.0.
But I still have a few cases similar to the one below.
Most of them I have been able to solve by putting the problematic code on the main thread.
In some other cases, I can't figure out, which part of my code is executing on the wrong thread. And I get a message like this one:
This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.
Stack:(
0 CoreFoundation 0x000000018765a1d8 <redacted> + 148
1 libobjc.A.dylib 0x000000018609455c objc_exception_throw + 56
2 CoreFoundation 0x000000018765a108 <redacted> + 0
3 Foundation 0x0000000188241ea4 <redacted> + 192
....................
16 libsystem_pthread.dylib 0x00000001866eece4 <redacted> + 200
17 libsystem_pthread.dylib 0x00000001866ee378 pthread_mutex_lock + 0
18 libsystem_pthread.dylib 0x00000001866edda4 start_wqthread + 4
)
Is there some special technic (option when using the debugger or ??) I can use to trace the path followed by the progran, to see where this is happening?

Obviously you are doing some UI update on back ground thread. Cant predict exactly where, without seeing your code.
These are some situations it might happen:-
you might be doing something on background thread and not using. Being in the same function this code is easier to spot.
DispatchQueue.main.async { // do UI update here }
calling a func doing web request call on background thread and its completion handler calling other func doing ui update.
to solve this try checking code where you have updated the UI after webrequest call.
// Do something on background thread
DispatchQueue.global(qos: .userInitiated).async {
// update UI on main thread
DispatchQueue.main.async {
// Updating whole table view
self.myTableview.reloadData()
}
}

I don't think there is any other inbuilt tool for debugging such crashes because it's the code which is modifying the AutoLayout UI elements/constraints from the code which is either running in Background thread or completion handlers. All completion handlers by default run in background thread. You need to use the GCD to update the UI elements from your completion handler blocks.

Related

Understanding iOS Crash [SIGABRT ABORT]

I just received my first crash report from Crashlytics and am attempting to correct the issue. Unfortunately it is only with a line of code that runs on older devices so I can't test it on my iPhone 6.
The crash report from Crashlytics highlights two threads, the first reads:
Fatal Exception: NSInvalidArgumentException
-[CABasicAnimation altitude]: unrecognized selector sent to instance 0x17734440
While the second reads:
Crashed: Map Update :: NSOperation 0x1a839470
SIGABRT ABORT at 0x316a3dfc
The indicated line of code for both threads is:
let relativeAlt = mylocation.altitude - appDelegate.elevation
Where:
let mylocation = self.mapView.myLocation
let appDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
I'm trying to understand what I'm reading in the crash report. The way I see it the program doesn't understand the altitude reference made for some reason? This doesn't make sense to me since this crash seems to occur after that app has been running for several minutes without error, the highlighted line of code is run possibly hundreds of times before the app crashed. What is really happening here?
Additional Information:
Since writing, I have received additional crashes that I believe stem from the same issue:
Crashed: Map Update :: NSOperation 0x19fb2d50
EXC_BAD_ACCESS KERN_INVALID_ADDRESS at 0x11d077ca
Crashed: Map Update :: NSOperation 0x145ced50
EXC_BAD_ACCESS KERN_INVALID_ADDRESS at 0x81450a64
The first highlighted the following line in my code (I believe since I had worked on the app since this beta release and the line numbers have changed slightly):
self.lastLocation = (self.mapView.myLocation as CLLocation).coordinate
While the second crash just gave me:
libobjc.A.dylib
objc_msgSend + 5
The first of the new crashes (That provided a line of code) provided this report:
Thread : Crashed: Map Update :: NSOperation 0x19fb2d50
0 libobjc.A.dylib 0x3105c708 objc_release + 7
1 FlightTracker 0x000ba830 FlightTracker.MapViewController. (locationManager (FlightTracker.MapViewController) -> (Swift.ImplicitlyUnwrappedOptional<ObjectiveC.CLLocationManager>, didUpdateLocations : Swift.ImplicitlyUnwrappedOptional<Swift.Array<Swift.AnyObject>>) -> ()).(closure #1) (MapViewController.swift:168)
2 Foundation 0x244ce0fd __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 8
3 Foundation 0x24438fc5 -[NSBlockOperation main] + 148
4 Foundation 0x2442b845 -[__NSOperationInternal _start:] + 768
5 Foundation 0x244d0a57 __NSOQSchedule_f + 186
6 libdispatch.dylib 0x315ad5d9 _dispatch_queue_drain$VARIANT$mp + 948
7 libdispatch.dylib 0x315ad0a9 _dispatch_queue_invoke$VARIANT$mp + 84
8 libdispatch.dylib 0x315af0d3 _dispatch_root_queue_drain + 330
9 libdispatch.dylib 0x315b01fb _dispatch_worker_thread3 + 106
10 libsystem_pthread.dylib 0x31720e25 _pthread_wqthread + 668
Probably not your problem, but I just had a SIGABRT that was driving me nuts (that's how I ended up looking at this question) and I'll post my solution in case it helps some future S.O. spelunker.
In my (iPad, not that it matters) app, you can push a button that results in the creation of a not-full-screen UIViewController which contains a UITableView, and this viewController is presented via UIPopoverController.
In my case I had a screw-up in my loading of the tableView items which, at the time of creating the tableView cell I ended up trying to add a null value into a dictionary. (It's a long story, having to do with an infrastructure class that expects the data to be in a certain format.)
Anyway, attempting to access newViewController.view caused the SIGABRT on that line, with no clue that the problem was related to filling the tableView cell. Nothing tableView-related was evident in the stack trace, so it took me quite a while to narrow things down. I eventually just guessed "maybe it's the tableview" and disconnected the IBOutlet and delegate/dataSource to see if the crash went away.
...And it did. Which lead me down the path of finding the real problem.
Anyway, that's my story. Hope it's helpful to someone.
Due to the lack of a full/proper crash report and the lack of more code and architecture, the following is an assumption using the little bits of information that are available.
You are accessing a variable in a background thread (NSOperation queue) that got released on another thread and now isn't available any longer, so the pointer shows to some other random object in the memory. And that random object surely has no idea what to do with the altitude message which is then causing the crash.
You have to make sure that all variables used in the background thread, are available and not released in another thread.
Wow, I just had another "impossible to track down" solution to this that was driving me bonkers.
Earlier in the day, I'd done a major refactor as some of the objects in my game had names like RFCFoo and some were like RfcBar and I wanted to standardize the capitalization.
So I used XCode's Refactor->Rename... tool, and it worked great, except for one thing:
It failed to rename one specific .xib file, which remained as "RFCBlahBlah.xib" when I was trying to load it as "RfcBlahBlah.xib"
Again, I hope this proves useful for some future SO searcher.

Disabling Zombies causes EXC_BAD_ACCESS

I am getting a crash in the app with the following stack trace-
Thread : Crashed: com.apple.main-thread
0 libobjc.A.dylib 0x39dfa66a objc_release + 9
1 libobjc.A.dylib 0x39dfb0d7 (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 358
2 CoreFoundation 0x2f4a6c69 _CFAutoreleasePoolPop + 16
3 CoreFoundation 0x2f53c1cb __CFRunLoopRun + 1306
4 CoreFoundation 0x2f4a6f0f CFRunLoopRunSpecific + 522
5 CoreFoundation 0x2f4a6cf3 CFRunLoopRunInMode + 106
6 GraphicsServices 0x343ff663 GSEventRunModal + 138
7 UIKit 0x31df216d UIApplicationMain + 1136
8 Batted 0x0009db07 main (main.m:16)
The crash occurs when zombies are not enabled in the Scheme's diagnostics options. However, when I enabled it the crash doesn't occur.
I have read some of the other Q&A regarding this, and all of them seem to advice that once this behavior is seen, enable the zombies and run the Zombie Profile Instruments on the Simulator.
I tried that, but Instruments doesn't seem to indicate anything wrong and the app works.
Any clue to what else can be done here to root-cause this issue?
I am using XCode 5.1 with iOS 7.1 in Simulator.
UPDATE 1
Found the offending code causing the problem but I am still not sure why it is causing the problem.
I am using CoreData, and in subclass of the NSManagedObject, I have -
- (void)willTurnIntoFault;
{
[super willTurnIntoFault];
if ([self observationInfo])
{
BNLogInfo(#"%# has observers:\n%#", [self objectID], [self observationInfo]);
}
}
In the above code, [self observationInfo] is the offending line.
When the app starts up, I loop over some of the NSManagedObjects in an enumeration block and set some property on it, which in turn fires the willTurnIntoFault method. Once the enumeration block completes, the crash happens.
The mystery is that the crash doesn't happen inside this method, but without this method subclassed everything runs fine.
The Q&As say to enable zombies (or run under the Zombies instrument) because a zombie often causes this sort of error.
But it's not the only cause.
Your program is corrupting the heap. Most likely it's overwriting the isa pointer (the class pointer) in some object that's in the autorelease pool, so when the run loop drains the autorelease pool, objc_release tries to dereference the bogus isa pointer and crashes.
Enabling zombies can mask an error like this because, with zombies, the runtime never actually frees objects. This means you end up with lots of parts of the heap that don't get used (unless you try to send a message to a zombie), so they never cause trouble if you corrupt them.
These sorts of crashes can be very difficult to debug, but there's a tool called “guard malloc” that sometimes helps. From the menu bar, choose Product > Scheme > Edit Scheme. Click the Run action in the list on the left. Then click the Diagnostics tab. Turn on the “Enable Guard Malloc” option. Then try to reproduce the crash. Guard malloc will detect certain types of heap corruption immediately and stop the program on the corrupting instruction.

Strategies for tracking down _objc_msgSend crashes inside dispatch_sync block

I'm receiving crash reports (via the excellent Hockey) indicating that I've got a memory problem in some code that's called inside a dispatch_sync block (or at least that's how I'm interpreting the crash report snippet below). I've not been able to recreate this crash on my test devices at all (so strategies like NSZombieEnabled are not helping me). I'm happy to make code changes to make the crash reports more informative (and ultimately solve the underlying issue), I just don't know where to start. Any thoughts?
Exception Type: SIGSEGV
Exception Codes: SEGV_ACCERR at 0xb000000c
Crashed Thread: 7
...
Thread 7 Crashed:
0 libobjc.A.dylib 0x3979bb66 _objc_msgSend + 6
1 libdispatch.dylib 0x39c898fb _dispatch_barrier_sync_f_invoke + 27
2 App 0x00260f23 __48-[Foo displayLinkCallback]_block_invoke + 147
3 libdispatch.dylib 0x39c85103 _dispatch_call_block_and_release + 11
4 libdispatch.dylib 0x39c894bf _dispatch_async_redirect_invoke + 111
5 libdispatch.dylib 0x39c8a7e5 _dispatch_root_queue_drain + 225
6 libdispatch.dylib 0x39c8a9d1 _dispatch_worker_thread2 + 57
7 libsystem_pthread.dylib 0x39db4dff __pthread_wqthread + 299
The dispatch_sync is provided a static serial queue. Is it possible that the _objc_msgSend is indicating a problem referencing this queue rather than some issue inside the block?
To pre-empt the obvious, I'm seeing no indication of deadlocks in these crash reports.
Update (8th Oct '13)
Adding code as requested (method and variable names changed, but still close to original). I suspect the issue is somewhere around the copying of foo. My hope was that this question would yield stategies for debugging this error. If 'check line by line' is the best strategy for debugging _objc_msgSend crashes inside dispatch_sync block then it's a little sad, but I'll take any help I can get at this point.
Also, I should point out that the crash I am investigating only ever happens on single-core devices and intermittently at that.
- (void) displayLinkCallback
{
dispatch_async(_frameDispatchQueue, ^{
if ([_lock tryLock])
{
dispatch_sync(_renderQueueSerial, ^{
NSObject *fooCopy = nil;
BOOL bar = NO;
// prevents deallocation during subsequent copy
// _foo is set and/or its child objects changed
// many times per second by other threads)
NSObject *foo = _foo;
// copy foo
fooCopy = [foo copy];
bar = [self needsBarGivenFoo:fooCopy];
if (bar)
{
_lastFoo = foo;
[self goFoo:fooCopy];
}
else
{
[self noFoo:fooCopy];
}
});
[_lock unlock];
}
});
}
I would suggest starting your analysis on those references in your source files. This symbolicated crashlog suggests that your crash stems from something in the displayLinkCallback method in the Foo class. You should probably start your investigation there.
It's telling you had a segmentation violation. But it's hard to diagnose further without seeing the source code for that method.

iOS Trying to figure out nasty crash

Normally the crash log indicates where this might be happening, but in this case, this is all I am getting. Lets say my main view is A and I have A,B, and C views.
This only happens if I do the following:
Go from A to B to C, go out of app and load up a few other apps. Then return to my app, go back to B, then go back to A (THIS IS WHERE IT CRASHES).
EDIT - I had posted the wrong thread before...here is the correct thread
CRASH LOG:
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x4daf03bd
Crashed Thread: 7
Thread 7 name: Dispatch queue: com.apple.root.default-priority
Thread 7 Crashed:
0 libobjc.A.dylib 0x36f22f78 objc_msgSend + 16
1 Foundation 0x33707d04 _NSDescriptionWithLocaleFunc + 44
2 CoreFoundation 0x34f3a96e __CFStringAppendFormatCore + 7998
3 CoreFoundation 0x34eb51d8 _CFStringCreateWithFormatAndArgumentsAux + 68
4 Foundation 0x33707c2e +[NSString stringWithFormat:] + 54
5 MyGreatApp 0x00061028 -[DataAccessor getProducts:div:productType:cat:searchsilver:completion:] (DataAccessor.m:301)
6 MyGreatApp 0x00017196 __36-[products showNationalCategories]_block_invoke_0 (products.m:1688)
7 libdispatch.dylib 0x37886c52 _dispatch_call_block_and_release + 6
8 libdispatch.dylib 0x378927b4 _dispatch_worker_thread2 + 256
9 libsystem_c.dylib 0x35b45df4 _pthread_wqthread + 288
10 libsystem_c.dylib 0x35b45cc8 start_wqthread + 0
Here is line 301 (Note, all the objects going into the string are NSStrings):
NSString *urlStr = [NSString stringWithFormat:#"%#?api_key=%#&device[duid]=%#&division=%#",apiUrl,apiKey, duid, division];
My wild guess is that something is being deallocated in viewDidUnload which is relied upon and not being re-initialized properly. In most cases, viewDidUnload does not get called while testing; and it's the one thing I can think might be happening while you are off in those other apps. Do some double-checking on all of A's properties which get used when the view comes on screen, and double-check that they are non-nil.
I think your callback is being run when a different view controller from the one you were expecting is being presented. I think its first best to work out the faulty string pointer by NSLogging them on separate code lines. Also have you used any _block decorations for those string parameters (or used them twice: both inside and outside the block) -- that would break the retain on them.

Crash in CFRelease when removing a range of objects from a mutable array

One of our testers is reporting the following crash:
0 APP_NAME_WAS_HERE 0x00074892 testflight_backtrace + 158
1 APP_NAME_WAS_HERE 0x000754bc TFSignalHandler + 244
2 libsystem_c.dylib 0x378ea7ec _sigtramp + 48
3 CoreFoundation 0x30ef42e6 CFRelease + 94
4 CoreFoundation 0x30f09a36 -[__NSArrayM removeObjectAtIndex:] + 294
5 CoreFoundation 0x30f4a65e -[NSMutableArray removeObjectsInRange:] + 90
6 APP_NAME_WAS_HERE 0x000570ca -[StoryViewController rewindToChunkIndex:] + 558
7 APP_NAME_WAS_HERE 0x00057396 -[StoryViewController restartChapter] + 22
Unfortunately, we can't reproduce the crash - we're only getting crash log sent through via TestFlight.
We did receive debug logs to confirm that the removeObjectsInRange is definitely receiving a valid range for the NSMutableArray that is being acted upon. (besides, that would kick up an exception rather than raising a signal, right?)
My only thought is that the object is getting a double release, but I'm not sure how this is possible with ARC switched on?
Note that the objects being removed are UIView subclasses, and before-hand, some or all of them may have been removed from their superviews. So I wouldn't be surprised if they were released at this stage, I just don't understand why that's causing it to crash!
EDIT: In an attempt to verify that it's an over-released object, I artificially tried over-releasing an object (using CFRelease(__bridge (CFTypeRef) obj) to force release in ARC environment) to see the type of crash log that it would produce. Unfortunately, it's a bit different, so perhaps it's not an over-release after all? Is it perhaps a scribble of some kind?
Here's what a definite over-release looks like:
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x00000000, 0x00000000
Crashed Thread: 0
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x369c732c __pthread_kill + 8
1 libsystem_c.dylib 0x36c20208 pthread_kill + 48
2 libsystem_c.dylib 0x36c19298 abort + 88
3 libsystem_c.dylib 0x36bd437a free + 374
4 libobjc.A.dylib 0x375e4d72 object_dispose + 14
5 CoreFoundation 0x362e9618 -[NSObject dealloc] + 76
6 UIKit 0x310323a8 -[UIView dealloc] + 620
7 libobjc.A.dylib 0x375e416e _objc_rootRelease + 30
8 CoreFoundation 0x362dc2e0 CFRelease + 88
9 APP_NAME_WAS_HERE 0x000cea98 -[StoryViewController rewindToChunkIndex:] (StoryViewController.m:584)
Here's what an over-release crash log looks like:
If you look at the stack trace, the crash occurs not because of wrong index, but because of a over-release of the objects.
NSArray sends a retain message when you add an object and a release message when you remove an object. Apparently, that release is crashing.
This means, you are over-releasing the object you added to your array.
Update
Are your sub-views strongly owned? Is your ownership modifier "strong" or "weak" or unsafe_unretained? Even in ARC, there can be unbalanced calls to retain if you don't "own" your variables properly. For example, since you are manually adding and removing views into another array, you should "own" it. Remove from superview will send a release to the view and addSubview will send a retain. When you build your views using XIB, the XIB loading mechanism uses the property'w ownership modifier and bump up the retain count accordingly when it adds it to the view (StoryViewController.view). Since XIB loading mechanism added it to subviews, you shouldn't unload it. If you want to unload it, you should "own" it by changing the property type of your subviews (outlets) to "strong", otherwise, you will end up messing the ownership.
Start thinking in terms of Object graphs and who owns what, when you write your ARC ownership modifiers. ARC is not like Garbage collection. Things like this will still happen :)
My fix for the problem was to turn the compiler's optimization level down to None [-O0] from the default setting of Fastest, Smallest [-Os] in the target's build setting (set in release only).
I'm not sure whether it's simply sidestepping the problem or whether there's actually a bug in the compiler, but there you go. And it explains why only testers were getting it.
Without seeing code, it's tough to say what the real problem is. I'd bet its a case of something being over-released. Remember, ARC does not apply to Core Foundation objects.
It's possible that you've assigned a property with an convenience constructor instead of with alloc and init. Such objects are autoreleased and must be explicitly retained or they will be immediately deallocated in the following cycle.

Resources