iOSのアプリ内課金でプロダクト情報を取得するために、SKProductsRequestのインスタンスにdelegateをセットして、startメソッドを呼び出した。
そして、delegateにセットしたインスタンスでデータを受け取り、諸々処理して、不要になったdelegate用のインスタンスを削除したら、EXC_BAD_ACCESSエラーが発生したというお話。
もちろん、SKProductsRequestのインスタンスのdelegate設定は解除したのに、だ。
-(void)productsRequest:(SKProductsRequest *)request
didReceiveResponse:(SKProductsResponse *)response {
...
[_productsRequest setDelegate:nil];
_productsRequest = nil;
// この通知を受けて、delegate用のインスタンスが削除破棄される
[[NSNotificationCenter defaultCenter] postNotification:n];
}
といった感じのコード。
推測としては、どうやらSKProductsRequestが-(void)productsRequest:didReceiveResponse
をコールした後にも、delegate用のインスタンスにアクセスしようとするんじゃないだろうか、と。
でも、その前にdelegate用のインスタンスが破棄されているので、EXC_BAD_ACCESSエラーが発生しているのではないかと思う。
setDelegate:nil
をやっているので、本来であれば、そういったことは起こらないはずなのだけど。
とりあえず、最後の通知部分(インスタンスが削除される部分)を以下のようにしてやることで、エラーを回避することができた。
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotification:n];
});
もし何か正しい理由と対策をご存知の方がいらっしゃいましたら、教えてください。
正しいやり方、分かりました。
SKProductsRequestDelegateはSKRequestDelegateを継承していて、このSKRequestDelegateに- (void)requestDidFinish:(SKRequest *)request
というメソッドがある。
リファレンスに
When this method is called, your delegate receives no further communication from the request and can release it.
とある通り、このメソッドを受けてdelegate用インスタンスを破棄すれば良かったのだ。
気づいてみれば、簡単なことだった。