为什么在没有 ARC 的 Xcode 中切换线程时我的对象自行释放?
Why my object deallocated by itself when switching thread in Xcode without 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"
的字典正确初始化。当我调试对象时,在手表 window 上,它显示 kdata 数据类型不断变化,从 NSArrayM
、NSSet
、NSDictionary
等。及其内容也不断变化。我什至在调用 completion ([responseObj retain]);
时添加了 retain
关键字,但仍然会产生错误。
但是如果把classX里面的代码改成这样:
// 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。
编辑:有人提到了 init.这是我的错误,上面写的不完全是我的代码,我把init方法复制错了。这是我的初始化方法:
- (instancetype) initWithData:(NSDictionary *)freshData {
NSParameterAssert(freshData); // make sure not nil
self = [super init];
if (self) {
kdata = freshData;
}
return self;
}
- (id)initWithJson:(NSDictionary *)data { kdata = data; }
您需要在这里呼叫超级 init
和 return self
。
开始学习基础知识。
问题是当您调用 'async' 时对象被正确释放。
您声明对象的方式被添加到自动释放池中,因为控件不会等待 'async' 完成并且控件 return 到达函数 '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); }
});
}
我有一个这样的对象:
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"
的字典正确初始化。当我调试对象时,在手表 window 上,它显示 kdata 数据类型不断变化,从 NSArrayM
、NSSet
、NSDictionary
等。及其内容也不断变化。我什至在调用 completion ([responseObj retain]);
时添加了 retain
关键字,但仍然会产生错误。
但是如果把classX里面的代码改成这样:
// 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。
编辑:有人提到了 init.这是我的错误,上面写的不完全是我的代码,我把init方法复制错了。这是我的初始化方法:
- (instancetype) initWithData:(NSDictionary *)freshData {
NSParameterAssert(freshData); // make sure not nil
self = [super init];
if (self) {
kdata = freshData;
}
return self;
}
- (id)initWithJson:(NSDictionary *)data { kdata = data; }
您需要在这里呼叫超级 init
和 return self
。
开始学习基础知识。
问题是当您调用 'async' 时对象被正确释放。 您声明对象的方式被添加到自动释放池中,因为控件不会等待 'async' 完成并且控件 return 到达函数 '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); }
});
}