Getting various memory leaks while accessing AddressBook in iOS 7

704 views Asked by At

I am using using this function to access the contacts list and saving the contact object using MContact class,but this code is giving me several memory leaks,i am not able to figure this out .Please help!

 +(NSArray *)getAllContacts

{

    CFErrorRef *error = nil;


    ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);

    __block BOOL accessGranted = NO;
    if (ABAddressBookRequestAccessWithCompletion != NULL) { // we're on iOS 6
        dispatch_semaphore_t sema = dispatch_semaphore_create(0);
        ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
            accessGranted = granted;
            dispatch_semaphore_signal(sema);
        });
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);

    }
    else { // we're on iOS 5 or older
        accessGranted = YES;
    }

    if (accessGranted) {

#ifdef DEBUG
        NSLog(@"Fetching contact info ----> ");
#endif


        ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);
        ABRecordRef source = ABAddressBookCopyDefaultSource(addressBook);
        CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(addressBook, source, kABPersonSortByFirstName);
        CFIndex nPeople = ABAddressBookGetPersonCount(addressBook);
        NSMutableArray* items = [NSMutableArray arrayWithCapacity:nPeople];


        for (int i = 0; i < nPeople; i++)
        {
            MContact *contacts = [MContact new];

            ABRecordRef person = CFArrayGetValueAtIndex(allPeople, i);

            //get First Name and Last Name

            contacts.firstName = (__bridge NSString*)ABRecordCopyValue(person, kABPersonFirstNameProperty);

            contacts.lastName =  (__bridge NSString*)ABRecordCopyValue(person, kABPersonLastNameProperty);

            if (!contacts.firstName) {
                contacts.firstName = @"";
            }
            if (!contacts.lastName) {
                contacts.lastName = @"";
            }


            NSMutableArray *contactEmails = [NSMutableArray new];
            ABMultiValueRef multiEmails = ABRecordCopyValue(person, kABPersonEmailProperty);

            for (CFIndex i=0; i<ABMultiValueGetCount(multiEmails); i++) {
                CFStringRef contactEmailRef = ABMultiValueCopyValueAtIndex(multiEmails, i);
                NSString *contactEmail = (__bridge NSString *)contactEmailRef;

                [contactEmails addObject:contactEmail];
                // NSLog(@"All emails are:%@", contactEmails);

            }

            if([contactEmails count]==0){
                return items ;
            }
            else{
                [contacts setemails:contactEmails];
                [items addObject:contacts];

            }




#ifdef DEBUG
    #endif




        }
        CFRelease(addressBook);
        return items;



    } else {
#ifdef DEBUG
        NSLog(@"Cannot fetch Contacts :( ");        
#endif
        return NO;


    }


}
1

There are 1 answers

1
stefandouganhyde On

The thing you need to watch out for here is that, by convention, functions that "copy" return objects with incremented reference counts, just like functions that "create". So, the way you've CFReleased your addressBook, you need to do that with the copied objects too. Alternatively, when you bridge your CFObjects to NSObjects, a __bridge_transfer would pass ownership to ARC, so ARC can release it for you.

The other gotcha here is the multiple return points in the function. If that's what you want to do, you need to make sure that appropriate things are cleaned up before each return.

Hopefully I've caught everything here:

+ (NSArray *)getAllContacts
{
    CFErrorRef *error = nil;

    ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);

    __block BOOL accessGranted = NO;
    if (ABAddressBookRequestAccessWithCompletion != NULL) { // we're on iOS 6
        dispatch_semaphore_t sema = dispatch_semaphore_create(0);
        ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
            accessGranted = granted;
            dispatch_semaphore_signal(sema);
        });
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    }
    else { // we're on iOS 5 or older
        accessGranted = YES;
    }

    if (accessGranted) {

#ifdef DEBUG
        NSLog(@"Fetching contact info ----> ");
#endif


        //ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error); Removing this line as you have this already. If you need to recreate, you must release the first one.
        ABRecordRef source = ABAddressBookCopyDefaultSource(addressBook);
        CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(addressBook, source, kABPersonSortByFirstName);
        CFRelease(source); // Copied object, so release.
        CFIndex nPeople = ABAddressBookGetPersonCount(addressBook);
        NSMutableArray* items = [NSMutableArray arrayWithCapacity:nPeople];

        for (int i = 0; i < nPeople; i++)
        {
            MContact *contacts = [MContact new];

            ABRecordRef person = CFArrayGetValueAtIndex(allPeople, i);

            //get First Name and Last Name

            contacts.firstName = (__bridge_transfer NSString*)ABRecordCopyValue(person, kABPersonFirstNameProperty);

            contacts.lastName =  (__bridge_transfer NSString*)ABRecordCopyValue(person, kABPersonLastNameProperty);

            if (!contacts.firstName) {
                contacts.firstName = @"";
            }
            if (!contacts.lastName) {
                contacts.lastName = @"";
            }

            NSMutableArray *contactEmails = [NSMutableArray new];
            ABMultiValueRef multiEmails = ABRecordCopyValue(person, kABPersonEmailProperty);

            for (CFIndex i=0; i<ABMultiValueGetCount(multiEmails); i++) {
                CFStringRef contactEmailRef = ABMultiValueCopyValueAtIndex(multiEmails, i);
                NSString *contactEmail = (__bridge_transfer NSString *)contactEmailRef;

                [contactEmails addObject:contactEmail];
                // NSLog(@"All emails are:%@", contactEmails);

            }
            CFRelease(multiEmails);                

            if([contactEmails count]==0) {
                break; 
                // If we return here, we'll leak addressBook and allPeople
                // We could release both, then return.
            }
            else {
                [contacts setemails:contactEmails];
                [items addObject:contacts];
            }


#ifdef DEBUG
    #endif

        }
        CFRelease(addressBook);
        CFRelease(allPeople); // Copied, so must release
        return items;



    } else {
#ifdef DEBUG
        NSLog(@"Cannot fetch Contacts :( ");        
#endif
        CFRelease(addressBook);
        return nil;


    }
}