Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
 Objective-CのBlocksの循環参照に関する僕なりのベストプラクティス
自己紹介
• 
• 
• 
• 

片岡 直之
株式会社シロク
SNS: @katty0324
ブログ: 三度の飯とエレクトロン (
http://blog.katty.in/ )
ある日の社内SNSにて・・・
 Objective-CのBlocksの循環参照に関する僕なりのベストプラクティス
なんとなく
 Objective-CのBlocksの循環参照に関する僕なりのベストプラクティス
とは言ったものの
最後にiOSアプリを作っていたの
は半年以上前だったことを思い出
し・・・

←アバターチャットアプリ
半年前にクローズ済み
ネタがないことに気づく。
 Objective-CのBlocksの循環参照に関する僕なりのベストプラクティス
自分のブログを漁ってみたところ
 Objective-CのBlocksの循環参照に関する僕なりのベストプラクティス
良さそうなのがあった。
 Objective-CのBlocksの循環参照に関する僕なりのベストプラクティス
というわけで本日のテーマ
Objective-CのBlocksの循環参
照に関する僕なりのベストプラク
ティス
その前に
Delegateとは?
Objective-C流のコールバック
手法
Blocksとは?
Objective-C流のモダンなコー
ルバック手法
参照カウントとは?
オブジェクトの解放管理のための
数値変数
Blocksの循環参照とは?
オブジェクトがBlocksを参照して
いて、そのBlocks内でそのオブ
ジェクトを参照している状態

強参照

呼び出し元
強参照

Blocksの実行者

Blocks
強参照
BLOCKSの使い方
int main(int argc, const char * argv[])
{
@autoreleasepool {
BlocksRunner *blocksRunner = [[[BlocksRunner alloc] init] autorelease];
NSLog(@"blockRunner retainCount: %ld", [blocksRunner retainCount]);
blocksRunner.runnable = ^{
NSLog(@"blocksRunner");
};
NSLog(@"blockRunner retainCount: %ld", [blocksRunner retainCount]);
[blocksRunner run];
}
return 0;
}

BlocksRunner init
blockRunner retainCount: 1
blockRunner retainCount: 1
blocksRunner
BlocksRunner dealloc
Blocksで処理を保持しておけば、
必要になった時に実行できる。
int main(int argc, const char * argv[])
{
@autoreleasepool {
BlocksRunner *blocksRunner = [[[BlocksRunner alloc] init] autorelease];
NSLog(@"blockRunner retainCount: %ld", [blocksRunner retainCount]);
blocksRunner.runnable = ^{
NSLog(@"blocksRunner: %@", blocksRunner);
};
NSLog(@"blockRunner retainCount: %ld", [blocksRunner retainCount]);
[blocksRunner run];
}
return 0;
}

BlocksRunner init
blockRunner retainCount: 1
blockRunner retainCount: 2
blocksRunner: <BlocksRunner:
0x1001144b0>
Blocksが循環参照してしまうと、
オブジェクトは解放されない。
ではどうするか?
int main(int argc, const char * argv[])
{
@autoreleasepool {
BlocksRunner *blocksRunner = [[[BlocksRunner alloc] init] autorelease];
NSLog(@"blockRunner retainCount: %ld", [blocksRunner retainCount]);
__block BlocksRunner *weakBlocksRunner = blocksRunner;
blocksRunner.runnable = ^{
NSLog(@"blocksRunner: %@", weakBlocksRunner);
};
NSLog(@"blockRunner retainCount: %ld", [blocksRunner retainCount]);
[blocksRunner run];
}
return 0;
}

BlocksRunner init
blockRunner retainCount: 1
blockRunner retainCount: 1
blocksRunner: <BlocksRunner:
0x1001143b0>
BlocksRunner dealloc
Blocks内に渡されたオブジェクト
は参照カウントが増えてしまうの
で、弱参照の修飾子を添えて渡す。

強参照

呼び出し元
弱参照

Blocksの実行者

Blocks
強参照
というのは結構スタンダードな手
法ですが
僕はweakSelfパターンと呼んで
勝手にアンチパターン扱いしてい
ます。
WEAKSELFパターンの弱点
__blockとか、__weakという修
飾子が汚い。
Blocksを使うたびに、毎回変数
を確保しなければならない。
オブジェクトを弱参照で渡すので、
オブジェクトの存在を保証できな
い。
Blocks構文の使い手が、中の実
装を意識しなくてはいけない。
そもそも
int main(int argc, const char * argv[])
{
@autoreleasepool {
BlocksRunner *blocksRunner = [[[BlocksRunner alloc] init] autorelease];
NSLog(@"blockRunner retainCount: %ld", [blocksRunner retainCount]);
blocksRunner.runnable = ^{
NSLog(@"blocksRunner: %@", blocksRunner);
};
NSLog(@"blockRunner retainCount: %ld", [blocksRunner retainCount]);
[blocksRunner run];
}
return 0;
}
こうやって使ってもちゃんとメモ
リが解放されるのが理想であって
int main(int argc, const char * argv[])
{
@autoreleasepool {
BlocksRunner *blocksRunner = [[[BlocksRunner alloc] init] autorelease];
NSLog(@"blockRunner retainCount: %ld", [blocksRunner retainCount]);
__block BlocksRunner *weakBlocksRunner = blocksRunner;
blocksRunner.runnable = ^{
NSLog(@"blocksRunner: %@", weakBlocksRunner);
};
NSLog(@"blockRunner retainCount: %ld", [blocksRunner retainCount]);
[blocksRunner run];
}
return 0;
}
使い手が、メモリが解放されるよ
うに仕向けなければならないのは
良くない。
BLOCKSの使い方は3種類
その場限りのBlocks
1度きりのBlocks
使いまわすBlocks
その場限りのBLOCKS
たとえば、jQueryにおける
$.each(func)のような、同期的
に処理されるものなど。
InstantBlocksRunner *instantBlocksRunner = [[[InstantBlocksRunner alloc]
init] autorelease];
[instantBlocksRunner run: ^{
NSLog(@"instantBlocksRunner: %@", instantBlocksRunner);
}];
循環参照している!
これをメモリリークさせないため
には・・・
- (void)run:(void (^)(void))runnable {
if(runnable)
runnable();
}

強参照

呼び出し元
強参照

Blocksの実行者

Blocks
弱参照
その場限りのBlocksは、Blocks
を保持しないことで、メモリリー
クを回避する!
1度きりのBLOCKS
非同期的にHTTP通信しその結果
をBlocksでコールバックするよ
うな場合など。
OneTimeBlocksRunner *oneTimeBlocksRunner = [[[OneTimeBlocksRunner
alloc] init] autorelease];
oneTimeBlocksRunner.runnable = ^{
NSLog(@"oneTimeBlocksRunner: %@", oneTimeBlocksRunner);
};
[oneTimeBlocksRunner run];
循環参照している!
これをメモリリークさせないため
には・・・
- (void)run {
if(runnable)
runnable();
self.runnable = nil;
}

強参照

呼び出し元
強参照
使い終わったら
即解放

Blocksの実行者

Blocks
強参照
1度だけBlocksを使う場合は、
使った直後にちゃんと解放してや
ることでメモリリークを回避す
る!
使いまわすBLOCKSパターン
画面上のボタンが押された時の
コールバック処理をBlocksで実
行する場合など。
MultiTimeBlocksRunner *multiTimeBlocksRunner =
[[[MultiTimeBlocksRunner alloc] init] autorelease];
[multiTimeBlocksRunner setRunnable:^(id target){
NSLog(@"multiTimeBlocksRunner: %@", target);
} target:multiTimeBlocksRunner];
[multiTimeBlocksRunner run];
[multiTimeBlocksRunner run];
[multiTimeBlocksRunner run];
循環参照している!
これをメモリリークさせないため
には・・・
@property (nonatomic, copy) void(^runnable)(id target);
@property (nonatomic, assign) id target;

強参照

呼び出し元
弱参照

Blocksの実行者

Blocks
強参照
この場合ばかりは、オブジェクト
を弱参照で保持できるオプション
を用意してメモリリークを回避す
る。
まとめ
Blocksを利用する際の循環参照
によるメモリリークを回避するた
めに、無差別に弱参照を使わない。
できるだけ使い手側にメモリ管理
を意識させないようにするために、
Blocksを保持する側が解放の責
任をもつ方が良い。
最後に
株式会社シロクでは、スマホアプリの開
発・運用支援ツールを続々提供していま
す!

プッシュ通知配信・解析プラットフォーム
Growth Push

ユーザー獲得・継続のための共通ポイント基盤
Growth Point

アプリのテストを効率化するツール
Growth Debug

ユーザビリティテストのための新しいツール
Growth ??? (近日公開)
 Objective-CのBlocksの循環参照に関する僕なりのベストプラクティス

More Related Content

Objective-CのBlocksの循環参照に関する僕なりのベストプラクティス