1

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:

Francis F
  • 3,157
  • 3
  • 41
  • 79
  • 1
    Extremely suspiciously similar to this question: http://stackoverflow.com/questions/18010276/potential-memory-leak-abaddressbookcopyarrayofallpeople – borrrden Aug 02 '13 at 09:42
  • When you said “allocated object is not retained lated [sic] in this execution path”, did you mean “referenced” or perhaps “released”? There's no reason for it to suggest retaining it if you already have +1 on it. (More generally, please copy and paste warnings from the Issues Navigator instead of trying to retype them.) – Peter Hosey Aug 03 '13 at 04:35
  • “If I use this format my memory leak issue solves here but I get error in my program” What error? – Peter Hosey Aug 03 '13 at 04:36

2 Answers2

1

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?

Community
  • 1
  • 1
Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
  • I was able to solve the issue by using _addressbook instead of self.addressBook. All issues went away in xcode 4.6 but _addressbook doesnt seems to work on 4.2 but bridge release seems to work on that version. And for your other question, yes I have already done the same thing using ABPeoplePickerNavigationController and it worked well, I just wanted to try this out in another method using CFArray. Thanks for your support :) – Francis F Aug 05 '13 at 05:27
0

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

Stavash
  • 14,244
  • 5
  • 52
  • 80
  • [addContactButton release] is not required because its ARC and even if I try to add it, ARC forbids it. And if CFRelease is added I get message that property returns core foundation object with a +0 retain count. I have tried assigning value of ABAddressBookCopyArrayOfAllPeople to another variable and the warning goes away but I get a runtime error :( – Francis F Aug 02 '13 at 10:05
  • Sorry, you didn't mention ARC – Stavash Aug 02 '13 at 11:22
  • self.contactAdd=(__bridge_retained CFArrayRef) CFBridgingRelease(contactAddtemp); I was able to solve the 1st warning with help of another variable along with this bridgerelease function but the 2nd warning still doesnt go away. – Francis F Aug 02 '13 at 12:04
  • @Gamerlegend: Those two things cancel each other out. It is exactly equivalent to saying `self.contactAdd = contactAddTemp;`. – Peter Hosey Aug 03 '13 at 04:33