Breakpoint-only error with managed objects - objective-c

While trying to debug my program in Xcode 4.2, I turned on breakpoints and discovered a problem in this piece of code located in my AppDelegate.m file.
#pragma mark -
#pragma mark Core Data stack
/**
Returns the managed object context for the application.
If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
*/
- (NSManagedObjectContext *)managedObjectContext {
if (managedObjectContext_ != nil) {
return managedObjectContext_;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext_ = [[NSManagedObjectContext alloc] init];
[managedObjectContext_ setPersistentStoreCoordinator:coordinator];
}
return managedObjectContext_;
}
At
if (managedObjectContext_ != nil) {
Xcode tells me that "Thread 1: Stopped at breakpoint #" and refuses to finish compiling my program. However, if I turn off breakpoints and run my program normally, it works just fine. Does anyone know why this is so? Thanks in advance :)

The breakpoints are doing what they are supposed to do, which is to stop the program so you can examine the variable values and fix bugs. Pressing the button which roughly looks like |> will make it resume executing.

Related

Undo/Redo Menu Items Never Enabled

I have a core data application with an NSTableView bound to an NSArrayController. I manage adding and removing objects using the array controller. I'm trying to add undo/redo support so when a person deletes an object from the table view, using a menu item, they can undo the delete.
My delete method is:
- (IBAction)removeHost:(id)sender
{
NSInteger row = [bookmarkList selectedRow];
// Get the object so we can get to the attributes of the host
NSArray *a = [bookmarksController arrangedObjects];
NSManagedObject *object = [a objectAtIndex:row];
if (!object) return;
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSUndoManager *undoManager = [managedObjectContext undoManager];
if (managedObjectContext.undoManager == nil)
{
NSLog(#"No undo manager in app controller!");
} else {
NSLog(#"We've got an undo manager in app controller!");
}
[undoManager registerUndoWithTarget:self selector:#selector(addBookmarkObject:) object:object];
[bookmarksController removeObject:object];
[undoManager setActionName:#"Bookmark Delete"];
}
Deleting the object works fine, but undo does not. The Command-Z menu item is never enabled. I setup a temporary menu item and action to test the undoManager,
- (IBAction)stupidUndoRemoveHost:(id)sender
{
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSUndoManager *undoer = [managedObjectContext undoManager];
NSLog(#"canUndo? %hhd", [undoer canUndo]);
NSLog(#"canRedo? %hhd", [undoer canRedo]);
NSLog(#"isUndoRegistrationEnabled? %hhd", [undoer isUndoRegistrationEnabled]);
NSLog(#"undoMenuItemTitle = %#", [undoer undoMenuItemTitle]);
NSLog(#"redoMenuItemTitle = %#", [undoer redoMenuItemTitle]);
[undoer undo];
}
Using this IBAction I can do the undo (well, sort of, it adds the object twice so clearly there's still more wrong here), but I can only do it once. If I delete another object canUndo returns 0, and stupidUndoRemoveHost does nothing.
I know I'm not understanding something here. I've read through more posts here than I can count, several blog posts, and the Apple documentation. I've done this before, but it was like ten years ago, so my skills are a bit rusty. Any help or pointers in the right direction are greatly appreciated.
Update: here is the addBookmarkObject method:
- (void)addBookmarkObject: (NSManagedObject *)object
{
[bookmarksController addObject:object];
}
And here is windowWillReturnUndoManager from the AppDelegate:
- (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window {
// Returns the NSUndoManager for the application. In this case, the manager returned is that of the managed object context for the application.
NSUndoManager *undoManager = [[NSUndoManager alloc] init];
self.persistentContainer.viewContext.undoManager = undoManager;
if (self.persistentContainer.viewContext.undoManager == nil)
{
NSLog(#"No undo manager!");
} else {
NSLog(#"We've got an undo manager!");
}
return self.persistentContainer.viewContext.undoManager;
}
windowWillReturnUndoManager: is called every time Appkit wants to register an undo operation and when it wants to enable/disable the Undo menu item. If windowWillReturnUndoManager: returns a new undo manager then the undo stack is empty and the Undo menu item is disabled.
Core Data will register an undo operation when an object is removed, removeHost: shouldn't register an extra undo operation.
- (IBAction)removeHost:(id)sender
{
[bookmarksController remove:sender];
[undoManager setActionName:#"Bookmark Delete"];
}
The Xcode macOS Cocoa App with Core Data template has some flaws.
NSWindowDelegate method windowWillReturnUndoManager: isn't called because in the xib, the delegate of the window isn't connected to the app delegate. Fix: connect the delegate of the window to the Delegate.
self.persistentContainer.viewContext.undoManager is nil. Fix: create the undo manager once when the persistent container is created.
- (NSPersistentContainer *)persistentContainer {
// The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
#synchronized (self) {
if (_persistentContainer == nil) {
_persistentContainer = [[NSPersistentContainer alloc] initWithName:#"TestCDUndo"];
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
if (error != nil) {
…
abort();
}
self->_persistentContainer.viewContext.undoManager = [[NSUndoManager alloc] init];
}];
}
}
return _persistentContainer;
}

NSOpenPanel and detected leaked observer

I received a warning from XCode during the execution of my program :
2016-01-21 03:19:26.468 IsoMetadonnees[1975:303] An instance 0x1004eefd0 of class NSVBOpenPanel was deallocated while key value observers were still registered with it. Observation info was leaked, and may even become mistakenly attached to some other object. Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger. Here's the current observation info:
<NSKeyValueObservationInfo 0x608000444710> (
<NSKeyValueObservance 0x6080000d5310: Observer: 0x100592cf0, Key path: level, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x6080004486a0>
)
The problem occurs while the application presents an NSOpenPanel to select some files that will be asynchronously loaded. The application does not crash and file are correctly loaded...
I don't create any value observer, so I imagine that the observer is created by NSOpenPanel, but I don't know any procedure to remove observer that I have not created...
Despite of this warning, I have made multiple loads without notice any crash. I use my application since many years without any problems, but I recently switch to ARC; may be the problem appeared (or is detected) at this time.
Here is a simplified version of my code :
- (IBAction)ajoutFichier:(id)sender {
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
// Here some configurations of openPanel
if ([openPanel runModal] == NSOKButton) {
tmp_listeURLFichiers = [openPanel URLs];
}
//[openPanel close]; // I add this code unsuccessfully
openPanel = nil; // I add this code unsuccessfully
// I call a task in back ground to load my files
if ((tmp_listeURLFichiers != nil) && ([tmp_listeURLFichiers count]>0))
[self performSelectorInBackground:#selector(ajouteListeFichiers:) withObject:tmp_listeURLFichiers];
}
// Load files in background
-(BOOL) ajouteListeFichiers:(NSArray *)listeDesFichierAAjouter {
#autoreleasepool {
// Some stuff to show a progress bar
// Loop to load selected files
for (id tmpCheminVersMonImage in listeDesFichierAAjouter) {
// Load files
}
} // <========== THE WARNING OCCURS AT THIS POINT, WHEN autoreleasepool is cleaned
return (YES);
}
I try adding
[openPanel close];
and
openPanel = nil;
to force releasing openPanel from memory (and thus observers) before starting background task, but that doesn't change anything...
Do you have any idea ?
Thank you for your help !
I could fix the problem using the following trick :
I declare a variable in my view controller :
__strong NSOpenPanel *prgOpenPanel;
Then I use it in my code
//NSOpenPanel *prgOpenPanel = [NSOpenPanel openPanel];
self.prgOpenPanel = nil;
self.prgOpenPanel = [NSOpenPanel openPanel];
// Here some configurations of openPanel
if ([prgOpenPanel runModal] == NSOKButton) {
tmp_listeURLFichiers = [prgOpenPanel URLs];
if ((tmp_listeURLFichiers != nil) && ([tmp_listeURLFichiers count]>0))
[self performSelectorInBackground:#selector(ajouteListeFichiers:) withObject:tmp_listeURLFichiers];
}
No more warnings !

Dealing with background location updates and Core Data file protection

I've been experimenting with CLLocationManager's startMonitoringSignificantLocationChanges and I've run into some problems with Core Data. It turns out that since iOS 5.0, Core Data defaults to using NSFileProtectionCompleteUntilFirstUserAuthentication. This means that if a passcode is set, the persistent store is unavailable from the time the device is turned on until the time the passcode is first entered. If you're using location updates, it's possible your app may get launched during that time, and Core Data will get an error trying to load the persistent store.
Obviously switching to NSFileProtectionNone would be the easiest way to solve this. I'd prefer not to though—I'm not storing anything super sensitive in the database, but these location updates aren't super critical either.
I know I can use [[UIApplication sharedApplication] isProtectedDataAvailable] to check whether the data has been unlocked yet, and I can use applicationProtectedDataWillBecomeUnavailable: in my application delegate to respond appropriately once it is unlocked. This seems messy to me though—I'll have to add in a bunch of extra checks to make sure nothing goes wrong if the persistent store is unavailable, re-setup a bunch of things once it does become available, and so on. And that extra code doesn't offer much benefit—the app still won't be able to do anything if it launches in this state.
So I guess I'm just not sure which is the more "proper" way to deal this:
Switch to NSFileProtectionNone.
Add in the extra checks to skip over things if the store is unavailable, and use applicationProtectedDataWillBecomeUnavailable: to set things up again once it is.
If the app is launched in the background ([[UIApplication sharedApplication] applicationState] == UIApplicationStateBackground) and protected data is unavailable ([[UIApplication sharedApplication] isProtectedDataAvailable] == NO)) just call exit(0) (or something similar) to quit the app. On one hand this seems like the simplest solution, and I don't really see any downsides. But it also seems… "wrong"? I guess I can't decide if it's a clean solution or just a lazy one.
Something else I'm just not thinking of?
After thinking this over for a while I've come up with a solution I'm happy with. One thing to consider with the exit(0) option is that if the user takes a while to unlock the device, the app could be continually loading, quitting, and reloading. Whereas if you simply prevent the app from doing much, it will probably only have to load once, and will most likely be more efficient. So I decided to try my option 3 and see how messy it really was. It turned out to be simpler than I thought.
First I added a BOOL setupComplete property to my app delegate. This gives me an easy way to check if the app was fully launched at various points. Then in application:didFinishLaunchingWithOptions: I attempt to initialize the managed object context, then do something like this:
NSManagedObjectContext *moc = [self managedObjectContext];
if (moc) {
self.setupComplete = YES;
[self setupWithManagedObjectContext:moc];
} else {
UIApplication *app = [UIApplication sharedApplication];
if ([app applicationState] == UIApplicationStateBackground && ![app isProtectedDataAvailable]) {
[app beginIgnoringInteractionEvents];
} else [self presentErrorWithTitle:#"There was an error opening the database."];
}
setupWithManagedObjectContext: is just a custom method that finishes setting up. I'm not sure the beginIgnoringInteractionEvents is necessary, but I added it to be on the safe side. That way when the app is brought to the front, I can be sure the interface is frozen until setup is complete. It might avoid a crash if an eager user is tapping anxiously.
Then in applicationProtectedDataDidBecomeAvailable: I call something like this:
if (!self.setupComplete) {
NSManagedObjectContext *moc = [self managedObjectContext];
if (moc) {
self.setupComplete = YES;
[self setupWithManagedObjectContext:moc];
UIApplication *app = [UIApplication sharedApplication];
if ([app isIgnoringInteractionEvents]) [app endIgnoringInteractionEvents];
} else [self presentErrorWithTitle:#"There was an error opening the database."];
}
That finishes the setup and re-enables the interface. That's most of the work, but you'll also need to check through your other code to make sure nothing that relies on Core Data is getting called before your persistent store is available. One thing to watch out for is that applicationWillEnterForeground and applicationDidBecomeActive may get called before applicationProtectedDataDidBecomeAvailable if the user launches the app from this background state. So in various places I've added if (self.setupComplete) { … } to make sure nothing runs before it's ready. I also had a couple of places where I needed to refresh the interface once the database was loaded.
In order to (partially) test this without a lot of driving around, I temporarily modified application:didFinishLaunchingWithOptions: to not set up the database:
NSManagedObjectContext *moc = nil; // [self managedObjectContext];
if (moc) {
self.setupComplete = YES;
[self setupWithManagedObjectContext:moc];
} else {
UIApplication *app = [UIApplication sharedApplication];
// if ([app applicationState] == UIApplicationStateBackground && ![app isProtectedDataAvailable]) {
[app beginIgnoringInteractionEvents];
// } else [self presentErrorWithTitle:#"There was an error opening the database."];
}
Then I moved my code in applicationProtectedDataDidBecomeAvailable: over to applicationWillEnterForeground:. That way I could launch the app, make sure nothing unexpected happens, press the home button, open the app again, and make sure everything was working. Since the actual code requires moving a significant distance and waiting five minutes each time, this gave me a good way to approximate what was happening.
One last thing that tripped me up was my persistent store coordinator. A typical implementation might look something like this:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (_persistentStoreCoordinator != nil) return _persistentStoreCoordinator;
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"Test.sqlite"];
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
return _persistentStoreCoordinator;
}
This is loosely based on Apple's sample code, which does explain in comments that you need to handle the error appropriately. My own code does a bit more than this, but one thing I hadn't considered is that if there's an error loading the persistent store, this will return a non-nil result! That was allowing all my other code to proceed as though it was working correctly. And even if persistentStoreCoordinator was called again, it would just return the same coordinator, without a valid store, instead of trying to load the store again. There are various ways you could deal with this, but to me it seemed best to not set _persistentStoreCoordinator unless it was able to add the store:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (_persistentStoreCoordinator != nil) return _persistentStoreCoordinator;
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"Test.sqlite"];
NSError *error = nil;
NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if ([coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
_persistentStoreCoordinator = coordinator;
} else {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
return _persistentStoreCoordinator;
}
I have experienced, that you have to check
[[UIApplication sharedApplication] isProtectedDataAvailable]
and process
applicationProtectedDataWillBecomeUnavailable
to be sure you don't access a protected file.
Checking for
managedObjectContext
did not work for me.

Calling Obj-C Code from JavaScript via Console: Arguments get dropped?

Having a heck of a time with this one.
I've got a super-simple Cocoa app containing one WebView, a WebScripting API defined in the page, and a single NSObject defined on that API. When I turn on the debugger tools (in the embedded WebView), I can see the API on the JavaScript window object, and I can see my "api" property defined on that -- but when I call the API's "get" method, the arguments aren't being serialized -- when the Obj-C method gets called, the arguments are missing. See below, which hopefully illustrates:
I've combed through the docs, I've (apparently) set the appropriate methods to expose everything that needs to be exposed, and I can see the method being called. There has to be something stupid I'm missing, but as a relative newbie to this environment, I'm not seeing it.
Thanks in advance for your help!
Have you set WebKitDeveloperExtras to YES in your default user defaults when you send -[NSUserDefaults registerDefaults:]?
Depending on what version of Xcode you're using you could be getting a known error. If you're using LLDB on anything but the most recent version, it might not be giving you the right variables in the debugger. The solution has been to use GDB instead of LLDB until Apple fixes the problem. But I think they fixed the problem in the latest version. I'd change the debugger to use GDB and see if you're getting the right variables in Xcode. (Product-> Edit Scheme...-> Run -> Debugger). I came across this problem in iOS, though, so I don't know its applicability to OSX. Worth a try anyway.
I originally came across the problem here: https://stackoverflow.com/a/9485349/1147934
I process javascript in the main thread of my app from a local file stored in the apps directory. I check for beginning and ending tokens for the js functions I am executing and whether the function contains a variable.
Hopefully this can give you some good ideas for your issue. You could also do alerts in the js to see if the values post correctly as you run the app (I am sure you thought of that already, but it's worth mentioning.) Happy coding! I hope this helps!
in the .h file define:
NSMutableString *processedCommand;
NSArray *commandArguments;
In the .m file:
// tokens
#define kOpenToken #"<%%"
#define kCloseToken #"%%>"
// this will throw
-(void)executeJScriptCommand:(NSString *)aCommand {
[self performSelectorOnMainThread:#selector(executeThisCommand:) withObject:aCommand waitUntilDone:YES];
}
// this will throw
-(NSString *)executeCommand:(NSString *)command {
NSString *aCommand = [[[command stringByReplacingOccurrencesOfString:kOpenToken withString:#""]
stringByReplacingOccurrencesOfString:kCloseToken withString:#""]
stringByTrimmingLeadingAndTrailingWhitespaces];
if ([aCommand hasPrefix:#"="])
{
// variable. get value
[self getVariableFromCommand:aCommand];
}
else {
[self executeThisCommand:aCommand];
}
NSString *returnValue = [NSString stringWithString:processedCommand];
self.processedCommand = nil;
self.commandArguments = nil;
return returnValue;
}
-(void)executeThisCommand:(NSString *)aCommand {
BOOL hasError = NO;
// clear result
self.processedCommand = nil;
self.commandArguments = nil;
BOOL isFromJS = NO;
NSString *function = nil;
NSMutableArray *commandParts = nil;
#try {
// first, break the command into its parts and extract the function that needs to be called, and the (optional) arguments
commandParts = [[NSMutableArray alloc] initWithArray:[aCommand componentsSeparatedByString:#":"]];
if ([[[commandParts objectAtIndex:0] lowercaseString] isEqualToString:#"js-call"]) {
isFromJS = YES;
[commandParts removeObjectAtIndex:0];
}
// get our function, arguments
function = [[commandParts objectAtIndex:0] retain];
[commandParts removeObjectAtIndex:0];
if ([commandParts count] > 0){
if (isFromJS == YES) {
NSString *arguments = [[commandParts objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
if ([arguments length] > 0) {
self.commandArguments = [arguments JSONValue];
}
}
else {
self.commandArguments = [NSArray arrayWithArray:commandParts];
}
}
// build invoke
SEL sel = NSSelectorFromString(function);
if ([self respondsToSelector:sel]) {
[self performSelectorOnMainThread:sel withObject:nil waitUntilDone:YES];
// using invocation causes a SIGABORT because the try/catch block was not catching the exception.
// using perform selector fixed the problem (i.e., the try/catch block now correctly catches the exception, as expected)
}
else {
[appDelegate buildNewExceptionWithName:#"" andMessage:[NSString stringWithFormat:#"Object does not respond to selector %#", function]];
}
}
#catch (NSException * e) {
hasError = YES;
[self updateErrorMessage:[NSString stringWithFormat:#"Error processing command %#: %#", aCommand, [e reason]]];
}
#finally {
[function release];
[commandParts release];
}
if (hasError == YES) {
[appDelegate buildNewExceptionWithName:#"executeThisCommand" andMessage:self.errorMessage];
}
}
// this can return nil
-(NSString *)getQueryStringValue:(NSString *)name {
NSString *returnValue = nil;
if (queryString != nil) {
returnValue = [queryString objectForKey:[name lowercaseString]];
}
return returnValue;
}

Why does my retained CMAttitude instance become nil after I call stopDeviceMotionUpdates and startDeviceMotionUpdates?

I have a class with an ivar:
#interface myCoolClass:NSObject
{
CMAttitude *referenceAttitude;
}
In my implementation have these selectors:
- (void) startTrackingMotion
{
if (motionManager == nil) {
motionManager = [[CMMotionManager alloc] init];
motionManager.accelerometerUpdateInterval = 0.01;
motionManager.deviceMotionUpdateInterval = 0.01;
referenceAttitude = [motionManager.deviceMotion.attitude retain];
}
[motionManager startDeviceMotionUpdates];
if (referenceAttitude == nil) {
CMDeviceMotion *dm = motionManager.deviceMotion;
referenceAttitude = [dm.attitude retain];
}
}
- (void) stopTrackingMotion
{
[motionManager stopDeviceMotionUpdates];
}
I want to take the referenceAttitude when I init the motion manager and use it over the life of the application. Sometimes, I need to track motion and other times I do not.
Here's the app flow:
call startTrackingMotion since I'm ready for motion
referenceAttitude stays retained and I use it to track motion
I call stopTrackingMotion since I'm going to do non motion stuff
the app do some other stuff
I call startTrackingMotion again since I'm ready for motion again
At this point, as I step through the code, I step over the "if (motionManager == nil)" loop since it's still there. However, everytime it comes to the "if (referenceAttitude == nil)" loop, the if statement resolves to true.
Am I retaining it incorrectly? Does calling stopDeviceMotionUpdates nil out my instance?
Thanks.
I dont think you are giving enough time for the motionManager to update itself.
You should get the referenceAttitude after the first tick of deviceMotionUpdateInterval (0.1).
Calling stopDeviceMotionUpdates doesn't nil out your referenceAttitude variable. What version of XCode are you using, and what kind of device are you running it on?
Have you verified that it is set to a valid CMAttitude instance at this point in code?
referenceAttitude = [motionManager.deviceMotion.attitude retain];
If you execute po [dm attitude] in the debugger within the referenceAttitude == nil block, what do you get?