2009-07-30 8 views
1

Je reçois des adresses e-mail du carnet d'adresses d'un projet Cocoa Touch et obtient des résultats inattendus en termes d'utilisation de la mémoire. L'utilisateur ouvre la ABPeoplePicker et si l'entrée AB, ils touchent a une adresse e-mail unique ou sans adresse e-mail, il utiliseLe mystère d'utilisation de la mémoire du sélecteur de carnet d'adresses dans le projet iPhone 3.0

  • (BOOL) peoplePickerNavigationController: (ABPeoplePickerNavigationController *) Peoplepicker shouldContinueAfterSelectingPerson: (ABRecordRef) personne

et si l'entrée a plusieurs adresses e-mail, il se déplace sur

  • (BOOL) peoplePickerNavigationController: (ABPeoplePickerNavigationController *) peoplepicker shouldContinueAfterSelectingPerson: (ABRecordRef) propriété de personne: (ABPropertyID) identificateur de propriété: identifiant (ABMultiValueIdentifier) ​​{

Dans le cas d'adresse e-mail unique, toute la mémoire utilisée par le sélecteur est libéré après l'adresse e-mail est sélectionnée. Dans le deuxième cas de courrier électronique multiple, environ 300 Ko sont conservés et non publiés, et cela augmente chaque fois qu'une entrée de carnet d'adresses à plusieurs adresses e-mail est choisie. Je crois que j'ai libéré manuellement tout ce dont j'ai besoin dans les méthodes AB et je ne peux pas savoir ce qui se passe dans cette mémoire ou comment y remédier, et je ne vois pas d'autres messages à propos de ce bug. Je soupçonne que j'ai une erreur. Si quelqu'un a des idées sur ce qui se passe ici, s'il vous plaît faites le moi savoir. J'ai ci-joint l'exemple de code ci-dessous pour ceux qui souhaitent reproduire le problème - il se comporte de manière identique dans le simulateur comme sur l'appareil afin que vous puissiez l'exécuter dans le simulateur avec Activity Monitor pour voir l'utilisation de la mémoire. Merci pour toute aide!

Les deux AddressBook.framework et AddressBookUI.framework doivent être ajoutés à un projet en cours d'exécution de ce code afin de fonctionner, et je me sers du 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 
+0

Modifié légèrement ce qui précède pour indiquer que AnalysisTool a averti qu'il y avait un appel après une version dans le code d'origine. Le problème de mémoire d'origine reste. – Halle

+0

J'ai une solution de contournement pour faire du sélecteur une variable d'instance avec @property et @synthesize etc, ce qui augmente l'encombrement de la mémoire globale, mais entraîne une limite sur la quantité de mémoire captée, peu importe le nombre de fois demandé. Toujours curieux de connaître la raison sous-jacente de la fuite, cependant. – Halle

+0

Plus d'informations: J'ai exécuté Object Allocations, et dans la période où l'empreinte augmente, la grande augmentation d'allocation est une catégorie appelée "GeneralBlock-53248", qui ajoute environ 50k octets au moment où le Carnet d'adresses est appelé puis un autre 50k lorsque la deuxième page du carnet d'adresses est appelée dans le cas de plusieurs adresses e-mail, avec un octet net et un octet global de la même taille (c'est-à-dire qu'il ne libère jamais d'octets). Lorsque je clique sur la flèche de divulgation du résumé, on me montre qu'au moment où les octets ont été ajoutés, libsqlite3.dylib appelle sqlite3_blob_write. – Halle

Répondre

0

Au cours du développement de mon application iPhone Serial Mail j'avais découvert une fuite de mémoire dans ABPeoplePickerNavigationController. J'ai classé cela comme un bug pour le Apple Bug Reporter. Les commentaires d'Apple sont que son bug est connu (mon rapport de bogue est fermé en tant que doublon de l'ID 6547310).

+0

Salut Christian, Je l'ai également classé comme un bug et j'ai téléchargé mon projet d'exemple avec le rapport de bogue. – Halle

+0

Voir ceci http://stackoverflow.com/questions/5074684/memory-leak-with-abpeoplepickernavigationcontroller – nspire

1

Vous pouvez essayer d'exécuter AnalysisTool sur elle pour voir si elle peut détecter toute fuite dans le code

+0

Je vais vérifier, merci James. – Halle

+0

Personnellement, je trouve cela incroyablement pratique pour voir où je pourrais avoir raté une sortie après un ensemble d'instructions if –

+0

Je l'utilise maintenant et c'est incroyablement utile. La seule chose qui est un peu étrange, c'est qu'il se plaint de l'absence de autorelease après les allocations/initialisations qui sont gérées manuellement en mémoire. J'avais l'impression que l'autorelease devait être évitée sur l'iPhone. – Halle

0

Une option consiste à faire du sélecteur une propriété readonly de la classe et de ne pas la synthétiser. Au lieu de cela, créez une méthode peoplePicker pour vous assurer qu'une seule instance du sélecteur est instanciée. Si cela ne fonctionne pas avec le cycle de vie de votre vue actuelle, une option consisterait à en faire abstraction dans une classe singleton réelle.

Voici un exemple je pour le sélecteur d'image (caméra) qui a le même problème de fuite:

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

Pour cela, je mis tous les rejets dans dealloc et didReceiveMemoryWarning (avec un chèque nul pour éviter la libération nulle). Dans ce cas, vous limiterez la fréquence d'instanciation du sélecteur de carnet d'adresses. Dans de nombreux endroits, Apple recommande d'utiliser une implémentation singleton pour les API de sélecteur gourmandes en mémoire.

Questions connexes