ClassThatDealsWithNetworking

ClassThatDealsWithNetworking

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

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

ControllerMediatorClass依次调用另一个类ClassThatDealsWithNetworking,其工作需要一些时间才能完成。

如果UIViewController在ClassThatDealsWithNetworking完成之前获得其释放,那么何时清理其所有权下的类?

当MyUIViewController将其设置为nil时,由于ControllerMediatorClass仍然充当ClassThatDealsWithNetworking实例的完成块,'ControllerMediatorClass'的实例是否不会立即释放?

MyUIViewController:

@property (nonatomic, strong) ControllerMediatorClass *mediatorClass;

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

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

ControllerMediatorClass:
- (void)makeANetworkCall
{
    ClassThatDealsWithNetworking *networkCommand;

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

(使用ARC)

最佳答案

当MyUIViewController将其设置为nil时,由于ControllerMediatorClass仍然充当ClassThatDealsWithNetworking实例的完成块,'ControllerMediatorClass'的实例是否不会立即释放?

是。因为块会自动捕获它使用的对象并保留它们,只要保留该块即可。

因此,由于[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。因此,编译器知道在执行该块时将需要它并保留它。并且在释放块时将隐式释放

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

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

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

10-08 12:27