Reputation: 3285
I am trying to do a program on contacts APP using address book and it works fine but when I analyze there are several memory leaks I have managed to minimize the memory leaks and now I am down to 2 main memory leak warning, One in my Address book reload function, I have included comments to show what all things I have tried
-(void)reloadAddressBook
{
//if(self.addressBook)
//CFRelease(self.addressBook);
self.addressBook = (__bridge ABAddressBookRef) CFBridgingRelease(ABAddressBookCreate());
if(ABAddressBookHasUnsavedChanges(self.addressBook))
{
ABAddressBookSave(self.addressBook,NULL);
}
//if(self.contactAdd)
//CFRelease(self.contactAdd);
self.contactAdd= ABAddressBookCopyArrayOfAllPeople(self.addressBook);
**//Memory warning here and says: call to function ABAddressBookCopyArrayOfAllPeople returns a core foundation object with a +1 retain count**
//self.contactAdd= (__bridge ABAddressBookRef) CFBridgingRelease(ABAddressBookCopyArrayOfAllPeople(self.addressBook));
**// If I use this format my memory leak issue solves here but I get error in my program**
}
- (void)viewDidLoad
{**//Memory warning here and says :object leaked :allocated object is not retained lated in this execution path and has retain count +1**
[super viewDidLoad];
self.contactSearchBar.delegate=self;
self.contactTableView.delegate=self;
self.contactTableView.dataSource=self;
UIBarButtonItem *addContactButton=[[UIBarButtonItem alloc]initWithTitle:@"Add" style:UIBarButtonItemStyleBordered target:self action:@selector(newContact:)];
self.navigationItem.rightBarButtonItem=addContactButton;
self.navigationItem.title=@"My Contacts";
}
Another memory leak is in this search bar function
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
if(searchText.length==0)
{
isFiltered=NO;
}
else
{
isFiltered=YES;
int j=0,i=0;
self.filteredData= CFArrayCreateMutable(kCFAllocatorDefault, 0,&kCFTypeArrayCallBacks);**// Memory warning here and says: call to function CFArrayCreateMutable returns a core foundation object with a +1 retain count**
for(i=0;i<CFArrayGetCount(self.contactAdd);i++)**//Memory warning here and says :object leaked :allocated object is not retained lated in this execution path and has retain count +1**
{
self.person=CFArrayGetValueAtIndex(self.contactAdd,i);
NSString *str=[[NSString stringWithFormat:@"%@", (__bridge_transfer NSString *)ABRecordCopyValue(self.person, kABPersonFirstNameProperty)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSRange contactRange= [str rangeOfString: searchText options:NSCaseInsensitiveSearch];
NSLog(@"i=%d, j=%d",i,j);
if(contactRange.location!=NSNotFound)
{
CFArrayInsertValueAtIndex(self.filteredData,j++,self.person);
CFArrayGetValueAtIndex(self.filteredData,j-1);
}
}
//CFRelease(self.contactAdd);
//CFRelease(self.filteredData);
}
Memory leak show on for loop statement and it says:
Upvotes: 1
Views: 1625
Reputation: 96333
Like I mentioned in my answer on your other question, in Core Foundation, Create and Copy functions return an ownership reference.
Properties that hold objects are generally also owning references, unless you declare them otherwise (usually with weak
). This means that, in a statement like this:
self.filteredData= CFArrayCreateMutable(…);
you now own the object twice: once because you Created it, and once because your property retained it.
You generally should only own each object once per property, ivar, or other strong reference (such as local variables). Any extra ownerships (such as from Create and Copy functions) are things you need to clean up, which is why it's best to do as little in CF-land as possible: ARC cleans things up for you, but it doesn't touch CF stuff unless you tell it to.
Speaking of properties and local variables, you don't need to make everything a property. person
in the search code should be a local variable within that method, since that's part of the state of that search, rather than something that your object needs to keep around indefinitely. You already have str
as a local variable, so I'm not sure why you used a property for person
.
You tell ARC to interact with the CF world through bridge casts:
__bridge_transfer
(or CFBridgingRelease
) tells ARC “clean up this ownership for me when I'm done with it”.__bridge_retained
(or CFBridgingRetain
) tells ARC “I'm going to throw this object into CF-land; don't let go of it until I say otherwise” (which you then must do at some point with either a transfer or an immediate CFRelease
).And, again, you generally want to take things out of the CF world and let ARC deal with them as much as possible.
Accordingly, I suggest making self.filteredData
an NSMutableArray, rather than a CFMutableArray. Creating the array using Foundation means it will already be under ARC's control, with no need to bridge-cast it.
For self.contactAdd
, you could bridge-cast it to NSArray when you retrieve it, and then treat it as an NSArray from then on. This includes using fast enumeration rather than indexing to iterate through it, or, better yet, using a predicate search instead of iterating through it yourself. (I wouldn't count on a built-in comparison predicate being able to work with ABPersons, but that's what predicateWithBlock:
is for.)
A predicate-based version of your search code would look something like this (extremely untested):
self.filteredData = [self.contactAdd filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings){
ABPersonRef person = (__bridge ABPersonRef)evaluatedObject;
NSString *firstName = [[NSString stringWithFormat:@"%@", (__bridge_transfer NSString *)ABRecordCopyValue(self.person, kABPersonFirstNameProperty)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSRange contactRange = [str rangeOfString:searchText options:NSCaseInsensitiveSearch];
return (contactRange.location != NSNotFound);
}];
(The block, and the predicate based upon it, returns whether or not each object matches. filteredArrayUsingPredicate:
creates an array containing every object for which the predicate evaluated to true
.)
One more thing: Have you considered using ABPeoplePickerNavigationController?
Upvotes: 1
Reputation: 14304
Obviously you're missing a [addContactButton release]
at the end of your viewDidLoad
. Also, why are the CFRelease
calls commented out? They should balance out CFArrayCreateMutable
Regarding self.contactAdd - if it's defined as a "retain" property, don't assign it the return value of ABAddressBookCopyArrayOfAllPeople
directly. Instead, instantiate a local variable, assign it to the property and then release it using CFRelease
Upvotes: 0