Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Memory leaks with AddressBook framework

I'm having some memory leaks with both ABAddressBookGetPersonWithRecordID and ABPersonSetImageData. I've been looking for solutions before posting here but I still don't understand. If I play quite a long time with the iPhone 3GS or with only a few contacts with iPhone 3G, actually the application crashes. Here is my code in the didSelectRowAtIndexPath method. I've seen sample codes with these methods and I don't see what I'm missing. Thank you in advance. (sorry for mistakes...)

Contact *myContact = [fetchedResultsController objectAtIndexPath:indexPath];

cancelCreateContact = NO;


ABAddressBookRef ab = ABAddressBookCreate();
int len = ABAddressBookGetPersonCount(ab);
ABRecordID contactID;
ABRecordRef person;
BOOL alreadyExists = NO;
CFStringRef first, last;

for(int i = 1; i < (len + 1); i++)
{
    person = ABAddressBookGetPersonWithRecordID(ab, (ABRecordID)i);

    if(!person){
        len++;
        continue;
    }

    first = ABRecordCopyValue(person, kABPersonFirstNameProperty);
    last = ABRecordCopyValue(person, kABPersonLastNameProperty);

    if ([[(NSString*)first lowercaseString] isEqualToString:[myContact.firstname lowercaseString]] && [[(NSString*)last lowercaseString] isEqualToString:[myContact.lastname lowercaseString]]) {
        alreadyExists = YES;
        contactID = ABRecordGetRecordID(person);
        break;
    }
}

