We have address book feature in our application, contacts are stored in NSMutableArray. I
have a separate class which is accessing contacts from outside, so I have initialized like
below in new class...
self.newListdata = [address_book_window listData];
Now my new class is able to access all contact using newListdata, number of contacts also
matching. In one situation if any contacts deleted from address book at run time, new
class newListdata also need to be updated, but it is not updating as I thought. Count is
also not updating. Am I doing anything wrong, Do I need to manually delete the contact in
newListdata also. Why it is not synchronized with address book contact as I am pointing to
address book list data.I have been learning objective C, so if anyone can help it will be
useful. thanks.
compare if newListdata and [address_book_window listData] are the same pointer (the same object).
printf("compare %f and %f", newlistdata, [address_book_window listData])
They should be the same address storage.
Note: since i don't know how you have implemented your code, since listData is encapsulated, address_book_window doesn't guaranty listData will always be at the same address storage (if you use a new list data by example). So newListData will could potentially point to a dangling pointer.
Best way to keep track of an object like this is by observer pattern, or KVO if you can. Since they are long to explain, google it ;)
if you have 2 NSMutableArrays they have strong references to the same objects, but they are 2 unique objects (they are 2 MutableArrays), this is the case when for example you create a NSMutableArray arrayWithArray:
if you have 1 Array, and 2 references to it when you remove or add object to it, it's a single object so it does not matter which reference you use to access it, it will be in "sync" as you mention (saying in sync is not correct because in fact they are 1 single object)
In your case maybe listData return a new array that contains references to the same objects, in such a case when you remove an object from one array, the second will still retain it (the object will not be deallocated then) and the 2 arrays will be different.
Related
I dont know how to declare a array which just stores pointers to objects. As per My understanding ,if I use method
[ someArray addObject:someObject ] ,
It would then add the copy of object to array and any changes to object wont get reflected to original object.
What I want is that create a array of pointers which would just point to objects and changes made to objects would persist. pardon me, If I am missing something basic.
An NSArray or NSMutableArray is an array of pointers. No copying is done.
you have your basics wrong. technically when you do that you create the array of pointers to those objects.
http://developer.apple.com/library/ios/#DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSMutableArray_Class/Reference/Reference.html read the description.
If you want to get the object copied you have to explicitly say so.
Look at this question for example
Deep copying an NSArray
By the way you should use an NSMutableArray.
Also look at the superclass NSArray
http://developer.apple.com/library/ios/#DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html#//apple_ref/occ/cl/NSArray
specifically for the initWithArray:copyItems:
flag
If YES, each object in array receives a copyWithZone: message to
create a copy of the object—objects must conform to the NSCopying
protocol. In a managed memory environment, this is instead of the
retain message the object would otherwise receive. The object copy is
then added to the returned array.
If NO, then in a managed memory environment each object in array simply receives a retain message when it is added to the returned
array.
By default adding an object to a nsmutablearray increases its capacity if necessary, adds a retain for the object, and the pointer to the object.
...if I use method
[ someArray addObject:someObject ] ,
It would then add the copy of object to array and any changes to
object wont get reflected to original object.
While it technically doesn't pertain to the question, I simply must correct your terminology. "Copy" in Objective-C implies that the method -copy is sent to the object, which would create a new object in of itself. What Arrays do is send -retain to their objects, which means that the array itself now owns a stake in the object, which is why changes that don't reference the array (-objectAtIndex:), or have a valid claim to the object itself are not reflected.
What I want is that create a array of pointers which would just point
to objects and changes made to objects would persist. pardon me, If I
am missing something basic.
Well, unfortunately iOS does not support the class NSPointerArray, which would make your life significantly easy in regards to an actual array of pointers. Without getting into any C-craziness, I can only reiterate what I mentioned above: If you need to mutate an object in an array, just access it with a valid reference to it, or use -objectAtIndex. So long as you still have a valid claim on the object (a reference in this case, it's pointer didn't change because it was sent -retain) you can change it. Note the simple example below:
NSMutableString *str = [[NSMutableString alloc]initWithString:#"Hello"];
NSArray *arr = [[NSArray alloc]initWithObjects:str, nil];
NSLog(#"%#",arr);
[str appendString:#" Friend!"];
NSLog(#"%#",arr);
This prints:
2012-08-07 21:37:46.368 .MyApp[2325:303] (
Hello
)
2012-08-07 21:37:46.369 .MyApp[2325:303] (
"Hello Friend!"
)
Simple!
I have three classes, A, B and C. A is the main class.
When the user wants to see the list of all objects that were purchased, Class B is called from A and shows the list of objects in a core data entity.
Inside class B, the user can buy new objects (in-app purchase). When the user wants to buy another object, class C is called.
When class C is called, a new object is created on the core data entity using
anObject = [NSEntityDescription insertNewObjectForEntityForName:#"Objects" inManagedObjectContext:context];
this object is then assigned to a local reference on Class C, using something like
self.object = anObject;
this object variable was declared like this:
.h
MyObjects *object;
#property (nonatomic, retain) MyObjects *object;
and #synthesized on .m
MyObjects is a core data class representing the entity.
In theory, object will retain anything assigned to it, so the line self.object = anObject I typed previously will retain anObject reference on self.object, right?
The problem is that when I try to access self.object in the same class after buying the new object, I receive an error "CoreData could not fulfill a fault for XXX", where XXX is exactly self.object.
At no point in the code there's any object removal from the database. The only operation to the database I could identify was a saving operation done by another class moments before the crash. The save is done by something like
if (![self.managedObjectContext save:&error]) ...
Is there any relation? what may be causing that?
CoreData manages the lifetime of managed objects and you should not retain and release them. If you want to keep a reference to the object so that it can be retrieved later then you have to store the object's id (obtained using -[NSManagedObject objectID]). Then use that to retrieve the object later using -[NSManagedObjectContext objectWithID:].
Make sure you understand about CoreData faulting. Read the documentation.
I had a similar issue a few days ago (using NSFetchedResultsController) where I was placing my fetchedObjects into an array and gathering attributes to populate tables from the array objects. It seems that if the objects in the array are faulted, you cannot unfault it unless you are acting on the direct object. In my case, I solved the issue by taking the lines of code in question and calling [[_fetchedResultsController objectAtIndexPath:indexPath] someAttribute]. I would assume that doing something similar would fix your problem as well. It seems a bit tedious to need to fetch from the managedObjectContext to obtain a faulted value, but this was the only way I could personally get past the issue.
Core Data is responsible for managing the lifetime of managed objects in memory. It's really important to understand Managed Object Contexts - Read the documentation.
Apple also provides an entire troubleshooting section here, and it contains among other things the causes for your error. But it's really only useful if you understand how core data works.
Most likely error is that the object you are saving does not belong to the managed object context.
Say you use the same object on different threads and those different threads use different managed object context, then this will happen.
I've created a program that uses core data and it works beautifully.
I've since attempted to move all my core data methods calls and fetch routines into a class that is self contained. My main program then instantiates that class and makes some basic method calls into that class, and the class then does all the core data stuff behind the scenes. What I'm running into, is that sometimes I'll find that when I grab a managed object from the context, I'll have a valid object, but its properties have been deallocated, and I'll cause a crash. I've played with the zombies and looked for memory leaks, and what I have gathered is it seems that the run loop is probably responsible for deallocating the memory, but I'm not sure.
Is there a way to determine if that memory has been deallocated and force the core data to get it back if I need to access it? My managedObjectContext never gets deallocated, and the fetchedResultsController never does, either.
I thought maybe I needed to use the [managedObjectContext refreshObject:mergeData:] method, or the [managedObjectContext setRetainsRegisteredObjects:] method. Although, I'm under the impression that last one may not be the best bet since it will be more memory intensive (from what I understand).
These errors only popped up when I moved the core data calls into another class file, and they are random when they show up.
Any insight would be appreciated.
-Ryan
Sounds to me like you are not retaining objects you want to keep hanging around. If you are doing something like this:
NSArray *array = [moc executeFetchRequest:request error:&error];
you do not own the returned array and it will most likely disappear when the current autorelease pool is drained. This will occur when the run loop finishes processing the current event.
All this is speculation. If you want a proper answer, you need to post your code.
It's hard to know what the problem is based on your description, but you might want to look at the Core Data memory management guide. You shouldn't have to worry about memory management for managed objects and their entities (they're fetched and faulted automatically). When you talk about "properties," do you mean custom properties backed by ivars? If so, these should be released in didTurnIntoFault and allocd as needed (probably in the accessor).
I was struggling with a similar issue. I'm using a managed object class and want to set its properties dependent on user input. But the sometimes the properties and sometimes the whole managed object were deallocated.
After reading the Apple documentation http://developer.apple.com/library/IOs/#documentation/Cocoa/Conceptual/CoreData/Articles/cdMemory.html the chapter "The Role of the Managed Object Context" I learned that managed objects are released each run loop completes.
And there is the golden advice to set
[myMangedObjectContext setRetainsRegisteredObjects:YES];
(I had to set it in the init method (initWithNibName for me) of my view controller.)
You should also regard to retain only the objects you need to as explained in the documentation. But read it yourself.
If I'm not right please correct me.
I also made a class that handles all my CoreData fetching and stuff. I ran into a couple of gotcha's, so here are some tips. (If I am making any memory management errors in these examples, please let me know.)
Two things:
1) Made a "fetchFiredObject" method in the CoreData handler class. So when I want to get a managedObject that has all its variables and is a "fully feathered bird" so to speak, instead of doing:
aManagedObject *myManagedObject = [myCoreDataHandler.managedObjectStorageArray objectAtIndex:1];
int x = myManagedObject.someVariable.intValue;
instead I do:
aManagedObject *myManagedObject = [myCoreDataHandler fetchFiredObjectAtIndex:1];
int x = myManagedObject.someVariable.intValue;
And in myCoreDataHandler's fetchFiredObjectAtIndex:i method, we're going into the array, finding the object key at index i, then doing a fetchRequest for that object key, and returning the freshly-fetched managedObject so that it won't have been faulted or deallocated, etc. :D
2) When I create a new child viewController, I populate its "myCoreDataHandler" value from the parent upon creation. However, this happens on a subsequent line of code after the line of code that creates the new viewController. Therefore, any code in the child's viewDidLoad that tries to use myCoreDataHandler's methods will return empty objects because viewDidLoad completes before the parent's next line of code where it sets the values of globals in the child object. So make sure you are not accessing your "Core Data handling object" from within viewDidLoad or anything local methods called by viewDidLoad! Instead call them from the parent after creating the new viewController.
I have a general question about objects, memory, and retaining.
I am not a complete newb (I have apps for sale on the iTunes Store) but something must have slipped past me. I work with the Google Docs API, and yesterday I was downloading a spreadsheet feed, and enumerating the results, namely, the Spreadsheet Record Feed.
After enumerating and adding objects to a dictionary, the dictionary got added to an array before the loop went to the next Record. So after 5 times through, the array had 5 objects, one dictionary per record, with values from each. Weirdly, at the end, the Array had 5 copies of the same information. Each time through the loop, the dictionary changed, like it was supposed to, but when I inserted it into the array, the other objects in the array changed to match.
I looked through some stuff on StackOverflow and found a suggestion to try this:
[array insertObject:[dictionary copy] atIndex:0];
That fixed it. Adding the copy method made everything work like normal.
I'm just wondering why.
Usually, when an object is put into an array, it's kept intact even if the original object is modified or destroyed.
You are mistaken. Arrays (and all other container classes in Cocoa) never work like that. They only store a reference to the objects they contain (and retain them) so any changes in the original objects will be reflected if you retrieve them from the array (because they are the same objects).
Sure, by calling copy you are creating a copy of the dictionary so now you are dealing with separate objects. At the same time, you are now leaking memory because you are responsible for releasing an object that is returned from copy.
Also, bear in mind that copy only makes a shallow copy, so the actual content of the copied dictionaries is not being copied. If you change the dictionaries' contents, these changes will be reflected in both dictionaries (the "original" one and the copied one you have added to the array).
When you insert an object (let's call it X) into an array, what actually gets placed into the array is a copy of the pointer to X, and not a copy of X itself. X is sent a retain message so that the array can hold on to it, but X is not sent a copy message. This means that changes to X later on will affect the object "stored" in the array.
WARNING: The suggested solution results in a substantial memory leak, since the copied data is never released. A better solution is to autorelease the copied array, so that it will be released when the array is released.
[array insertObject:[[dictionary copy] autorelease] atIndex:0];
Or, for iPhone code (in which autorelease can be bad, especially in loops):
NSDictionary * newDict = [dictionary copy];
[array insertObject:newDict atIndex:0];
[newDict release];
UPDATE: Be sure to read Ole Begemann's answer as well. He makes an excellent point about deep vs. shallow copies of the dictionary.
I'm trying to use ABRecordRef within an NSMutableArray, but it doesn't seem to work. I know that ABRecord is a C class, but I thought that ABRecordRef was the work around Objective-C class that allowed me to use it with NSObjects. What do I need to do to make this work?
What do you mean by "Not Working"? As in, you get compile or run-time errors?
As I noted in the response to the other poster, you can't use the Objective-C API on the iPhone (There also is no true ABrecord class to brdge to).
Generally it's a really good idea with the address book stuff on the iPhone to copy out elements you are interested in, and save the copied values off in something like a dictionary. If you need to save all the elements, you have to have code that reads every value as defined in the AddressBook.h header file, there's no API way to generically walk the records.
Also remember that at any time, the user might change the address book if they quit your app and come back - so be careful about what you change after they relaunch the app if you are storing values!!