J'ai un objet comme ça:

typedef void (^ Completion) (Response *);

// Response class
@interface Response : NSObject {
    NSDictionary * kdata;
}
- (id)initWithJson:(NSDictionary *)data;
@property (nonatomic, assign) NSDictionary * data;
@end

@implementation Response
- (id)initWithJson:(NSDictionary *)data { kdata = data; }
- (NSDictionary *) data                 { return kdata; }
- (void) setData: (NSDictionary *)data  { kdata = data; }
- (NSDictionary *) msg                  { return kdata[@"msg"]; }
@end


// inside a networking class X implementation
- (void) doSomething:(completionBlock)completion {
    NSDictionary * json = // get from networking function, which will always have key "msg".
    Response * responseObj = [[Response alloc] initWithJson:json];
    dispatch_async(dispatch_get_main_queue(), ^{
        if (completion != nil) { completion (responseObj); }
    });
}


// inside caller method
[X doSomething:^(Response * response) {
    NSLog (@"%@", [response msg]);
}

Ce code soulèvera une erreur lors de l'accès à kdata[@"msg"], même si je suis sûr d'après le débogage que l'objet a été correctement initialisé avec un dictionnaire contient la clé "msg". Lorsque je débogue l'objet, sur la fenêtre de surveillance, cela me montre que le type de données kdata ne cesse de changer, de NSArrayM, NSSet, NSDictionary, etc. Et son contenu continue également de changer. J'ajoute même le mot clé retain lors de l'appel completion ([responseObj retain]); mais je produis toujours une erreur.

Mais si le code de la classe X est changé comme ceci:

// inside a networking class X implementation
- (void) doSomething:(completionBlock)completion {
    NSDictionary * json = // get from networking function, which will always have key "msg".
    Response * responseObj = [[Response alloc] initWithJson:json];
    if (completion != nil) { completion (responseObj); } // here is the change, no more switching to main thread
}

// inside caller method - no change here
[X doSomething:^(Response * response) {
    NSLog (@"%@", [response msg]);
}

Le code fonctionne parfaitement. Pourquoi est-ce arrivé? Ceci est construit dans Xcode sans ARC.

EDIT: quelqu'un a mentionné à propos de l'init. C'est mon erreur que ce qui a été écrit ci-dessus n'est pas exactement mon code, et je copie mal la méthode init. Voici ma méthode init:

- (instancetype) initWithData:(NSDictionary *)freshData {
    NSParameterAssert(freshData); // make sure not nil
    self = [super init];
    if (self) {
        kdata = freshData;
    }
    return self;
}
0
Chen Li Yong 16 janv. 2017 à 13:14

2 réponses

Meilleure réponse

Le problème est que l'objet est libéré dès que vous appelez «async». La façon dont vous avez déclaré votre objet est ajoutée au pool de libération automatique puisque le contrôle n'attend pas la fin de 'async' et le retour du contrôle en atteignant la fin de la fonction 'doSomething' et en libérant ses objets locaux qui ont été ajoutés au pool de libération automatique, et après cela, l'emplacement de la mémoire est utilisé pour d'autres données et c'est ce que vous voyez des données déroutantes. Je pense qu'en ajoutant le spécificateur __block devant votre déclaration, vous demandez au code de capturer fortement cet objet dans les blocs suivants et de le relâcher lorsque le bloc a fini de s'exécuter. Essaie.

// inside a networking class X implementation
    - (void) doSomething:(completionBlock)completion {
        NSDictionary * json = // get from networking function, which will always have key "msg".
        __block Response * responseObj = [[Response alloc] initWithJson:json];
        dispatch_async(dispatch_get_main_queue(), ^{
          if (completion != nil) { completion (responseObj); }
        });
    }
1
Rhm Akbari 19 janv. 2017 à 22:34
- (id)initWithJson:(NSDictionary *)data { kdata = data; }

Vous devez appeler les supers init ici et revenir self. Commencez à apprendre les bases.

1
Cy-4AH 16 janv. 2017 à 10:29