if (alreadyExists) {
    //NSLog(@"already exists");
    ABRecordRef aContactFound = ABAddressBookGetPersonWithRecordID(ab, contactID);

    ABRecordRef aRecord = ABPersonCreate();

    CFErrorRef anError = NULL;

    CFStringRef firstname = ABRecordCopyValue(aContactFound, kABPersonFirstNameProperty);
    ABRecordSetValue(aRecord, kABPersonFirstNameProperty, firstname, &anError);
    CFRelease(firstname);

    CFStringRef lastname = ABRecordCopyValue(aContactFound, kABPersonLastNameProperty);
    ABRecordSetValue(aRecord, kABPersonLastNameProperty, lastname, &anError);
    CFRelease(lastname);

    CFStringRef job = ABRecordCopyValue(aContactFound, kABPersonJobTitleProperty);
    ABRecordSetValue(aRecord, kABPersonJobTitleProperty, job, &anError);
    CFRelease(job);

    ABMultiValueRef instantMessage = ABRecordCopyValue(aContactFound, kABPersonInstantMessageProperty);
    ABRecordSetValue(aRecord, kABPersonInstantMessageProperty, instantMessage, &anError);
    CFRelease(instantMessage);

    ABMultiValueRef phone = ABRecordCopyValue(aContactFound, kABPersonPhoneProperty);
    ABRecordSetValue(aRecord, kABPersonPhoneProperty, phone, &anError);
    CFRelease(phone);

    ABMultiValueRef email = ABRecordCopyValue(aContactFound, kABPersonEmailProperty);
    ABRecordSetValue(aRecord, kABPersonEmailProperty, email, &anError);
    CFRelease(email);

    CFDataRef imageData = ABPersonCopyImageData(aContactFound);
    ABPersonSetImageData(aRecord, imageData, &anError);
    ABAddressBookSave(ab, &anError);
    CFRelease(imageData);

    ABUnknownPersonViewController *ABView = [[ABUnknownPersonViewController alloc] init];
    ABView.unknownPersonViewDelegate = self;
    ABView.displayedPerson = aRecord;
    ABView.allowsAddingToAddressBook = NO;
    ABView.allowsActions = YES;
    ABView.hidesBottomBarWhenPushed = YES;

    [self.navigationController pushViewController:ABView animated:YES];

    [ABView release];

    CFRelease(aRecord);

}else{
    //NSLog(@"doesn't exist");
    //sinon ouvre une fiche pré-remplie

    ABRecordRef aRecord = ABPersonCreate();

    CFErrorRef anError = nil;

    if(![myContact.firstname isEqualToString:@""]) ABRecordSetValue(aRecord, kABPersonFirstNameProperty, myContact.firstname, &anError);

    if(![myContact.lastname isEqualToString:@""]) ABRecordSetValue(aRecord, kABPersonLastNameProperty, myContact.lastname, &anError);

    if(![myContact.email isEqualToString:@""]) {
        ABMultiValueRef ABemail = ABMultiValueCreateMutable(kABMultiStringPropertyType);
        ABMultiValueAddValueAndLabel(ABemail, myContact.email, kABWorkLabel, NULL);
        ABRecordSetValue(aRecord, kABPersonEmailProperty, ABemail, &anError);
        CFRelease(ABemail);
    }

    if(![myContact.phone_business isEqualToString:@""] || ![myContact.phone_mobile isEqualToString:@""]){ 
        ABMultiValueRef ABphones = ABMultiValueCreateMutable(kABMultiStringPropertyType);
        if(![myContact.phone_business isEqualToString:@""]) ([myContact.phone_business stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]].length == 4 || [myContact.phone_business stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]].length == 5) ? ABMultiValueAddValueAndLabel(ABphones, [NSString stringWithFormat:@"014443%@", myContact.phone_business], kABPersonPhoneMainLabel, NULL) : ABMultiValueAddValueAndLabel(ABphones, myContact.phone_business, kABPersonPhoneMainLabel, NULL);
        if(![myContact.phone_mobile isEqualToString:@""] && ([myContact.phone_mobile stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]].length == 10)) ABMultiValueAddValueAndLabel(ABphones, myContact.phone_mobile, kABPersonPhoneMobileLabel, NULL);
        ABRecordSetValue(aRecord, kABPersonPhoneProperty, ABphones, &anError);
        CFRelease(ABphones);
    }

    if(![myContact.job isEqualToString:@""]) ABRecordSetValue(aRecord, kABPersonJobTitleProperty, myContact.job, &anError);

    if(![myContact.msn isEqualToString:@""] || ![myContact.twitter isEqualToString:@""] || ![myContact.facebook isEqualToString:@""]){  
        ABMultiValueRef ABmessaging = ABMultiValueCreateMutable(kABMultiDictionaryPropertyType);
        NSMutableDictionary *dMessaging;

        if(![myContact.msn isEqualToString:@""]){
            dMessaging = [[NSMutableDictionary alloc] init];
            [dMessaging setObject:myContact.msn forKey:(NSString *) kABPersonInstantMessageUsernameKey];
            [dMessaging setObject:@"MSN" forKey:(NSString *)kABPersonInstantMessageServiceKey];
            ABMultiValueAddValueAndLabel(ABmessaging, dMessaging, kABPersonInstantMessageServiceMSN, NULL);
            [dMessaging release];
        }

        if(![myContact.twitter isEqualToString:@""]){
            dMessaging = [[NSMutableDictionary alloc] init];
            [dMessaging setObject:myContact.twitter forKey:(NSString *) kABPersonInstantMessageUsernameKey];
            [dMessaging setObject:@"Twitter" forKey:(NSString *)kABPersonInstantMessageServiceKey];
            ABMultiValueAddValueAndLabel(ABmessaging, dMessaging, kABOtherLabel, NULL);
            [dMessaging release];
        }

        if(![myContact.facebook isEqualToString:@""]){
            dMessaging = [[NSMutableDictionary alloc] init];
            [dMessaging setObject:myContact.facebook forKey:(NSString *) kABPersonInstantMessageUsernameKey];
            [dMessaging setObject:@"Facebook" forKey:(NSString *)kABPersonInstantMessageServiceKey];
            ABMultiValueAddValueAndLabel(ABmessaging, dMessaging, kABOtherLabel, NULL);
            [dMessaging release];

        }


        ABRecordSetValue(aRecord, kABPersonInstantMessageProperty, ABmessaging, &anError);
        CFRelease(ABmessaging);
    }

    //pas dans l'XMLToObjectParser parce que ça prenait une plombe...
    NSURL *url = [NSURL URLWithString:myContact.picture_path];
    NSData *data = [NSData dataWithContentsOfURL:url];

    if(!data){
        NSString *picture_path = (![myContact.gender isEqualToString:@""]) ? [NSString stringWithFormat:@"default_%@_head.png", [myContact.gender lowercaseString]] : @"default_m_head.png";

        [myContact setPicture_path:picture_path];
        NSError *error = nil;
        if(![self.managedObjectContext save:&error]){
            NSLog(@"pb lors de l'enregistrement de picture path");
        }

        //NSData *localData = [NSData dataWithContentsOfFile:myContact.picture_path];
        UIImage *image = [UIImage imageNamed:picture_path];
        NSData *localData = UIImagePNGRepresentation(image);   

        CFDataRef cfLocalData = CFDataCreate(NULL, [localData bytes], [localData length]);
        ABPersonSetImageData(aRecord, cfLocalData, &anError);
        ABAddressBookSave(ab, &anError);
        CFRelease(cfLocalData);

    }else {
        UIImage *image = [UIImage imageWithData:data];
        NSString *extension = [(NSArray*)[myContact.picture_path componentsSeparatedByString:@"."] objectAtIndex:1];

        NSData *localData = ([extension isEqualToString:@"png"]) ? UIImagePNGRepresentation(image) : UIImageJPEGRepresentation(image, 1.0f);   

        CFDataRef cfLocalData = CFDataCreate(NULL, [localData bytes], [localData length]);
        ABPersonSetImageData(aRecord, cfLocalData, &anError);
        ABAddressBookSave(ab, &anError);
        CFRelease(cfLocalData);
    }

    if (anError != nil) { NSLog(@"error :: %@", anError); } 



    ABUnknownPersonViewController *ABView = [[ABUnknownPersonViewController alloc] init];
    ABView.unknownPersonViewDelegate = self;
    ABView.displayedPerson = aRecord;
    ABView.allowsAddingToAddressBook = YES;
    ABView.allowsActions = YES;
    ABView.hidesBottomBarWhenPushed = YES;

    [self.navigationController pushViewController:ABView animated:YES];


    [ABView release];

    CFRelease(aRecord);

}
CFRelease(ab);

like image 625
Najaaa Avatar asked Dec 22 '10 14:12

Najaaa


1 Answers

Firstly: read up on your Core Foundation memory management. You don't yet know the rules by heart.

Secondly: when a CF-friendly function has "Copy" in the name, you must check its result for NULL, and release that result when done if it's not NULL. So this:

first = ABRecordCopyValue(person, kABPersonFirstNameProperty);

Will become a memory leak if it's never followed by CFRelease(first);.

Thirdly: if a Core Foundation value is NULL, passing it to CFRelease will crash:

CFStringRef firstname = ABRecordCopyValue(aContactFound, kABPersonFirstNameProperty);
ABRecordSetValue(aRecord, kABPersonFirstNameProperty, firstname, &anError);
CFRelease(firstname);

If firstname is NULL (which it could be--imagine a contact named simply "Smith") then a crash will happen.

like image 153
Jonathan Grynspan Avatar answered Nov 01 '22 22:11

Jonathan Grynspan