【问题标题】:Lifetime of a class and blocks类和块的生命周期
【发布时间】:2012-10-03 16:01:17
【问题描述】:

当一个由 UIViewController 创建的类作为另一个类的完成块时,当 UIViewController 得到一个 dealloc 调用时,内存管理生命周期是如何工作的?

假设 UIViewController 继承类实例化了一个类,ControllerMediatorClass。

ControllerMediatorClass 反过来调用另一个类 ClassThatDealsWithNetworking,它的工作需要一些时间才能完成。

如果 UIViewController 在 ClassThatDealsWithNetworking 完成之前获得它的 dealloc,那么它所拥有的类什么时候会被清理?

当 MyUIViewController 将“ControllerMediatorClass”的实例设置为 nil 时,它是否不会立即被释放,因为 ControllerMediatorClass 仍然充当 ClassThatDealsWithNetworking 实例的完成块?

MyUIViewController:

@property (nonatomic, strong) ControllerMediatorClass *mediatorClass;

- (IBAction)userTappedSomething
{
    [mediatorClass makeANetworkCall];
}

- (void)dealloc
{
   self.mediatorClass = nil;
}

ControllerMediator类:

- (void)makeANetworkCall
{
    ClassThatDealsWithNetworking *networkCommand;

    [networkCommand execute:^(NSDictionary *data) 
        {          
            // handling completion that 
        } error:^(MyError *error) 
        {        
            // handling completion
        }
    ];
}

(使用 ARC)

【问题讨论】:

  • +1 使用调解器来处理这个!

标签: objective-c ios memory-management objective-c-blocks


【解决方案1】:

当 MyUIViewController 将“ControllerMediatorClass”的实例设置为 nil 时,它是否不会立即被释放,因为 ControllerMediatorClass 仍然充当 ClassThatDealsWithNetworking 实例的完成块?

是的。因为块会自动捕获它使用的对象,并在保留块时保留它们。

因此,[ClassThatDealsWithNetworking execute:] 方法可能会保留传递给它的完成块,然后在后台执行网络调用,然后在完成后调用该块并释放该块,当该块被保留时每个变量在这个块中使用的也被保留。并且会在块被释放时被释放。


所以想象一下你的 ClassThatDealsWithNetworking 类的伪代码:

typedef void(^NetworkingCompletionBlock)(NSDictionary* data)
@interface ClassThatDealsWithNetworking
@property(nonatomic, copy) NetworkingCompletionBlock completionBlock;
-(void)execute:(NetworkingCompletionBlock)block;
@end

@implementation ClassThatDealsWithNetworking
-(void)execute:(NetworkingCompletionBlock)block {
  // make a copy of the block passed as a parameter
  // so that we keep the completionBlock around inside
  // the ClassThatDealsWithNetworking instance
  // until the network request has finished
  // ==> THIS WILL implicitly RETAIN every object used in the completionBlock
  self.completionBlock = block; 
  ...
  // Then perform the network request
  [NSURLConnection connectionWithRequest:... delegate:self];
  ...
}

-(void)connection:(NSURLConnection*)cxn didFailWithError:(NSError*)error
{
  // call the completion block here
  self.completionBlock(nil,error);
  // then release the completionBlock
  // ==> THIS WILL RELEASE every object previously implicitly retained by the completionBlock
  self.completionBlock = nil;
}
-(void)connectionDidFinishLoading:(NSURLConnection*)cxn
{
  NSDictionary* data = ...
  // call the completion block here
  self.completionBlock(data,nil);
  // then release the completionBlock
  // ==> THIS WILL RELEASE every object previously implicitly retained by the completionBlock
  self.completionBlock = nil;
}
@end

如果你这样做:

[networkCommand execute:^(NSDictionary *data) 
{
    self.someProperty = data;
} error:^(MyError *error) 
{
    NSLog(@"%@", error);
    self.someProperty : nil;
}];

然后self(在您的示例中为您的ControllerMediatorClass将被块本身隐式保留,只要块存在,因为您在正文中的引用self阻塞某处。所以编译器知道在执行块时它会需要它并保留它。并且它会在块被释放时隐式释放

这样可以确保你在块体中使用的所有对象在块执行时仍然存在,避免崩溃。

请注意,如果您不小心,这可能会导致保留循环。例如,如果您忘记在ClassThatDealsWithNetworking 类中(在委托方法或dealloc 方法中)自我self.completionBlock = nil,则该块将永远不会被ClassThatDealsWithNetworking 实例释放,并将继续保留@987654333 @。


有关详细信息,请阅读 Apple 文档中的 Blocks Programming Guide

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-10
    • 2023-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-03
    • 2016-12-01
    相关资源
    最近更新 更多