У меня есть объект вроде этого:Почему мой объект освобожден сам по себе при переключении потока в 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;
}
Да, я подозревал, что объект уже выпущен в момент вызова моего асинхронного вызова, но я не знаю, как исправить эту ситуацию. Ах я вижу! Я никогда не знал об этой команде '__block'. Может ли эта команда увеличить счетчик, поскольку он «скопирован» для использования в блоке, поэтому он не освобождается в конце внешней функции? –
Да, он увеличивает счетчик удержания и уменьшает его в конце выполнения блока, а затем, если счет сохранения равен нулю, он будет выпущен в следующем цикле сбора мусора среди других автоматически освобождаемых памяти. Но вы упомянули, что уже пытались использовать «сохранить» вручную, и это не помогло, что заставило меня с подозрением относиться к этому, поэтому я попросил его попробовать. –
Понимая ваше объяснение, я понимаю, что мой метод 'keep' не работает, потому что я использую' keep' внутри потока блоков, чтобы передать объект в блок завершения lol. К тому времени объект, вероятно, уже был выпущен еще до вызова метода 'keep', поэтому я сохранил освобожденный блок памяти. xD Btw, спасибо, это работает! –