I'm trying to track down an error that appears to be definitely a timing issue. I have an application that uses a Universal Framework. When the process is complete in the framework an NSNotification is sent back to the application. We have recently added a third party framework to our framework. Now, while the methods for the third party framework are being executed, as execution returns to our framework I receive the following error in the Console Output:
Assertion failed: (exclusive), function assert_locked, file ../dep/include/boost/boost_1_55_0/boost/thread/pthread/shared_mutex.hpp, line 51.
But I'm not sure that is the ultimate issue, because our framework continues to execute and the NSNotification is sent back to the application. Right after the Notification is sent and execution returns to the calling method (or the method call) in our framework I see a warning on the executing thread. Then, execution continues back to the original calling method and the warning goes away.
Here's the weird part. If I step through the code very slowly, it might just work. If I'm not slow enough I get the SIGSTOP and the code never returns to the UI. If I'm too fast, I get a SIGABRT.
I've been trying to find the exact issue using Instuments. This answer to a similar question backed up my suspicion that this is a timing issue. I think the boost assert_locked Assertion might have something to do with this.
My code is pretty boring but I know you want to see it, so here it is:
- (void)generateImageTemplates:(UIImage *)src
{
int result = 0;
cv::Mat image = *(cv::Mat*)[self cvMatFromUIImage:src];
user = IEngine_InitUser();
int userID=0;
result = IEngine_AddFingerprintRAW(user, UNKNOWN, image.data, image.size().width, image.size().height);
result = IEngine_RegisterUser(user, &userID);
[[NSNotificationCenter defaultCenter] postNotificationName:#"InnovatricsComplete" object:self];
}
If you're wondering what result is, it's an error code. So far these have all come back equal to 0. Meaning no errors. I'll work on handling these errors once I can get a successful return to the UI without crashing.
Control returns to the method call:
- (void)generateImageTemplates:(UIImage *)processedImage
{
[self.captureCommand generateImageTemplates:processedImage];
}
Control returns to the method call in the application View Controller:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 0){
[self clearPressed:self.clearButton];
} else {
[self.cameraVC generateImageTemplates:self.processedImage];
}
}
Finally, the NSNotification callback code:
- (void)onInnovatricsComplete:(NSNotification *)note
{
[self.cameraVC willMoveToParentViewController:nil];
[self.cameraVC.view removeFromSuperview];
[self.cameraVC removeFromParentViewController];
}
I warned you it was pretty boring!
I'm completely stumped! Though I continue to surf the internet for clues, is there anybody out there who can help me resolve this issue?
Thank you.
Here are some screenshots (in reverse order):
look at NSUinteger’s answer in How to know where crash for postNotificationName:object:userInfo
the listener might be deallocated before it recieves the notification.
Related
For people wanting to reply quickly without reading the post: I am not hitting any memory limits. Read the whole post for details.
My WatchKit extension cannot properly function without the user first being "onboarded" through the phone app. Onboarding is where the user must accept the permissions that we require, so it's very crucial.
On my WatchKit extension, I wanted to display a simple warning for users who had not finished onboarding within our phone app yet.
As such, I thought I'd get the status of onboarding from the phone in two ways:
When the user opens the app/the app is activated (I use the willActivate method to detect this)
When the app finishes onboarding it sends a message to the watch of its completion (if the extension is reachable, of course)
Both of these combined would ensure that the status of onboarding is always kept in sync with the watch.
I wrote the first possibility in, utilizing reply handlers to exchange the information. It worked just fine, without any troubles. The warning telling the user to complete disappears, the extension does not crash, and all is well.
I then wrote in the second possibility, of the extension being reachable when the user finishes onboarding (with the phone then directly sending the companion the new status of onboarding). My extension crashes when it receives this message, and I am stuck with this odd error.
Program ended with exit code: 0
My extension does not even get a chance to handle the new onboarding status, the extension just quits and the above error is given to me.
I am not hitting any sort of memory limit. I have read the technical Q&A which describes what a memory usage limit error looks like, and I don't receive any sort of output like that whatsoever. As well, before the extension should receive the message, this is what my memory consumption looks like.
I have monitored the memory consumption of the extension right after finishing onboarding, and I see not a single spike indicating that I've gone over any kind of threshold.
I have tried going line by line over the code which manages the onboarding error, and I cannot find a single reason that it would crash with this error. Especially since the reply handler method of fetching the onboarding status works so reliably.
Here is the code of how I'm sending the message to the watch.
- (void)sendOnboardingStatusToWatch {
if(self.connected){
[self.session sendMessage:#{
LMAppleWatchCommunicationKey: LMAppleWatchCommunicationKeyOnboardingComplete,
LMAppleWatchCommunicationKeyOnboardingComplete: #(LMMusicPlayer.onboardingComplete)
}
replyHandler:nil
errorHandler:^(NSError * _Nonnull error) {
NSLog(#"Error sending onboarding status: %#", error);
}];
}
}
(All LMAppleWatchCommunicationKeys are simply #define'd keys with exactly their key as the string value. ie. #define LMAppleWatchCommunicationKey #"LMAppleWatchCommunicationKey")
Even though it's never called by the extension, here is the exact receiving code of the extension which handles the incoming data, if it helps.
- (void)session:(WCSession *)session didReceiveMessage:(NSDictionary<NSString *, id> *)message {
NSString *key = [message objectForKey:LMAppleWatchCommunicationKey];
if([key isEqualToString:LMAppleWatchCommunicationKeyOnboardingComplete]){
BOOL newOnboardingStatus = [message objectForKey:LMAppleWatchCommunicationKeyOnboardingComplete];
[[NSUserDefaults standardUserDefaults] setBool:newOnboardingStatus
forKey:LMAppleWatchCommunicationKeyOnboardingComplete];
dispatch_async(dispatch_get_main_queue(), ^{
for(id<LMWCompanionBridgeDelegate> delegate in self.delegates){
if([delegate respondsToSelector:#selector(onboardingCompleteStatusChanged:)]){
[delegate onboardingCompleteStatusChanged:newOnboardingStatus];
}
}
});
}
}
Before including this onboarding-related code, my WatchKit extension was tested by over 100 people, without any troubles. I am using the exact same custom error dialogue that I was using before, just with a different string. I cannot for the life of me figure out what is causing this crash, and the ambiguity of it has given me very little to work with.
Any help would be greatly appreciated. Thank you very much for taking your time to read my post.
Edit: I just tried creating a symbolic breakpoint for exit(), which is never hit. If I call exit() myself, it calls the breakpoint, so I know the breakpoint itself is working.
One question and one issue:
I have the following code:
- (void) registerForLocalCalendarChanges
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(localCalendarStoreChanged) name:EKEventStoreChangedNotification object:store ];
}
- (void) localCalendarStoreChanged
{
// This gets call when an event in store changes
// you have to go through the calendar to look for changes
[self getCalendarEvents];
}
These methods are in a class/object called CalendarEventReporter which contains the method getCalendarEvents (in the callback).
Two things:
1) If the app is in the background the callback does not run. Is there a way to make it do that?
2) When I bring the app back into the foreground (after having changed the calendar on the device) the app crashes without any error message in the debug window or on the device. My guess is that the CalendarEventReporter object that contains the callback is being garbage-collected. Is that possible? Any other thoughts on what might be causing the crash? Or how to see any error messages?
1) In order for the app to run in the background you should be using one of the modes mentioned in the "Background Execution and Multitasking section here:
uses location services
records or plays audio
provides VOIP
services
background refresh
connection to external devices
like through BLE
If you are not using any of the above, it is not possible to get asynchronous events in the background.
2) In order to see the crash logs/call stack place an exception breakpoint or look into the "Device Logs" section here: Window->Organizer->Devices->"Device Name" on left->Device Logs on Xcode.
To answer your first question, take a look at https://developer.apple.com/library/ios/documentation/iphone/conceptual/iphoneosprogrammingguide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html
What I did to get code running in the background is to do something like
In the .h file
UIBackgroundTaskIdentifier backgroundUploadTask;
In the .m file
-(void) functionYouWantToRunInTheBackground
{
self.backgroundUploadTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[self endBackgroundUpdateTask];
}];
//code to do something
}
-(void) endBackgroundUpdateTask
{
[[UIApplication sharedApplication] endBackgroundTask: self.backgroundUploadTask];
self.backgroundUploadTask = UIBackgroundTaskInvalid;
}
The code above I pretty much learned from objective c - Proper use of beginBackgroundTaskWithExpirationHandler
As for your second question, you should set a breakpoint where code is supposed to run when you bring the app back to the foreground. No one can figure out why an app crashes if not given enough code or information.
The solution to the second part of the question was to raise the scope of the object containing the callback code. I raised it to the level of the containing ViewController. This seems to work. I still can't figure out how to raise the Notification (i.e. execute the call back) if the notification comes while the app is in the background/suspended. This prevented the object containing the callback from being cleaned up.
I have a view with a bunch of UISwitch.
My problem is that when I tap on a switch I need to wait about 10 seconds before being able to tap any other switch of the view.
Here is my code :
-(void) didTapSwitch:(UISwitch *)sender
{
NSLog(#"BEGIN didTapSwitch, %#",sender);
DADudesManager *dudesManager = [DADudesManager getInstance];
DADude *updatedDude = [dudesManager.dudesList objectAtIndex:[[self.spendingDudesTableView indexPathForCell:sender.superview.superview] row]];
DAAccountManager *accountManager = [DAAccountManager getInstance];
[accountManager.accountsOperationQueue addOperationWithBlock:^{
NSLog(#"BACKGROUND OPERATION BEGINS switchDudeBeneficiates, %#",sender);
DASpendingsManager *spendingsManager = [DASpendingsManager getInstance];
[[spendingsManager.spendingObserver childByAppendingPath:self.spending.spendingID] updateChildValues:#{updatedDude.dudeName: [sender isOn] ? #"1" : #"0"}];
NSLog(#"BACKGROUND OPERATION ENDS switchDudeBeneficiates, %#",sender);
}];
NSLog(#"END switchDudeBeneficiates, %#",sender);
}
My spendingObserver is a Firebase object initiated before.
When the code above is executed, the NSLogs show almost instantaneously in the console, the data is updated online at the same time, but the switches don't react to any tap for another 9 to 11 secs.
Of course commenting the line [[spendingsManager.spendingObserver childByAppendingPath:self.spending.spendingID] updateChildValues:#{weakDude.dudeName: [weakSwitch isOn] ? #"1" : #"0"}]; removes the latency, so the problem must come from Firebase, but I have no clue what's going on.
I am probably missing something obvious as I'm pretty new to IOS development !
I can think couple of reasons.
You are sending the PayLoad in the main thread, which is causing the User INterface events to be suspended.
The code you ran, might be linked to other functions in the library you are using, that might be causing the lag.
TRY - >
try putting your code in an NSOperation and execute that. Or use GCD to do work on different thread just not the UI thread which is the main thead.
Step back and simplify. Make your switch code simply log the change in value. NSLog includes a timestamp, so you can tell when the switch events occur.
If do-nothing code responds quickly, as I suspect it will, then add log statements at the beginning and end of your switch action method. That way you can see if there is a delay between the beginning and end of the processing.
You could also run the app in instruments (time profiler) and see where your app is spending time.
In my IOS app, I'm sometimes getting the above error after a 3rd party library call, which seems to be allocating some memory, albeit not much (about 13MB);
Assertion failed: (result == KERN_SUCCESS), function +[XPCMachSendRight wrapSendRight:], file /SourceCache/XPCObjects/XPCObjects-46/XPCMachSendRight.m, line 27.
there's no indication where or why this is happening, and I couldn't find any help or even similar problems on the internet. Any ideas?
It happens when we call function before completion of another, i.e. it's threading problem. So check for that. For example:
[self dismissViewControllerAnimated:YES completion:^{
// correct place for code after completion
}];
// wrong place for code after completion
I have an iPhone/iPad app where one of the main functions is to arrange objects on a plot. I use Core Data to manage the objects' relationships; for this example, I'll talk about Units, which have a to-one relationship to a Plot (and a to-many inverse). Units' properties include positionX, positionY, and angle.
Each Unit (inherits from NSManagedObject) is paired with a UnitViewController (inherits from UIViewController). The Unit has a property .viewController and the UnitViewController has a property .object, so in different uses they can refer to each other. These are set when the Plot is opened or new Units are added (or re-added from Undo, etc).
Each UnitViewController has a UIPanGestureRecognizer for its view, and when that gesture occurs, the UnitViewController changes its .object's positionX and positionY values. When that happens, the UnitViewController then observes those changes through KVO and re-positions the view.
This may seem convoluted, but the reason I did it this way is that I can also change the position numerically in a UITableView. Here's an abbreviated version of the code, showing the crucial bits of the path. Some of these methods exist in my UIViewController+LD category, hence the general names.
- (IBAction)dragObject:(UIPanGestureRecognizer *)gesture
{
// GESTURE BEGAN
if ([gesture state] == UIGestureRecognizerStateBegan) {
[self beginGesture:gesture];
if (!_selected) [self setSelected:YES];
}
// turn on registration before last pass
if ([gesture state] == UIGestureRecognizerStateEnded || [gesture state] == UIGestureRecognizerStateCancelled) {
[[NSNotificationCenter defaultCenter] postNotificationName:ENABLE_UNDO_REGISTRATION object:nil];
}
// MOVE
[self dragUnitWithGesture:gesture];
// turn off registration after first pass
if ([gesture state] == UIGestureRecognizerStateBegan) {
[[NSNotificationCenter defaultCenter] postNotificationName:DISABLE_UNDO_REGISTRATION object:nil];
}
// GESTURE ENDED
if ([gesture state] == UIGestureRecognizerStateEnded ||
[gesture state] == UIGestureRecognizerStateCancelled) {
[self endGesture];
}
}
- (void)dragUnitWithGesture:(UIPanGestureRecognizer *)gesture
{
CGPoint translation = [gesture translationInView:self.view.superview];
[self saveNewObjectCenterWithTranslation:translation];
}
- (void)saveNewObjectCenterWithTranslation:(CGPoint)translation
{
[self saveNewObjectCenter:CGPointMake(initialCenter.x + translation.x, initialCenter.y + translation.y)];
}
- (void)saveNewObjectCenter:(CGPoint)center
{
CGPoint dataPoint = [Converter dataPointFromViewPoint:center];
self.object.positionX = [NSNumber numberWithFloat:dataPoint.x];
self.object.positionY = [NSNumber numberWithFloat:dataPoint.y];
}
- (void)beginGesture:(UIGestureRecognizer *)gesture
{
[[NSNotificationCenter defaultCenter] postNotificationName:BEGIN_UNDO_GROUPING object:nil];
self.initialCenter = self.view.center;
}
- (void)endGesture
{
NSError *error = nil;
[self.object.managedObjectContext save:&error];
[[NSNotificationCenter defaultCenter] postNotificationName:END_UNDO_GROUPING object:nil];
}
My issue comes from crash reports obtained through Crashlytics, because I cannot replicate the crash on my devices. There have been multiple reports that all occur with the stack trace:
_UIGestureRecognizerSendActions
-[UnitViewController dragObject:]
-[UnitViewController dragUnitWithGesture:]
-[UnitViewController saveNewObjectCenterWithTranslation:]
-[UIViewController(LD) saveNewObjectCenter:]
_sharedIMPL_setvfk_core + 110
-[NSObject(NSKeyValueObserverNotification) willChangeValueForKey:] + 180
NSKeyValueWillChange + 474
NSKeyValuePushPendingNotificationPerThread + 214
This particular one ended with:
Crashed: com.apple.main-thread
EXC_BAD_ACCESS KERN_INVALID_ADDRESS at 0x00000000
But I've also seen:
Fatal Exception: NSInternalInconsistencyException
An -observeValueForKeyPath:ofObject:change:context: message was received but not handled (keyPath: positionX)
Fatal Exception: NSObjectInaccessibleException
Core Data could not fulfill a fault for ‘0x00000000 <x-coredata://xxxxxx/Unit/p116>'
So my question: is there a known issue with this type of data modification? Is it simply too fast for the Core Data framework to handle? Or is there something else I could be doing wrong? This problem is only one of the ways Core Data issues have manifested in my app, and I'd love to get to the heart of the matter to make my app more stable.
Update:
I don't have enough reputation to post images, but here's a link to the full stack trace:
stackTrace
As much as I agree with Marcus on the spirit of reducing the number of save: calls, I'm not convinced that save: frequency is the source of this particular problem because it's only called when the pan gesture ends.
Instead, the crash-log screenshot looks very much like a KVO observer has been deallocated without first being removed as an observer.
Review your code and make sure that all addObserver:forKeyPath:context: calls are balanced by removeObserver:forKeyPath:context: and that it's not possible for your observer(s) to be deallocated without first removing their observances.
If you can't spot where it might be going wrong then perhaps edit your question to include the KVO-related code so we can take a look at it.
As for the could not fulfill a fault exception, that's a different issue and probably deserves a different stack overflow question, complete with the full stack trace.
Core Data can handle a surprising amount of data at just about any pace you want. However your code may be doing more work than is needed.
First, you appear to be saving on each move. That is completely unnecessary. Core Data works the same whether the entity is saved or not. However, saving to disk is expensive. So if you are saving on every movement/change you can be causing a problem. Removing those saves will only help. Consider saving only when the user expects a pause: leaving the app, changing views, etc.
Second, what thread is this crash coming from? If you have a multi-threaded situation here and you are receiving notifications on the wrong thread you can get some odd crashes. The UI expects/demands that you interact with it only on the main thread (thread 0). Core Data has some rules around threading as well. I cannot tell from your snippet of the stack trace if this is an issue. Posting more of the stack trace will help.
Update
Saving that often hurts the user experience because you are blocking the main thread on each save. While it should not directly cause a crash it could in theory cause notifications to get "queued up" waiting for the writes to be done. Better to save every X seconds or something than block on each notification firing.
Can you post the full stack trace? The snippet you showed has removed a great deal of information that will help to diagnose the problem.
As for not duplicating, that tends to also point towards a threading issue. Not every CPU is identical. A threading issue can show up on a small subset of devices and not duplicatable at all on others. I once had a threading issue show up on a single device.
Update
Further reflection. Wiring up User Events to saving in Core Data is just bad imho. There is no guarantee that the user is going to do anything sane. You would be far better off having the user event kick off a timer. If the timer is already present then kick it out a bit further. When the timer fires, you save.
That will give you a bit of disconnect and protect you from user behavior.
Answered
After a lot of digging and fixing of other bugs, I think I've figured out the problem. I haven't fixed it yet, but I'm able to repeat crashing behavior, so it's only a matter of how many hours it'll take to sort it out.
The problem lies in an ill-conceived undo grouping, which un-does part of an action but not all of it, and when that grouping is undone, a number of different crashes can occur, depending on what action is performed next. Basically, I'm left with a faulted NSManagedObject subclass instance that doesn't exist anymore, and the view controller that reacts to these other actions tries to call it from various places. When that happens, the app obviously crashes, but since none of the actions in the stack trace itself indicate the actual cause of the problem, it was hard to track down.
So this has nothing to do with save frequency, which is good. I'm still curious to see if it prevents the Core Data crashes that I believe were occurring on another thread.
Thanks to everyone who answered!
UPDATE
The actual problem was elsewhere. See my other question here to see the answer and the solution:
make NSUndoManager ignore a property of an NSManagedObject