2

UIViewController によって作成されたクラスが別のクラスの完了ブロックとして機能する場合、UIViewController が dealloc 呼び出しを取得すると、メモリ管理ライフサイクルはどのように機能しますか?

クラスを継承する UIViewController がクラス ControllerMediatorClass をインスタンス化するとします。

次に、ControllerMediatorClass は別のクラス ClassThatDealsWithNetworking を呼び出しますが、その作業が完了するまでに時間がかかります。

ClassThatDealsWithNetworking が完了する前に UIViewController がその割り当て解除を取得した場合、その所有権下にあるクラスはいつクリーンアップされますか?

ControllerMediatorClass は引き続き ClassThatDealsWithNetworking のインスタンスへの完了ブロックとして機能するため、MyUIViewController がそれを nil に設定すると、「ControllerMediatorClass」のインスタンスはすぐに割り当て解除されませんか?

MyUIViewController:

@property (nonatomic, strong) ControllerMediatorClass *mediatorClass;

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

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

コントローラーメディエータークラス:

- (void)makeANetworkCall
{
    ClassThatDealsWithNetworking *networkCommand;

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

(ARC使用)

4

1 に答える 1

0

ControllerMediatorClassはClassThatDealsWithNetworkingのインスタンスの完了ブロックとして機能するため、MyUIViewControllerがnilに設定しても、'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 = nilClassThatDealsWithNetworkingクラス(デリゲートメソッドまたはdeallocメソッド)で自分自身を忘れた場合、ブロックはClassThatDealsWithNetworkingインスタンスによって解放されることはなく、保持し続けselfます。


詳細については、AppleのドキュメントのBlocksProgrammingGuideを参照してください。

于 2012-10-03T17:30:01.610 に答える