Pregunta

Estoy obteniendo direcciones de correo electrónico de la Libreta de direcciones de un proyecto de Cocoa Touch y obteniendo algunos resultados inesperados en términos de uso de memoria. El usuario abre ABPeoplePicker y si la entrada AB que tocan tiene una única dirección de correo electrónico o no utiliza ninguna dirección de correo electrónico que utiliza

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

y si la entrada tiene varias direcciones de correo electrónico, se moverá a

  • (BOOL) peoplePickerNavigationController: (ABPeoplePickerNavigationController *) peoplePicker shouldContinueAfterSelectingPerson: (ABRecordRef) person property: (ABPropertyID) identificador de la propiedad: (ABMultiValueIdentifier) ??identifier {

En el caso de una sola dirección de correo electrónico, toda la memoria utilizada por el selector se libera después de seleccionar la dirección de correo electrónico. En el segundo caso de correo electrónico múltiple, alrededor de 300 k se mantienen y no se liberan, y esto aumenta cada vez que se elige una entrada de la libreta de direcciones de correo electrónico múltiple. Creo que he liberado manualmente todo lo que necesito en los métodos AB y no puedo rastrear lo que está guardando en esa memoria o cómo solucionarlo, y no veo ninguna otra publicación sobre este error. Sospecho que tengo un error. Si alguien tiene alguna idea de lo que está pasando aquí, hágamelo saber. He adjuntado un código de ejemplo a continuación para aquellos que desean reproducir el problema: se comporta de manera idéntica en el simulador que en el dispositivo, de modo que puede ejecutarlo en el simulador con el Monitor de actividad para ver el uso de la memoria. ¡Gracias por cualquier ayuda!

Tanto AddressBook.framework como AddressBookUI.framework se deben agregar a un proyecto que ejecute este código para que funcione, y estoy usando el 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
¿Fue útil?

Solución

Durante el desarrollo de mi aplicación para iPhone Correo serial Descubrí una pérdida de memoria en ABPeoplePickerNavigationController. He archivado esto como un error en el Apple Bug Reporter. La opinión de Apple es que se trata de un error conocido (mi informe de error está cerrado como un duplicado del ID 6547310).

Otros consejos

Puede intentar ejecutar AnalysisTool en él para ver si puede detectar alguna fuga en el código

Una opción es hacer que el selector sea una propiedad de solo lectura de la clase y no sintetizarla. En su lugar, cree un método peoplePicker y asegúrese de que solo se instancia una instancia del selector. Si eso no funciona con su ciclo de vida de la vista actual, una opción sería abstraer esto en una clase singleton real.

Este es un ejemplo que utilicé para el selector de imágenes (cámara) que tiene el mismo problema de fugas:

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

Para esto coloqué todos los lanzamientos en dealloc y didReceiveMemoryWarning (con una comprobación nula para evitar liberar nil). En este caso, limitará efectivamente la frecuencia con la que se crea una instancia del selector de la libreta de direcciones. En muchos lugares, Apple recomienda usar una implementación singleton para las API de selector de uso intensivo de memoria.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top