2017-01-16 7 views
0

У меня есть объект вроде этого:Почему мой объект освобожден сам по себе при переключении потока в Xcode без ARC?

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]); 
} 

Этого код поднимет ошибку доступа к kdata[@"msg"], хотя я уверен, что из отлаживать, что объект был инициированным правильно со словарем содержит ключ "msg". Когда я отлаживаю объект, в окне просмотра он показывает мне, что тип данных kdata продолжает меняться: от NSArrayM, NSSet, NSDictionary и т. Д. И его содержимое также меняется. Я даже добавляю ключевое слово retain при вызове completion ([responseObj retain]);, но все равно выдаю ошибку.

Но если код в классе X превращается в так:

// 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]); 
} 

код работает отлично. Почему это произошло? Это встроено в Xcode без ARC.

EDIT: кто-то упомянул об инициализации. Это моя ошибка, что то, что было написано выше, не совсем соответствует моему коду, и я неправильно скопирую метод init. Это мой метод инициализации:

- (instancetype) initWithData:(NSDictionary *)freshData { 
    NSParameterAssert(freshData); // make sure not nil 
    self = [super init]; 
    if (self) { 
     kdata = freshData; 
    } 
    return self; 
} 

ответ

1

Проблема заключается в том, что объект get освобождается сразу, когда вы вызываете «async». Способ, которым вы объявили, что ваш объект добавлен в пул автозапуска, поскольку элемент управления не ждет завершения «async» и возврата элемента управления, дойдя до конца функции «doSomething» и выпуская локальные объекты, которые были добавлены в пул автозаполнения , и после этого место памяти используется для других данных, и это то, что вы видите запутанными данными. Я думаю, добавив спецификатор __block перед объявлением, вы проинструктируете код, чтобы он зафиксировал этот объект в следующих блоках сильно и отпустил его, когда блок завершил выполнение. Попробуйте.

// 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); } 
     }); 
    } 
+0

Да, я подозревал, что объект уже выпущен в момент вызова моего асинхронного вызова, но я не знаю, как исправить эту ситуацию. Ах я вижу! Я никогда не знал об этой команде '__block'. Может ли эта команда увеличить счетчик, поскольку он «скопирован» для использования в блоке, поэтому он не освобождается в конце внешней функции? –

+0

Да, он увеличивает счетчик удержания и уменьшает его в конце выполнения блока, а затем, если счет сохранения равен нулю, он будет выпущен в следующем цикле сбора мусора среди других автоматически освобождаемых памяти. Но вы упомянули, что уже пытались использовать «сохранить» вручную, и это не помогло, что заставило меня с подозрением относиться к этому, поэтому я попросил его попробовать. –

+0

Понимая ваше объяснение, я понимаю, что мой метод 'keep' не работает, потому что я использую' keep' внутри потока блоков, чтобы передать объект в блок завершения lol. К тому времени объект, вероятно, уже был выпущен еще до вызова метода 'keep', поэтому я сохранил освобожденный блок памяти. xD Btw, спасибо, это работает! –

1
- (id)initWithJson:(NSDictionary *)data { kdata = data; } 

Вам нужно позвонить надставкам init здесь и вернуться self. Начните изучать основы.

+0

У вас есть точка. Однако в моем исходном коде функция init уже корректна. Я просто неправильно копирую его на вопрос. Поэтому я отредактировал его, чтобы отразить правильное состояние. –