I'm trying to add an object to an NSMutableArray with code like this:
Item *newItem = [[Item alloc] init];
[self.theArray addObject:newItem];
If I remember correctly, back in the old retain/release days I wouldn't have to worry about the newItem variable going out of scope, because the object would receive a retain when it was added to the array, and so wouldn't be deallocated.
But I'm using ARC now, and the object disappears. The array itself is fine, and the other objects it contained already are not affected. So I suspect that my newItem is being automatically deallocated for some reason.
Can someone give me an idea about what is going on here, and how I could fix it?
Item *newItem = [[Item alloc] init];
// This line is the same as this
//
// __strong Item *newItem = [[Item alloc] init];
//
// the newItem variable has strong reference of the Item object.
// So the reference count of the Item object is 1.
[self.theArray addObject:newItem];
// Now theArray has strong reference of the Item object.
// So the reference count of the Item object is 2.
The reference count of the Item object is 2, thus the Item object will not be released. If your code had a scope like the following,
{
Item *newItem = [[Item alloc] init];
[self.theArray addObject:newItem];
}
It doesn't affect the Item object.
{
Item *newItem = [[Item alloc] init];
[self.theArray addObject:newItem];
// the reference count of the Item object is 2 as I said.
}
// The scope of the newItem variable was ended.
// So the lifetime of the newItem variable was ended,
// then the strong reference by the newItem was gone.
// Thus the reference count of the Item object was reduced from 2 to 1.
The reference count of the Item object is 1, thus the Item object will not be released as well.
I finally found out what was wrong. It had nothing to do with allocations. What was happening was the awakeFromNib method was being called again when a table view was being reloaded. Of course that reset all kinds of things, making stuff disappear.
Related
I am using UIManagedDocument with Parent Child context.
In my child context I do the following
Code 1
NSSet *results = [self.event.memberships filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return ([[evaluatedObject deleted] boolValue] == NO);
}]];
Above code returns the expected results (only Not deleted members for the event).
Code 2
But this code does not. It fetches all records.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"deleted == NO"];
NSSet *results = [self.event.memberships filteredSetUsingPredicate:predicate];
It seems confusing. Both should return same results, but predicateWithBlock returns correct results where as predicateWithFormat returns all records.
What are the pros and cons of using predicateWithBlock instead of predicateWithFormat?
The problem is that you have defined an attribute deleted for your entity. That conflicts with the isDeleted method of NSManagedObject, so you should rename that attribute.
The following "experiment" shows that strange things happen if you call your attribute "deleted" (c is a managed object with a custom deleted attribute):
// Set custom "deleted" property to YES:
c.deleted = #YES;
// Use the property, as your Code 1
NSLog(#"%#", [c deleted]);
// Output: 1
// Use Key-Value Coding, as your Code 2
NSLog(#"%#", [c valueForKey:#"deleted"]);
// Output: 0
// Now really delete the object and try again:
[context deleteObject:c];
NSLog(#"%#", [c valueForKey:#"deleted"]);
// Output: 1
Your "Code 1" refers to the property, therefore it returns the expected result. "Code 2" uses Key-Value Coding, and [c valueForKey:#"deleted"] returns YES if the object
actually has been deleted from the context!
So renaming that attribute should solve your problem. Unfortunately the compiler does not
emit warnings if an attribute name conflicts with a built-in method.
Use the formatting placeholder to replace the bool value:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K == %#",
#"deleted", #(NO)];
Your use of the key path is probably ok, but the right-hand side probably doesn't look like "NO" to the parser.
I have one CoreData record that contains all of the app's settings. When I read that single record (using MagicalRecord), I get an array back. My question is: can I get addressabiltiy to the individual fields in the record without using "[0]" (field index), but rather using [#"shopOpens"]?
I was thinking something like this, but I don't think it's right:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"aMostRecentFlag == 1"]; // find old records
preferenceData = [PreferenceData MR_findAllWithPredicate:predicate inContext:defaultContext]; // source
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *preferencesDict = [[userDefaults dictionaryForKey:#"preferencesDictionary"] mutableCopy]; // target
// start filling the userDefaults from the last Preferences record
/*
Printing description of preferencesDict: {
apptInterval = 15;
colorScheme = Saori;
servicesType = 1;
shopCloses = 2000;
shopOpens = 900;
showServices = 0;
syncToiCloud = 0;
timeFormat = 12;
}
*/
[preferencesDict setObject: preferenceData.colorScheme forKey:#"shopOpens"];
UPDATE
This is how I finally figured it out, for those who have a similar question:
NSPredicate *filter = [NSPredicate predicateWithFormat:#"aMostRecentFlag == 0"]; // find old records
NSFetchRequest *freqest = [PreferenceData MR_requestAllWithPredicate: filter];
[freqest setResultType: NSDictionaryResultType];
NSDictionary *perferenceData = [PreferenceData MR_executeFetchRequest:freqest];
Disclaimer: I've never used magical record, so the very first part is just an educated guess.
I imagine that preferenceData is an instance of NSArray firstly because the method name uses findAll which indicates that it will return multiple instances. Secondly, a normal core data fetch returns an array, and there is no obvious reason for that find method to return anything different. Thirdly, you referenced using an index operation in your question.
So, preferenceData is most likely an array of all objects in the store that match the specified predicate. You indicated that there is only one such object, which means you can just grab the first one.
PreferenceData *preferenceData = [[PreferenceData
MR_findAllWithPredicate:predicate inContext:defaultContext] firstObject];
Now, unless it is nil, you have the object from the core data store.
You should be able to reference it in any way you like to access its attributes.
Note, however, that you can fetch objects from core data as dictionary using NSDictionaryResultType, which may be a better alternative for you.
Also, you can send dictionaryWithValuesForKeys: to a managed object to get a dictionary of specific attributes.
i've a problem with my table!
i use a parsing tableview but when i change view, my table loses data. So i decide to save all data to nsuserdefault; but, here the problem, NSUserDefault warns me:
"Note that dictionaries and arrays in property lists must also contain only property values."
NB: itemsToDisplay is a NSMutableArray and contain title, url, data and summary of parsedItems.
Well, here my code:
self.itemsToDisplay = [[[NSUserDefaults standardUserDefaults] arrayForKey:#"items"] mutableCopy];
if (!self.itemsToDisplay) {
self.itemsToDisplay = [[NSMutableArray alloc] init];
}
self.itemsToDisplay = [[NSMutableArray alloc]init];
self.itemsToDisplay = [parsedItems sortedArrayUsingDescriptors:
[NSArray arrayWithObject:[[[NSSortDescriptor alloc] initWithKey:#"date"
ascending:NO] autorelease]]];
[[NSUserDefaults standardUserDefaults] setObject:self.itemsToDisplay forKey:#"items"];
[[NSUserDefaults standardUserDefaults] synchronize];
I suppose the problem is setObject:self.itemsToDisplay, but i don't know how solve it.
Thank You guys..
First lets mention that the table cannot lose data because it does not hold any user data. The data is either provided through bindings or through delegation see NSTableViewDataSource in Apples documentation).
Second, the first three assignments to self.itemsToDisplay serve no purpose (unless you have side-effects in the setter) because they are all overridden by the last assignment.
Finally, if this code is already in the delegate then the delegate should be instantiated in your NIB file for the data to survive past a view swap. If your delegate is an object that is instantiated with your view it will also die with it along with all of the data and writing to the user-defaults is a bad idea for what you are trying to achieve. Simply set the delegate to an object whose lifetime is greater than that of both views.
self.itemsToDisplay = [parsedItems sortedArrayUsingDescriptors:
[NSArray arrayWithObject:[[[NSSortDescriptor alloc] initWithKey:#"date"
ascending:NO] autorelease]]];
//First lets encode it
NSUserDefaults *userDefault=[NSUserDefaults standardUserDefaults];
NSData *myEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:self.itemsToDisplay];
[userDefault setObject:myEncodedObject forKey:[NSString stringWithFormat:#"sample"]];
I am trying to parse a lot of text files and organize their contents as managed objects. There are a lot of duplicates in the text files, so one of the "collateral" tasks is to get rid of them.
What i am trying to do in this respect is to check whether an entity with the given content exists, and if it doesn't, i create one. However, i have different entities with different attributes and relationships. What i want is a kind of function that would take a number of attributes as an input and return a new NSManagedObject instance, and i wouldn't have to worry if it was inserted into the data store or fetched from it.
Is there one?
I must also say that i am a noob at core data.
Some more detail, if you want:
I am trying to write a sort of dictionary. I have words (Word{NSString *word, <<-> Rule rule}), rules (Rule{NSString name, <->>Word word, <<->PartOfSpeech partOfSpeech, <<-> Ending endings}), parts of speech (PartOfSpeech{NSString name, <<-> Rule rule}) (i hope the notation is clear).
Two words are equal, if they have the same word property, and "linked" to the same rule. Two rules are the same, if they have the same endings and part of speech.
So far i've written a method that takes NSPredicate, NSManagedObjectContext and NSEntityDescription as an input, and first queries the datastore and returns an entity if it finds one, or creates a new one, inserts it into the datastore and returns it. However, in this case I cannot populate the new entity with the necessary data (within that method), so i have to either pass an NSDictionary with the names of attributes and their values and insert them, or return by reference a flag as to whether i created a new object or returned an old one, so that i could populate it with the data outside.
But it looks kind of ugly. I'm sure there must be something more elegant than that, i just couldn't find it. Please, help me if you can.
Your basically on the right path. Core Data is an object graph. There not a lot of dynamic built in. There's also no "upsert". like you surmise, you have to fetch and if it doesn't exist, you insert one.
Here is what I have just started using to handle a fetch-or-create scenario. I am using a top level managed object which contains a few to-many relationships to subordinate objects. I have a class that houses a few arrays of data (those are not shown here). This class is responsible for saving and retrieving to and from core data. When the class is created, I do a fetch-or-create to access my top level NSManagedObject.
#implementation MyDataManagerClass
...
#synthesize MyRootDataMO;
- (MyDataManagerClass *) init {
// Init managed object
NSManagedObjectContext *managedObjectContext = [(MyAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
// Fetch or Create root user data managed object
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"MyRootDataMO" inManagedObjectContext:managedObjectContext];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];
NSError *error = nil;
NSArray *result = [managedObjectContext executeFetchRequest:request error:&error];
if (result == nil) {
NSLog(#"fetch result = nil");
// Handle the error here
} else {
if([result count] > 0) {
NSLog(#"fetch saved MO");
MyRootDataMO = (MyRootDataMO *)[result objectAtIndex:0];
} else {
NSLog(#"create new MO");
MyRootDataMO = (MyRootDataMO *)[NSEntityDescription insertNewObjectForEntityForName:#"MyRootDataMO" inManagedObjectContext:managedObjectContext];
}
}
return self;
}
...
As per the title really. I have an entity which has a property "idNumber". Just as I can bind a text box to the array controller's arrangedObjects with Model Key Path "#count" to provide a count of all the objects in the array, I would like to be able to bind a text field to the array controller's arrangedObjects with a value transformer to return a count of a filtered subset of the array (those objects with an idNumber >5).
I'm assuming this is possible??
My attempt is:
I have bound the text box to the array controller, Controller Key "arrangedObjects" Model Key Path "" Value Transformer "AllToSomeTransformer".
The code for the AllToSomeTransformer is:
-(id)transformedValue:(id)value {
NSArray *arrayOfAllCars;
if (value == nil) return nil;
if ([value respondsToSelector: #selector(count)]) {
arrayOfAllCars = [NSArray arrayWithArray:value];
} else {
[NSException raise: NSInternalInconsistencyException
format: #"Value (%#) does not respond to -count.",
[value class]];
}
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"idNumber > %#", [NSNumber numberWithInt:5]];
NSArray *arrayOfBlueCars = [arrayOfAllCars filteredArrayUsingPredicate:predicate];
return [NSNumber numberWithInt:[arrayOfBlueCars count]];
}
I believe my value transformer is correctly registered etc. By way of trying to figure out what's going on I added some NSLog outputs through to above code. It appears the above method is only called once, on app startup, and not again when new objects are added to the array. Could this be why the text field is not being updated with values??
Thanks, Oli
Since the transformer is called and does work but only once, that suggest there is something wrong with the bindings such that the transformer is not observing the changes in arrangedObjects. I'm not sure what that would be.