2017-01-16 58 views
0

我有一个这样的对象:为什么我的对象在没有ARC的情况下在Xcode中切换线程时会自行释放?

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等等,其内容也不断变化。我甚至在调用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; 
} 

回答

1

问题是当您调用“异步”时,对象get被释放。 你声明你的对象的方式被添加到自动释放池,因为控件不等待'异步'完成,控制返回到达函数'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

是的,它会增加保留计数,并在块的执行结束时递减,届时如果保留计数为零,它将在下一个垃圾收集器周期中与其他自动释放内存一起释放。但是你提到你已经尝试过使用'retain'手动操作,它不起作用,这让我对它产生怀疑,所以这就是我要求试用它的原因。 –

+0

理解你的解释之后,我理解当然我的'retain'方法不起作用,因为我使用块线程内的'retain'将对​​象传递给完成块lol。到那时,即使在调用retain方法之前,对象可能已经被释放,所以我保留了一个释放的内存块。 xD顺便说一句,谢谢,这工作! –

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

您需要的时候调用超init这里并返回self。 开始学习基础知识。

+0

您有一点。但是,在我的原始代码中,init函数已经是正确的了。我只是错误地将其复制到问题。所以我编辑它以反映正确的状态。 –

相关问题