//Declarations
var scoreIncrement:Int = 0
var team = ""
var userScore = Dictionary<String,Int>()
//Set Values
team = "USA"
userScore[team] = 0
//Store in NSUserDefaults
NSUserDefaults.standardUserDefaults().setObject(userScore, forKey: "userScore")
NSUserDefaults.standardUserDefaults().synchronize()
//Increment Score
scoreIncrement = 1
userScore[team]! += scoreIncrement
Above code gives 'EXC_BAD_ACCESS(code=EXC_I386_GPFLT)' error on last line of the code. However if I comment out line:
NSUserDefaults.standardUserDefaults().setObject(userScore, forKey: "userScore")
The error goes away. Any idea why this is happening, I am not even retrieving the dictionary from NSUserDefaults yet.
Solved the problem by retrieving the dictionary back out of NSUserDefaults right after saving it with:
var userScoreTemp : AnyObject? = NSUserDefaults.standardUserDefaults().objectForKey("userScore")
if userScoreTemp != nil {
userScore = userScoreTemp! as Dictionary
}
I had this problem as well. It turned out to be a KVO issue. It's possible that you have deallocated an object that was observing this value and so the message was being sent and not being received as it expects to be.
My solution was to remove the observer from the associated deallocated object. Always remember if you addObserver:forKeyPath:options:context: to match it with an associating removeObserver:forKeyPath: call.
I had the same problem.
I stored String value in NSUserDefaults like:
[[NSUserDefaults standardUserDefaults] stValue:#"Rahul" forKey:#"Name"];
After a certain period it crashed at the same point, writing the same value. The cause was that after this period I registered for changes in the NSUserdefaults with code like:
[[NSNotificationCenter defaultCenter] addObserver:self selector: #selector(valueChange) name:NSUserDefaultsDidChangeNotification object:nil];
This was done in an importer at initialization. After the importer was deallocaed, a new write to the user defaults was triggering the observer for an object that was gone. Crash.
The solution was of course to remove the observer .
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults removeObserver:self forKeyPath:#"Name"];
Related
I have the following core-data structure:
I am trying to add Vocabulary objects to the Group class.
My attempts at doing this with the [Group addObject: VocabularyObject] method have
come to no avail.
AppDelegate *delegate = [[AppDelegate alloc]init];
Group *group = [_arr objectAtIndex:indexPath.row]; //I have an array with 'Group' objects
//create vocabulary item
Vocabulary *vocabularyEntity = [NSEntityDescription insertNewObjectForEntityForName:#"Vocabulary" inManagedObjectContext:[delegate managedObjectContext]];
vocabularyEntity.prompt = #"Here is a cool prompt";
vocabularyEntity.definition = #"Here is an even cooler definition";
[delegate saveContext];
[group addTermsObject:vocabularyEntity];
I am getting this error, I used exception breakpoints and the error comes from the addTermsObject call.
[__NSDictionaryI addTermsObject:]: unrecognized selector sent to instance 0x74c4f70
The object I am trying to add is definitely a Vocabulary object, so i'm not exactly sure what the problem could be.
Any ideas?
Thank you!
The error message states that your
Group *group = [_arr objectAtIndex:indexPath.row];
is not a managed object as you expected, but an NSDictionary. Perhaps you fetched the array using
[fetchRequest setResultType:NSDictionaryResultType];
in the fetch request? In that case all the fetched objects are just dictionaries without
any connection to the managed object context, and you can't use these dictionaries to
establish any relationships.
UPDATE: Another error is here:
AppDelegate *delegate = [[AppDelegate alloc]init];
This allocates a new application delegate instead of using the existing one. This is
probably not what you want and you should replace it with
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
I'm implementing support for Lion's "Resume" feature in my OS X app.
I have a custom subclass of NSViewController in which I implemented the method
encodeRestorableStateWithCoder: as:
#implementation MyClass (Restoration)
-(void)encodeRestorableStateWithCoder:(NSCoder*)coder {
[coder encodeObject:_dataMember forKey:#"object_key"]; // I get the warning below when this line is executed for the second time
}
- (void)restoreStateWithCoder:(NSCoder *)coder {
_dataMember = [coder decodeObjectForKey:#"object_key"];
}
#end
However, since I have multiple instances of MyClass, different values are saved into the same key ("object_key") and I get the following warning from Cocoa:
NSKeyedArchiver warning: replacing existing value for key
'object_key'; probable duplication of encoding keys in class hierarchy
What is the best practice to overcome this problem?
Edit: I found here that each instance automatically has its own namespace to avoid collisions, so the problem might be in the way I'm manually calling encodeRestorableStateWithCoder to different instances with the same NSCoder object without telling it that these are different instances. However, I still can't figure out how to do that properly.
Thanks in advance!
To overcome this problem, it is possible to create a new NSMutableData where each of which is written by a separate (new) NSKeyArchiver, and store them all in an array which is stored in the original NSCoder object.
Here is an example for encoding the restorable state of subitems. The decoding part can be straight-forward given this code.
- (void)encodeRestorableStateWithCoder:(NSCoder *)coder
{
[super encodeRestorableStateWithCoder:coder];
// Encode subitems states:
NSArray* subitems = self.items;
NSMutableArray* states = [NSMutableArray arrayWithCapacity: subitems.count];
for (SubItemClass* item in subitems)
{
NSMutableData* state = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:state];
[item encodeRestorableStateWithCoder:archiver];
[archiver finishEncoding];
[states addObject:state];
}
[coder encodeObject:states forKey:#"subitems"];
}
I have:
#interface CMainTableViewController : UITableViewController
In that class I have an NSFetchResultController.
I have the following separate data manager class:
DB_ManagerSingleton
//Here I have a UIManagedDocument that creates the DB/persistent store
To open my persistent store I do the following from my DB_ManagerSingleton class:
UIManagedDocument *fileDB = [[UIManagedDocument alloc]initWithFileURL:url];
if([[NSFileManager defaultManager] fileExistsAtPath:self.fileDB.fileURL.path] != TRUE)
{
[self.fileDB saveToURL:self.fileDB.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success)
{
[self completeDBSetup:success];
}];
}//End if
else if(self.fileDB.documentState == UIDocumentStateClosed)
{
[self.fileDB openWithCompletionHandler:^(BOOL success)
{
[self completeDBSetup:success];
}];
}//End else if
else if(self.fileDB.documentState == UIDocumentStateNormal)
{
[self completeDBSetup:TRUE];
}//End else if
The completeDBSetup places 4 NSManageObjects into the NSManagedObjectContext and notifies the registered observers of the success that the persistent store was opened:
if([CLevelFactory createLevels] == TRUE)
[self.fileDB.managedObjectContext save:&error];
for(id<DB_StateNotification> observer in self.arrNotificationDelegates)
{
if(observer != nil)
[observer DB_StateChange:self.isDBSetup];
}//End for loop
Now, in this case the observer is my TableViewController class. Within this class I have implemented all the methods in the NSFetchedResultsControllerDelegate and the implementation is copied exactly from the Coredata books example.
http://developer.apple.com/library/ios/#samplecode/CoreDataBooks/Listings/Classes_RootViewController_m.html#//apple_ref/doc/uid/DTS40008405-Classes_RootViewController_m-DontLinkElementID_14
The way I set up my tableView controller in the DB_StateChange method from above is as follows:
self.fetchResultController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:dbMgr.fileDB.managedObjectContext
sectionNameKeyPath:#"levelID"
cacheName:nil];
self.fetchResultController.delegate = self;
NSError *error = nil;
self.bQuerierYourData = [self.fetchResultController performFetch:&error];
NSArray *fetchedObjects = [self.fetchResultController fetchedObjects];
//NOTE: I ALWAYS have a count greater than 0. Even on the first run.
if(fetchedObjects.count > 0)
[self.tableView reloadData];
Now the issue is this:
The very first time my application launches I get squat in my table view. I get a fetchedObjects.count of 4 - which is what I expect, but not'a in the table view. Now, without touching anything. Not inserting, updating, or deleting anything in the persistent store, I just quick the application. In this case hitting the stop button on the debugger. When I go ahead and restart the app, everything comes in beautifully. I don't insert data every time the app starts, I test and if the data already exists, I just move on without inserting any data.
Does anyone have any idea what is going on here?
Can someone help me. I have a coredata application but I need to save the objects from a fetchedResultsController into an NSDictionary to be used for sending UILocalNotifications.
Should I use an NSMutableSet, or a NSDictionary, or an array. I'm not used to using collections and I can't figure out the best way to do that.
Could you please give me clues on how to do that please ?
Thanks,
Mike
If I'm reading your question correctly, you're asking how you should pack objects into the userInfo dictionary of a UILocalNotification. Really, it's however works best for you; userInfo dictionaries are created by you and only consumed by you.
I'm not sure why you would be using an NSFetchedResultsController - that class is for managing the marshaling of managed objects between UI classes (like UITableView) efficiently, whereas here it sounds like you would be better off just getting an NSArray of results from your managedObjectContext and the corresponding request, like this:
NSError *error = nil;
NSArray *fetchedObjects = [myManagedObjectContext executeFetchRequest: myRequest error: &error];
if (array == nil)
{
// Deal with error...
}
where you have a pre-existing managed object context and request. You don't need to use an NSFetchedResultsController here.
From there, the simplest suggestion would be to build your userInfo dictionary like this:
NSDictionary* myUserInfo = [NSDictionary dictionaryWithObject: fetchedObjects forKey: #"AnythingYouWant"];
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
// ... do other setup tasks ...
localNotif.userInfo = myUserInfo;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
[localNotif release];
Then when it comes time to receive that notification, you can read that dictionary like this:
- (void)application:(UIApplication *)app didReceiveLocalNotification:(UILocalNotification *)notif
{
NSArray* myFetchedObjects = [notif.userInfo objectForKey: #"AnythingYouWant"];
for(id object in myFetchedObjects)
{
// ... do other stuff ...
}
}
Now, hopefully that's clarified how the userInfo dictionary works. I don't know the details of your app, so it's hard to say, but I'm suspicious that actually passing fetched objects is NOT what you want to do here, mainly because I'm not sure that you have any guarantee that the receiving delegate method will be working with the same object context as the sending method. I would suggest perhaps putting the entity name and predicate in the dictionary and then refetching the objects at receive time with whatever the current MOC is at that moment.
Good luck!
Let's say I have a key #"MyPreference", with a corresponding value stored through NSUserDefaults.
Is there a way to be notified when the value is modified?
Or could it be done through bindings? (But this case, instead of binding the value to a UI element, I wish my object to be notified of the change, so that I can perform other tasks.)
I am aware that NSUserDefaultsDidChangeNotification can be observed, but this appears to be a all-or-nothing approach, and there does not appear to be a mechanism there to get at the specific key-value-pair that was modified. (Feel free to correct.)
Spent all day looking for the answer, only to find it 10 minutes after asking the question...
Came across a solution through Key-Value-Observing:
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self
forKeyPath:#"values.MyPreference"
options:NSKeyValueObservingOptionNew
context:NULL];
Or, more simply (per comment below):
[[NSUserDefaults standardUserDefaults] addObserver:self
forKeyPath:#"MyPreference"
options:NSKeyValueObservingOptionNew
context:NULL];
Swift:
override func viewDidLoad() {
super.viewDidLoad()
NSUserDefaults.standardUserDefaults().addObserver(self, forKeyPath: "THE KEY", options: NSKeyValueObservingOptions.New, context: nil)
}
override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
// your logic
}
deinit {
NSUserDefaults.standardUserDefaults().removeObserver(self, forKeyPath: "THE KEY")
}
And Apple employee advised to use NSUserDefaultsDidChangeNotification notification over here: https://devforums.apple.com/message/237718#237718
I agree with #DenNukem. I was using the NSKeyValueObservingOptionNew. But this function started giving me the BAD_ACCESS Code=1 error wherever I used the NSUserDefault in order to save other objects. In case you are using Key-Value Observer (KVC), just be aware of the Zombie issue on NSUserDefaults.
Here is the link to the solution:
NSUserDefaults and KVO issues