Address Book mistério uso de memória seletor no projeto de iPhone 3.0
-
05-07-2019 - |
Pergunta
Estou recebendo endereços de email fora do catálogo de endereços a partir de um projeto de Cocoa Touch e recebendo alguns resultados inesperados em termos de uso de memória. O usuário abre a ABPeoplePicker e se a entrada AB eles tocam tem um único endereço de e-mail ou nenhum endereço de e-mail que utiliza
- (BOOL) peoplePickerNavigationController: (ABPeoplePickerNavigationController *) PeoplePicker shouldContinueAfterSelectingPerson: (ABRecordRef) pessoa
e se a entrada tem vários endereços de e-mail que se move sobre a
- (BOOL) peoplePickerNavigationController: (ABPeoplePickerNavigationController *) PeoplePicker shouldContinueAfterSelectingPerson: (ABRecordRef) Propriedade pessoa: (ABPropertyID) identificador de propriedade: (ABMultiValueIdentifier) ??identificador {
No caso endereço de e-mail único, toda a memória usada pelo selecionador é liberado após o endereço de email está selecionado. No segundo caso email múltipla, cerca de 300k é mantido e não liberado, e isso aumenta cada vez que uma entrada do livro de multi-endereço de email está escolhido. Eu acredito que eu tenha liberado manualmente tudo o que preciso para nos métodos AB e eu não posso rastrear o que está segurando-se que a memória ou como corrigi-lo, e eu não estou vendo quaisquer outros posts sobre este ser um bug de forma Eu suspeito que eu tenho um erro. Se alguém tiver alguma idéia o que está acontecendo aqui, por favor me avise. Anexei exemplo de código abaixo para aqueles que desejam reproduzir o problema - ele se comporta de forma idêntica no simulador como no dispositivo para que você possa executá-lo no simulador com Activity Monitor para ver o uso de memória. Obrigado por qualquer ajuda!
Ambos AddressBook.framework e AddressBookUI.framework necessidade de ser adicionado a um projeto de executar este código para que ele funcione, e eu estou usando o SDK 3.0:
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
Solução
Durante o desenvolvimento do meu iPhone App Correio Serial eu tinha descoberto um vazamento de memória em ABPeoplePickerNavigationController. Tenho pagamento isto como um erro ao Bug Reporter Apple. Feedback de Apple é que o seu é um bug conhecido (o meu relatório de erro é fechada como uma duplicata de ID 6547310).
Outras dicas
Você poderia tentar correr AnalysisTool sobre ele para ver se ele pode detectar qualquer vazamento no código
Uma opção é fazer com que o selecionador de uma propriedade somente leitura da classe e não sintetizá-lo. Em vez disso, criar um método PeoplePicker garantir que apenas um único exemplo do seletor é instanciado. Se isso não funcionar com o seu ciclo de vida visão atual uma opção seria abstrair isso em uma única classe real.
Aqui está um exemplo que usei para o selecionador de imagem (câmera), que tem o mesmo problema de vazamento:
- (UIImagePickerController*)pickerController
{
// pickerController is a readonly property
if( pickerController == nil )
{
pickerController = [[UIImagePickerController alloc] init];
pickerController.allowsImageEditing = NO;
}
return pickerController;
}
Para isso eu coloquei todos os lançamentos na dealloc e didReceiveMemoryWarning (com um cheque nulo para evitar liberando nil). Neste caso, você vai efetivamente limitar a frequência com o seletor de livro de endereços é instanciado. Em muitos lugares, a Apple recomenda o uso de uma implementação singleton para as APIs selecionador de memória intensiva.