I have the below in one of my functions to copy the contacts on the phone:
ABAddressBookRef addressbook = ABAddressBookCreate();
if(self.contacts != nil)
[contacts release];
self.contacts = (NSArray *)ABAddressBookCopyArrayOfAllPeople(address book);
contacts is an NSArray declared in the class, retained, synthesized et al.
The array is then used in another view controller:
if (!self.contactsViewController) {
self.contactsViewController = [[[contactsViewController alloc] initWithNibName:#"ContactsViewController" bundle:nil] autorelease];
}
self.contactsViewController.contacts = self.contacts;
[self.navigationController pushViewController:self.contactsViewController animated:YES];
I know you are supposed to release the addressbook, but when I do, the contacts that are copied to the viewController only have their names left; all phone numbers and emails disappear when I call them up with ABPersonViewController. It, on the other hand, works fine when I don't release address book, but leaks memory all over the floor. What is going on and how do I fix it?
You are not supposed to release the address book returned by ABAddressBookCreate. Following the conventions, methods that create and return and object should make it autorelease.
However, the line
self.contacts = (NSArray *)ABAddressBookCopyArrayOfAllPeople(address book);
is certainly leaking. Why?, well because ABAddressBookCopyArrayOfAllPeople is creating a new object, that you are retaining again: self.contacts =. So you should either change it for
contacts = (NSArray *)ABAddressBookCopyArrayOfAllPeople(address book);
or use another method that doesn't create a new array of contacts.
Related
Here is code I am referring to.
// Person.h
#interface Person : NSObject {
NSString *firstName;
NSString *lastName;
}
#end
// Person.m
#implementation Person
- (id)init {
if (![super init]) return nil;
firstName = #"John";
lastName = #"Doe";
}
#end
// MyClass.m
#implementation MyClass
.....
- (NSArray *)getPeople {
NSMutableArray *array = [[NSMutableArray alloc] init];
int i;
for (i = 0; i < 10; i++) {
Person *p = [[Person alloc] init];
[array addObject:p];
}
return array;
}
.....
#end
Now, I know there is no memory-management going on in this sample code. What would be required?
In the getPeople loop, I am alloc'ing a Person (retainCount 1), then adding it to array. The retain count is now 2, right? If it is two, should I be [p release]'ing after adding it to the array, bringing the retainCount back down to 1?
Am I right in that it is the caller's responsibility to release the array returned by the method? (Which would also free the memory of the Person's, and their instance variables, assuming their counts are at 1).
I have read Apple's memory management document, but I guess what I am most unclear about, is what increases an objects retain count? I think I grasp the idea of who's responsibility it is to release, though. This is the fundamental rule, according to Apple:
You take ownership of an object if you create it using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc, newObject, or mutableCopy), or if you send it a retain message. You are responsible for relinquishing ownership of objects you own using release or autorelease. Any other time you receive an object, you must not release it.
bobDevil's sentence "only worry about the retain counts you add to the item explicitly" made it click for me. After reading the Ownership policy at Apple, essentially, the object/method that created the new object, is the one responsible for releasing /it's/ interest in it. Is this correct?
Now, let's say I a method, that receives an object, and assigns it to a instance variable. I need to retain the received object correct, as I still have an interest in it?
If any of this is incorrect, let me know.
You are correct that the retain count is 2 after adding it to an array. However, you should only worry about the retain counts you add to the item explicitly.
Retaining an object is a contract that says "I'm not done with you, don't go away." A basic rule of thumb (there are exceptions, but they are usually documented) is that you own the object when you alloc an object, or create a copy. This means you're given the object with a retain count of 1(not autoreleased). In those two cases, you should release it when you are done. Additionally, if you ever explicitly retain an object, you must release it.
So, to be specific to your example, when you create the Person, you have one retain count on it. You add it to an array (which does whatever with it, you don't care) and then you're done with the Person, so you release it:
Person *p = [[Person alloc] init]; //retain 1, for you
[array addObject:p]; //array deals with p however it wants
[p release]; //you're done, so release it
Also, as I said above, you only own the object during alloc or copy generally, so to be consistent with that on the other side of things, you should return the array autoreleased, so that the caller of the getPeople method does not own it.
return [array autorelease];
Edit:
Correct, if you create it, you must release it. If you invest interest in it (through retain) you must release it.
Retain counts are increased when you call alloc specifically, so you'll need to release that explicitly.
factory methods usually give you an autoreleased object (such as [NSMutableArray array] -- you would have to specifically retain this to keep it around for any length of time.).
As far as NSArray and NSMutableArray addObject:, someone else will have to comment. I believe that you treat a classes as black boxes in terms of how they handle their own memory management as a design pattern, so you would never explicitly release something that you have passed into NSArray. When it gets destroyed, its supposed to handle decrementing the retain count itself.
You can also get a somewhat implicit retain if you declare your ivars as properties like #property (retain) suchAndSuchIvar, and use #synthesize in your implementation. Synthesize basically creates setters and getters for you, and if you call out (retain) specifically, the setter is going to retain the object passed in to it. Its not always immediately obvious, because the setters can be structured like this:
Person fart = [[Person alloc] init];
fart.firstName = #"Josh"; // this is actually a setter, not accessing the ivar
// equivalent to [fart setFirstName: #"Josh"], such that
// retainCount++
Edit:
And as far as the memory management, as soon as you add the object to the array, you're done with it... so:
for (i = 0; i < 10; i++) {
Person *p = [[Person alloc] init];
[array addObject:p];
[p release];
}
Josh
You should generally /not/ be worried about the retain count. That's internally implemented. You should only care about whether you want to "own" an object by retaining it. In the code above, the array should own the object, not you (outside of the loop you don't even have reference to it except through the array). Because you own [[Person alloc] init], you then have to release it.
Thus
Person *p = [[Person alloc] init];
[array addObject:p];
[p release];
Also, the caller of "getPeople" should not own the array. This is the convention. You should autorelease it first.
NSMutableArray *array = [[[NSMutableArray alloc] init] autorelease];
You'll want to read Apple's documentation on memory management: http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
Good evening.
I'm developing a QRcode reader and I have one question.
When I read a vcard, I want to show the contact data in a UItableview like the contact's default uitablview in iPhone.
I want to show the contact data as above:
And I want to add the option to save to.
I want to know how can I do it. I have to manually program the view or is there some easier way to do it?
Thanks so much.
CFDataRef vCardData = (CFDataRef)[vCard dataUsingEncoding:NSUTF8StringEncoding];
ABAddressBookRef addressBook = [AddressBook Instance].addressBook;
ABRecordRef defaultSource = ABAddressBookCopyDefaultSource(addressBook);
NSArray *contacts = (NSArray *)ABPersonCreatePeopleInSourceWithVCardRepresentation(defaultSource,vCardData);
CFRelease(defaultSource);
if (contacts.count) {
ABRecordRef person = [contacts objectAtIndex:0];
ABUnknownPersonViewController *unknownPersonVC = [[ABUnknownPersonViewController alloc] init];
unknownPersonVC.unknownPersonViewDelegate = self;
unknownPersonVC.allowsAddingToAddressBook = YES;
unknownPersonVC.displayedPerson = person;
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:unknownPersonVC];
[unknownPersonVC release];
[self.navigationController pushViewController:unknownPersonVC animated:YES];
[navController release];
}
instead of creating custome Contact person View using table View use above code to first get all contact person record Ref and then display them one by one using
ABUnknownPersonViewController or ABPersonViewController..
may be help you..
You need to create the UITableView and implement all of its dataSource and delegate methods to draw the sections and cells. There's no free way to do this automatically, unfortunately.
You should probably check out a table view framework such as the free Sensible TableView. These frameworks automate a lot of these common tasks and will save you a lot of time.
I am firstly new to SO and new to iOS Development. I have completed the CS193P course on iTunes and am ready to tackle my first proper app. I have the prototype set up and just need some reference pointers on where to begin (need that confidence).
The premise of the app is to allow the user to add entries to 1 of 2 available lists; either a giving or a receiving list. The entries will include things like "Name of Event", "Name", "Date" etc. There will also be an option for the user to of course go through the lists and see the entries; I want to allow the user to choose whether they search by name, date or event.
I've got the prototype set up completely and I am just wondering if this kind of application would be somewhat considered similar to an Address Book? The user can add the name of the person (or select the name from contacts), etc.
Apologies for our outstandingly basic question here, but does anyone have any good reference points for essentially, creating a list of entires that get appended to a specific list. I have familiar with Modal View Controllers and the delegates; I'm basically wondering what I should use to "store" the entires the users add. Should I use Core Data or some other technique, etc?
Thanks!
Amit
Yes you have to use Core Data to store the entries. Otherwise whenever the user reopens the app all the entries will be gone.
I've been using NSKeyedArchivers to store lists.
http://developer.apple.com/library/ios/#documentation/cocoa/reference/foundation/Classes/NSKeyedArchiver_Class/Reference/Reference.html
Very easy to manage, and it can store, save, and retrieve all your data easily.
Example would be a list (NSMutableArray) of objects. Each object implements NSCoding and has the initWithCoder: and encodeWithCoder: functions.
e.g. (assuming the objects have a name and date property)
- (id) initWithCoder:(NSCoder *){
self = [super init];
if (self){
[self setName:[aDecoder decodeObjectForKey:#"name"]];
[self setDate:[aDecoder decodeObjectForKey:#"date"]];
}
return self;
}
- (void) encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:name forKey:#"name"];
[aCoder encodeObject:date forKey:#"date"];
}
Then you could simply have your NSMutableArray which you add these objects to, managed by something which has the following functions, and just call saveChanges on it:
- (NSString *) itemArchivePath{
NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentsDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [documentDirectories objectAtIndex:0];
return [documentDirectory stringByAppendingPathComponent:#"myArchiveName.archive"];
}
- (BOOL) saveChanges{
NSString *path = [self itemArchivePath];
return [NSKeyedArchiver archiveRootObject:myList toFile:path];
}
With those two functions implemented, you can just call saveChanges.
And to Retrieve the list later after the next startup, in your manager's init:
- (id) init{
self = [super init];
if (self){
NSString *path = [self itemArchivePath];
myList = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
// If the array hadn't been saved previously, create a new empty one
if (!myList){
myList = [[NSMutableArray Alloc] init];
}
}
}
Your question is very general, so here is my general answer: I would recommend using Core Data to store the lists, and an NSFetchedResultsController to display them. I would also recommend you have a look at the free Sensible TableView framework, which I use regularly to automate tasks that are very similar to the one you have at hand.
I'm pretty sure this is causing a leak and I would like some advice. Here's code based on what I'm doing:
NSMutableArray* straVideoTitles;
- (void) parseData{
//stuff
straVideoTitles = [self getVideoTitle:strData]; //strData contains unparsed data
//more stuff
}
- (NSMutableArray*) getVideoTitles:(NSString*)strData{
NSMutableArray *array;
array = [[NSMutableArray alloc] init];
//Parse data and populate array
return array;
}
Based on the fact that I'm allocating space for NSMutableArray and not releasing it, thats a leak right? How do I tackle this? Should I forgo returning a value and assign straVideoTitles inside getVideoTitles then release like:
- (void) getVideoTitles:(NSString*)strData{
NSMutableArray *array;
array = [[NSMutableArray alloc] init];
//Parse data and populate array
straVideoTitles = array;
[array release];
}
or am i going about this all wrong? Or is everything fine because I'm releasing straVideoTitles in dalloc?
You could change the
return array;
into
return [array autorelease];
Or you could use ARC and just don't care about it anymore.
Edit: The second approach is possible and does not include a memory leak but the code is less capsulated and therefore less reusable and future prove.
Change to
return [array autorelease];
It is good practice to return autorelease objects from methods. This is called a deferred release message.
You are relinquishing ownership while allowing the caller of the method to use the returned array before it is deallocated.
Your return statement should read:
return [array autorelease];
For more information on memory management take a look here:
Advanced Memory Management Programming Guide
I have the following problem: In a certain view controller I have a NSDictionary, which itself is an entree in an NSArray object. This view controller has a child view which displays some of the key value pairs that are in this dictionary. Since I need only some key value pairs, I construct a new dictionary object from which I then remove the key value pair I do not want to have in it. To be able to access this dictionary in the child view, I though it would be possible to just set the dictionary via a property, which seems to work fine. To illustrate with some code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
...
// today is an instance of NSArray holding a number of NSDictionary objects
NSDictionary *completeData = [self.today objectAtIndex:row];
NSDictionary *data = [[NSDictionary alloc] initWithDictionary:completeData];
[data removeObjectForKey:#"name"];
SomeViewController *childController = [[SomeViewController alloc] init];
childController.data = data;
[self.navigationController pushViewController:childController animated:YES];
[childController release];
// This results in a EXC_BAD_ACCESS error when navigating back to the parent
// view and calling didSelectRowAtIndexPath a second time. When commenting this
// line out, the error dissapears, but now the object leaks
[data release];
}
The problem arises when, after returning to the parent view, I try to replace the NSArray object (today) by an updated version of itself by calling
- (void)refreshDataNotification:(NSNotification *)notification {
if (notification) {
self.today = [NSArray arrayWithArray:[[[MyAppDelegate getAppDelegate] todaySchedule]
objectForKey:#"data"]];
[self.tableView reloadData];
}
}
Note that as long as I do not release 'data' in didSelectRowAtIndexPath I get no error, but then the object leaks. When I do release it, I receive an EXC_BAD_ACCESS when refreshDataNotification is executed.
If someone has any clue as to what I might be doing wrong, then please do share with me.
Set the environment variable NSZombieEnabled to YES to get more helpful error messages about over releasing objects. (Set the environment variable by viewing details under 'Executables')
Also, it would be helpful to see how you've defined your properties. (e.g. what is the #property for data in SomeViewController?)
ps - I know you haven't pasted actual code, but data is a terrible instance name for an NSDictionary. dict is better - but something more descriptive would make your code easier to understand.