215

xcodeでこの警告を回避するにはどうすればよいですか. コード スニペットは次のとおりです。

[player(AVPlayer object) addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
queue:nil usingBlock:^(CMTime time) {
    current+=1;

    if(current==60)
    {
        min+=(current/60);
        current = 0;
    }

    [timerDisp(UILabel) setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];///warning occurs in this line
}];
4

7 に答える 7

522

ここでのキャプチャはself、 の暗黙的なプロパティ アクセスで行われます-によって強く保持されるブロック内からプロパティを参照またはオンにself.timerDispすることはできません。selfselfself

ブロック内selfにアクセスする前に弱い参照を作成することで、これを回避できます。timerDisp

__weak typeof(self) weakSelf = self;
[player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
                                     queue:nil
                                usingBlock:^(CMTime time) {
                                                current+=1;

                                                if(current==60)
                                                {
                                                    min+=(current/60);
                                                    current = 0;
                                                }

                                                 [weakSelf.timerDisp setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];
                                            }];
于 2013-01-28T06:39:16.963 に答える
54
__weak MyClass *self_ = self; // that's enough
self.loadingDidFinishHandler = ^(NSArray *receivedItems, NSError *error){
    if (!error) {
       [self_ showAlertWithError:error];
    } else {
       self_.items = [NSArray arrayWithArray:receivedItems];
       [self_.tableView reloadData];
    }
};

覚えておくべき非常に重要なことの 1 つ: インスタンス変数をブロック内で直接使用しないでください。それを弱いオブジェクトのプロパティとして使用してください。サンプル:

self.loadingDidFinishHandler = ^(NSArray *receivedItems, NSError *error){
        if (!error) {
           [self_ showAlertWithError:error];
        } else {
           self_.items = [NSArray arrayWithArray:receivedItems];
           [_tableView reloadData]; // BAD! IT ALSO WILL BRING YOU TO RETAIN LOOP
        }
 };

そして忘れないでください:

- (void)dealloc {
    self.loadingCompletionHandler = NULL;
}

誰にも保持されていないオブジェクトの弱いコピーを渡すと、別の問題が発生する可能性があります。

MyViewController *vcToGo = [[MyViewCOntroller alloc] init];
__weak MyViewController *vcToGo_ = vcToGo;
self.loadingCompletion = ^{
    [vcToGo_ doSomePrecessing];
};

割り当てが解除され、このブロックが起動された場合、変数vcToGoを含むゴミ箱への認識されないセレクターでクラッシュすると思います。vcToGo_それを制御してみてください。

于 2013-02-18T16:22:01.430 に答える
47

より良いバージョン

__strong typeof(self) strongSelf = weakSelf;

ブロックの最初の行として、その弱いバージョンへの強い参照を作成します。ブロックの実行が開始されたときに self がまだ存在し、nil に戻っていない場合、この行により、ブロックの実行期間全体にわたってそれが存続することが保証されます。

したがって、全体は次のようになります。

// Establish the weak self reference
__weak typeof(self) weakSelf = self;

[player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
                                 queue:nil
                            usingBlock:^(CMTime time) {

    // Establish the strong self reference
    __strong typeof(self) strongSelf = weakSelf;

    if (strongSelf) {
        [strongSelf.timerDisp setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];
    } else {
        // self doesn't exist
    }
}];

私はこの記事を何度も読みました。これは、 Erica Sadunに よる、ブロックと NSNotificationCenter を使用する際の問題を回避する方法に関する優れた記事です。


迅速な更新:

たとえば、swift では、成功ブロックを使用する単純なメソッドは次のようになります。

func doSomeThingWithSuccessBlock(success: () -> ()) {
    success()
}

このメソッドを呼び出しself、成功ブロックで使用する必要がある場合。と の機能を使用し[weak self]ますguard let

    doSomeThingWithSuccessBlock { [weak self] () -> () in
        guard let strongSelf = self else { return }
        strongSelf.gridCollectionView.reloadData()
    }

このいわゆる強弱ダンスは、人気のあるオープン ソース プロジェクトで使用されていますAlamofire

詳細については、swift-style-guide をチェックしてください

于 2014-05-13T10:40:33.560 に答える
15

別の回答で、ティムは次のように述べています。

自己によって強く保持されるブロック内から自己または自己のプロパティを参照することはできません。

これは正しくありません。ある時点でサイクルを中断する限り、これを行っても問題ありません。たとえば、自己を保持するブロックを持つ起動するタイマーがあり、自己のタイマーへの強い参照も保持しているとします。ある時点でタイマーを破棄してサイクルを壊すことが常にわかっている場合、これはまったく問題ありません。

ちょうど今の私の場合、次のようなコードに対して次の警告がありました。

[x setY:^{ [x doSomething]; }];

ここで、メソッドが「set」で始まることを検出した場合にのみ、clang がこの警告を生成することを知っています (ここでは言及しない特別なケースが 1 つあります)。私としては、retain ループが発生する危険がないことはわかっているので、メソッド名を「useY:」に変更しました。他の人に役立つ場合に備えて、私の解決策に注目する価値があると思いました。

于 2013-03-25T00:51:23.050 に答える