iPhone 3.0项目中的地址簿选择器内存使用谜

时间:2009-07-30 10:35:59

标签: iphone objective-c iphone-sdk-3.0

我从Cocoa Touch项目中获取地址簿中的电子邮件地址,并在内存使用方面获得了一些意想不到的结果。用户打开ABPeoplePicker,如果他们触摸的AB条目有一个电子邮件地址或没有使用的电子邮件地址

  • (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person

如果条目有多个电子邮件地址,则会转到

  • (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person属性:(ABPropertyID)属性标识符:(ABMultiValueIdentifier)标识符{

在单个电子邮件地址的情况下,选择电子邮件地址后,将释放选取器使用的所有内存。在第二个多个电子邮件的情况下,大约300k被保留并且没有被释放,并且每次选择多电子邮件地址簿条目时这都会增加。我相信我已经在AB方法中手动发布了我需要的所有内容,但是我无法追踪到内存中的内容或者如何修复它,而且我没有看到任何其他帖子关于这是一个bug所以我怀疑我有错误。如果有人对此有什么想法,请告诉我。我已经为那些希望重现问题的人添加了下面的示例代码 - 它在模拟器中的行为与在设备上的行为完全相同,因此您可以在带有Activity Monitor的模拟器中运行它以查看内存使用情况。谢谢你的帮助!

需要将AddressBook.framework和AddressBookUI.framework添加到运行此代码的项目中才能使其正常运行,我使用的是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

3 个答案:

答案 0 :(得分:1)

您可以尝试在其上运行AnalysisTool以查看它是否可以检测到代码中的任何泄漏

答案 1 :(得分:0)

在开发我的iPhone App Serial Mail期间,我在ABPeoplePickerNavigationController中发现了内存泄漏。我已将此作为错误提交给Apple Bug Reporter。来自Apple的反馈是,他是一个已知的错误(我的错误报告作为ID 6547310的副本被关闭)。

答案 2 :(得分:0)

一个选项是使选择器成为类的只读属性,而不是合成它。相反,创建一个peoplePicker方法确保仅实例化选择器的单个实例。如果这不适用于您当前的视图生命周期,则可以选择将其抽象为实际的单例类。

以下是我用于图像选择器(相机)的示例,它具有相同的泄漏问题:

- (UIImagePickerController*)pickerController
{
    // pickerController is a readonly property
    if( pickerController == nil )
    {
        pickerController = [[UIImagePickerController alloc] init];
        pickerController.allowsImageEditing = NO;
    }
    return pickerController;
}

为此,我将所有版本放在dealloc和didReceiveMemoryWarning中(使用nil检查以避免释放nil)。在这种情况下,您将有效地限制实例化地址簿选择器的频率。在许多地方,Apple建议对内存密集型选择器API使用单例实现。