Location via proxy:
[ UP ]
[Report a bug]
[Manage cookies]
No cookies
No scripts
No ads
No referrer
Show this form
Submit Search
Objective-CのBlocksの循環参照に関する僕なりのベストプラクティス
•
18 likes
•
8,601 views
Naoyuki Kataoka
Follow
http://blog.katty.in/2612
Read less
Read more
1 of 76
More Related Content
Objective-CのBlocksの循環参照に関する僕なりのベストプラクティス
2.
自己紹介 • • • • 片岡 直之 株式会社シロク SNS: @katty0324 ブログ:
三度の飯とエレクトロン ( http://blog.katty.in/ )
3.
ある日の社内SNSにて・・・
5.
なんとなく
7.
とは言ったものの
8.
最後にiOSアプリを作っていたの は半年以上前だったことを思い出 し・・・ ←アバターチャットアプリ 半年前にクローズ済み
9.
ネタがないことに気づく。
11.
自分のブログを漁ってみたところ
13.
良さそうなのがあった。
15.
というわけで本日のテーマ
16.
Objective-CのBlocksの循環参 照に関する僕なりのベストプラク ティス
17.
その前に
18.
Delegateとは?
19.
Objective-C流のコールバック 手法
20.
Blocksとは?
21.
Objective-C流のモダンなコー ルバック手法
22.
参照カウントとは?
23.
オブジェクトの解放管理のための 数値変数
24.
Blocksの循環参照とは?
25.
オブジェクトがBlocksを参照して いて、そのBlocks内でそのオブ ジェクトを参照している状態 強参照 呼び出し元 強参照 Blocksの実行者 Blocks 強参照
26.
BLOCKSの使い方
27.
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
28.
Blocksで処理を保持しておけば、 必要になった時に実行できる。
29.
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>
30.
Blocksが循環参照してしまうと、 オブジェクトは解放されない。
31.
ではどうするか?
32.
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
33.
Blocks内に渡されたオブジェクト は参照カウントが増えてしまうの で、弱参照の修飾子を添えて渡す。 強参照 呼び出し元 弱参照 Blocksの実行者 Blocks 強参照
34.
というのは結構スタンダードな手 法ですが
35.
僕はweakSelfパターンと呼んで 勝手にアンチパターン扱いしてい ます。
36.
WEAKSELFパターンの弱点
37.
__blockとか、__weakという修 飾子が汚い。
38.
Blocksを使うたびに、毎回変数 を確保しなければならない。
39.
オブジェクトを弱参照で渡すので、 オブジェクトの存在を保証できな い。
40.
Blocks構文の使い手が、中の実 装を意識しなくてはいけない。
41.
そもそも
42.
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; }
43.
こうやって使ってもちゃんとメモ リが解放されるのが理想であって
44.
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; }
45.
使い手が、メモリが解放されるよ うに仕向けなければならないのは 良くない。
46.
BLOCKSの使い方は3種類
47.
その場限りのBlocks
48.
1度きりのBlocks
49.
使いまわすBlocks
50.
その場限りのBLOCKS
51.
たとえば、jQueryにおける $.each(func)のような、同期的 に処理されるものなど。
52.
InstantBlocksRunner *instantBlocksRunner =
[[[InstantBlocksRunner alloc] init] autorelease]; [instantBlocksRunner run: ^{ NSLog(@"instantBlocksRunner: %@", instantBlocksRunner); }];
53.
循環参照している!
54.
これをメモリリークさせないため には・・・
55.
- (void)run:(void (^)(void))runnable
{ if(runnable) runnable(); } 強参照 呼び出し元 強参照 Blocksの実行者 Blocks 弱参照
56.
その場限りのBlocksは、Blocks を保持しないことで、メモリリー クを回避する!
57.
1度きりのBLOCKS
58.
非同期的にHTTP通信しその結果 をBlocksでコールバックするよ うな場合など。
59.
OneTimeBlocksRunner *oneTimeBlocksRunner =
[[[OneTimeBlocksRunner alloc] init] autorelease]; oneTimeBlocksRunner.runnable = ^{ NSLog(@"oneTimeBlocksRunner: %@", oneTimeBlocksRunner); }; [oneTimeBlocksRunner run];
60.
循環参照している!
61.
これをメモリリークさせないため には・・・
62.
- (void)run { if(runnable) runnable(); self.runnable
= nil; } 強参照 呼び出し元 強参照 使い終わったら 即解放 Blocksの実行者 Blocks 強参照
63.
1度だけBlocksを使う場合は、 使った直後にちゃんと解放してや ることでメモリリークを回避す る!
64.
使いまわすBLOCKSパターン
65.
画面上のボタンが押された時の コールバック処理をBlocksで実 行する場合など。
66.
MultiTimeBlocksRunner *multiTimeBlocksRunner = [[[MultiTimeBlocksRunner
alloc] init] autorelease]; [multiTimeBlocksRunner setRunnable:^(id target){ NSLog(@"multiTimeBlocksRunner: %@", target); } target:multiTimeBlocksRunner]; [multiTimeBlocksRunner run]; [multiTimeBlocksRunner run]; [multiTimeBlocksRunner run];
67.
循環参照している!
68.
これをメモリリークさせないため には・・・
69.
@property (nonatomic, copy)
void(^runnable)(id target); @property (nonatomic, assign) id target; 強参照 呼び出し元 弱参照 Blocksの実行者 Blocks 強参照
70.
この場合ばかりは、オブジェクト を弱参照で保持できるオプション を用意してメモリリークを回避す る。
71.
まとめ
72.
Blocksを利用する際の循環参照 によるメモリリークを回避するた めに、無差別に弱参照を使わない。
73.
できるだけ使い手側にメモリ管理 を意識させないようにするために、 Blocksを保持する側が解放の責 任をもつ方が良い。
74.
最後に
75.
株式会社シロクでは、スマホアプリの開 発・運用支援ツールを続々提供していま す! プッシュ通知配信・解析プラットフォーム Growth Push ユーザー獲得・継続のための共通ポイント基盤 Growth Point アプリのテストを効率化するツール Growth
Debug ユーザビリティテストのための新しいツール Growth ??? (近日公開)