Hello,
I am getting email addresses out of the Address Book from a Cocoa Touch project and getting some unexpected results in terms of memory usage. The user opens the ABPeoplePicker and if the AB entry they touch has a single email address or no email address it uses
- (BOOL) peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
and if the entry has multiple email addresses it moves on to
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier {
In the single email address case, all of the memory used by the picker is released after the email address is selected. In the second multiple email case, about 300k is kept and not released, and this increases every time a multi-email Address Book entry is chosen. I believe that I have manually released everything I need to in the AB methods and I can't track down what is holding on to that memory or how to fix it, and I'm not seeing any other posts about this being a bug so I suspect I have an error. If anyone has any ideas what is going on here, please let me know. I have attached example code below for those who wish to reproduce the problem - it behaves identically in the simulator as on the device so you can run it in the simulator with Activity Monitor to see the memory usage. Thank you for any assistance!
Both AddressBook.framework and AddressBookUI.framework need to be added to a project running this code in order for it to function, and I am using the 3.0 SDK:
testViewController.h:
#import <UIKit/UIKit.h>
#import <AddressBook/AddressBook.h>
#import <AddressBookUI/AddressBookUI.h>
@interface testViewController : UIViewController <ABPeoplePickerNavigationControllerDelegate> {
UITextView *emailList ;
}
@property (nonatomic, retain) UITextView *emailList ;
@end
testViewController.m:
#import "testViewController.h"
@implementation testViewController
@synthesize emailList;
- (void) showContactPicker:(id)sender {
ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc] init];
picker.peoplePickerDelegate = self;
[self presentModalViewController:picker animated:YES];
[picker release];
}
- (void) peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker {
[self dismissModalViewControllerAnimated:YES];
}
- (BOOL) peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person {
BOOL returnState = NO;
ABMultiValueRef emails = ABRecordCopyValue(person, kABPersonEmailProperty);
if(ABMultiValueGetCount(emails) <= 0) { // the selected contact has no attached email address
[self dismissModalViewControllerAnimated:YES];
}
else if(ABMultiValueGetCount(emails) == 1) { // the selected contact has exactly one email address
CFStringRef email = ABMultiValueCopyValueAtIndex(emails, 0);
NSString *emailString = (NSString *) email;
self.emailList.text = [self.emailList.text stringByAppendingString:[NSString stringWithFormat:@"%@ ", emailString]];
[emailString release];
[self dismissModalViewControllerAnimated:YES];
}
else { // the selected contact has many email addresses, continue to the alternate method
returnState = YES;
}
CFRelease(emails);
return returnState;
}
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier {
ABMultiValueRef multiEmails = ABRecordCopyValue(person, kABPersonEmailProperty);
CFStringRef multiEmail = ABMultiValueCopyValueAtIndex(multiEmails, identifier);
CFRelease(multiEmails);
NSString *multiEmailString = (NSString *) multiEmail;
//CFRelease(multiEmail); //AnalysisTool pointed out that this is a double release since multiEmailString is an alias of multiEmail
self.emailList.text = [self.emailList.text stringByAppendingString:[NSString stringWithFormat:@"%@ ", multiEmailString]];
[multiEmailString release];
[self dismissModalViewControllerAnimated:YES];
return NO;
}
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *openContactsTitle = [[NSArray alloc] initWithObjects:@"Add Addresses", nil];
UISegmentedControl *openContacts = [[UISegmentedControl alloc] initWithItems:openContactsTitle];
openContacts.frame = CGRectMake(10,10,105,30);
[openContacts addTarget:self action:@selector(showContactPicker:) forControlEvents:UIControlEventValueChanged];
openContacts.segmentedControlStyle = UISegmentedControlStyleBar;
openContacts.momentary = TRUE;
[self.view addSubview:openContacts];
[openContacts release];
[openContactsTitle release];
emailList = [[UITextView alloc] initWithFrame:CGRectMake(10,60,200,200)];
[self.view addSubview:emailList];
emailList.text = @"";
}
- (void)dealloc {
[emailList release];
[super dealloc];
}
@end