【问题标题】:Why my object deallocated by itself when switching thread in Xcode without ARC?为什么在没有 ARC 的 Xcode 中切换线程时我的对象自行释放?
【发布时间】:2017-01-16 10:14:23
【问题描述】:

我有一个这样的对象:

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 数据类型不断变化,从NSArrayMNSSetNSDictionary 等。它的内容也在不断变化。我什至在调用completion ([responseObj retain]); 时添加了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]);
}

代码完美运行。为什么会这样?这是在没有 ARC 的 Xcode 中构建的。

编辑:有人提到了 init。这是我的错误,上面写的不完全是我的代码,我把init方法复制错了。这是我的初始化方法:

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

【问题讨论】:

    标签: ios objective-c xcode automatic-ref-counting


    【解决方案1】:

    问题是当你调用 '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); }
            });
        }
    

    【讨论】:

    • 是的,我怀疑对象在我的异步调用被调用时已经被释放,但我不知道如何补救这种情况。啊,我明白了!我从来不知道这个__block 命令。这个命令是否也碰巧增加了计数器,因为它被“复制”以在块中使用,所以它不会在外部函数结束时释放?
    • 是的,它增加保留计数并在块执行结束时减少它,到那时如果保留计数为零,它将在下一个垃圾收集器周期中与其他自动可释放内存。但是您提到您已经尝试过手动使用“保留”但它不起作用,这让我对此感到怀疑,所以这就是我要求尝试它的原因。
    • 在理解了你的解释之后,我当然明白我的retain 方法不起作用,因为我使用块线程内部的retain 将对象传递给完成块lol。到那时,对象可能在调用retain 方法之前就已经被释放了,所以我保留了一个释放的内存块。 xD 顺便说一句,谢谢,这行得通!
    • 太好了,我很高兴。 :)
    【解决方案2】:
    - (id)initWithJson:(NSDictionary *)data { kdata = data; }
    

    你需要在这里调用 supers init 并返回 self。 开始学习基础知识。

    【讨论】:

    • 你说得有道理。但是,在我的原始代码中,init 函数已经是正确的。我只是错误地将其复制到问题中。所以我对其进行了编辑以反映正确的状态。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-07
    • 1970-01-01
    • 2012-01-30
    • 2011-11-18
    • 1970-01-01
    相关资源
    最近更新 更多