あのblocksは大丈夫?iOSで、blocksを使った記述で、リリースビルドのみにクラッシュする事例

追記:4/19/2012 @tokyopengwyn さんから指摘をいただき、安易に__blocksを追加するのがいつも正しい訳では無いというという事を教えて頂きました。この事例は、ReleaseとDebugビルドで挙動が違うことがあるというを思いに止めていただければと思います。ARC+Blocksには落とし穴も多いのでご注意ください。
追記:4/19/2012 ソースをgithubに公開しました。Blocksで記述出来るiPad用のPopOverMenuと通知のクラスを公開しました | Zero4Racer PRO Developer’s Blog こちらの記事から参照ください。

追記:4/21/2012 @tokyopengwyn さんの検証はこちら、分かりやすくまとめられています。 One Flew Over The {()}’s Nest: Blocks関連のバグがデバッグコンパイルで再現しないケース
ただいま、作成中のアプリ、TweetOverviewが、テストフライトでのテストを行っていて、そろそろサブミット出来るかなと思っています。

開発中はこんな感じです。

詳細画面
詳細画面

Blocksを使用している部分で、リリースビルドだけでクラッシュする場合があったので、これがそのままAppStoreに載ってたら大変なことになったと思うと、ちょっとぞくっとしました。

[iOS]リリースビルドでのみクラッシュする状況への対処について | Zero4Racer PRO Developer’s Blog この記事で書いた事がまた原因かなと思ったのですが、今回はこちらの設定を変えても問題は治りませんでした。


問題のコードはこちら。

dispatch_block_t block = ^{

NSString *str = [NSString stringWithFormat:[NSString stringWithFormat:NSLocalizedString(@"%@ \n by @%@ \n(Source: %@)", @"%@ \n by @%@") , [_tweet valueForKey:@"text"], [_tweet valueForKeyPath:@"user.screen_name"],[NSString stringWithFormat:@"http://twitter.com/%@/status/%@",[_tweet valueForKeyPath:@"user.screen_name"],[_tweet valueForKeyPath:@"id_str"]]]];

[[UIPasteboard generalPasteboard] setString:str];
};

ブロック内でコピーをするという簡単なコードですが、問題は、__blockを使用せずに、オブジェクト”_tweet”に直接触っていたという事でした。しかもこれが、debugビルドだとクラッシュしないため、通常は問題なく動いてしまうという事です。基本通り、__blockを使用して以下の様に書き直すと正しく動いてくれました。

__block id bt = _tweet;
dispatch_block_t block = ^{

NSString *str = [NSString stringWithFormat:[NSString stringWithFormat:NSLocalizedString(@"%@ \n by @%@ \n(Source: %@)", @"%@ \n by @%@") , [bt valueForKey:@"text"], [bt valueForKeyPath:@"user.screen_name"],[NSString stringWithFormat:@"http://twitter.com/%@/status/%@",[bt valueForKeyPath:@"user.screen_name"],[bt valueForKeyPath:@"id_str"]]]];

[[UIPasteboard generalPasteboard] setString:str];
};

__block演算子は、ブロックをコピーする際に、アクセスカウンタを一つ増やし、ブロックのメモリが削除される時にアクセスカウンタを減らすことによって、ブロックの仕様に exc_bad_access を発生させないためのものです。
詳細は、エキスパート Objective-C でどうぞ。
追記:この部分ですが、__blocksを使用しない形に書き換えました。Blockをretainして、copyしていなかったために、正しくキャプチャされなかったと思います。現在はこの様になっています。

NSMutableDictionary * dicRow = [NSMutableDictionary dictionaryWithCapacity:0];
[dicRow setValue:NSLocalizedString(@"USE THIS:Pattern 4 declare bt and copy block",nil) forKey:kDicKeyLinkPopCellText];
[arrRows addObject:dicRow];
id bt = _tweet;
dispatch_block_t block = ^{

NSString *str = [NSString stringWithFormat:@"%@",[bt valueForKey:@"text"]];

[[UIPasteboard generalPasteboard] setString:str];

[[JTCAppNotificationManager sharedManger] startTimerNotificationWithMessage:NSLocalizedString(@"Copy succeeded", @"Copy succeeded") dulation:2.5 iconName:@"w17-check.png"];
};
[dicRow setValue:[block copy] forKey:kDicKeyLinkPopBlock];

以前よりもコードが多いですが、以前の部分は削っていた部分を、今回は削っていません。最後の部分で、ディクショナリにセットする際に、ブロックをコピーして、追加することにより、リリースビルドで落ちなくなりました。

blocks, ARC は、非常に便利ですが、やはりある程度使用方法のルールを守らないと問題が起こるので注意ですね。

追記:このコードはARCの状況ですね。質問ツイートがあったので張っときます。

「あのblocksは大丈夫?iOSで、blocksを使った記述で、リリースビルドのみにクラッシュする事例」への2件のフィードバック

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください