NSHashTableでDelegatesパターン

31
NSHashTable Delegates

Transcript of NSHashTableでDelegatesパターン

Page 1: NSHashTableでDelegatesパターン

NSHashTableでDelegatesパターン

Page 2: NSHashTableでDelegatesパターン

自己紹介

@starfruits_j (Little Gleam)

Page 3: NSHashTableでDelegatesパターン

株式会社 Azione

株式会社 Azione のiOSアプリ開発、管理職

昨年末にSQLを書くことが嫌いということでNyaruDBと

Realmについて発表しました

PHPではlaravelがお気に入り

Page 4: NSHashTableでDelegatesパターン

開発実績デコメーラー

Nator

モバスペブック

© 2014 Azione Co.,Ltd. All Right Reserved.

Page 5: NSHashTableでDelegatesパターン

個人でもアプリ出してますQRコードリーダー

FF10 モンスター捕獲数カウンター

hackadl

WebPage翻訳 for Safari

Page 6: NSHashTableでDelegatesパターン

発表内容Swift!?

NSHashTable

使い道について

Page 7: NSHashTableでDelegatesパターン

Objective-Cが好きnilをスキップできるところ

weak参照(ARC)があるところ

メソッド名が長いところ

Page 8: NSHashTableでDelegatesパターン

NSHashTableという存在

Page 9: NSHashTableでDelegatesパターン

使い方NSHashTable * hashTable = [[NSHashTable alloc] init];[hashTable addObject:@"test"];[hashTable addObject:@"obj"];[hashTable removeObject:@"obj"];NSLog(@"table: %@", [hashTable allObjects]);

NSArrayというよりNSSetを拡張したような感じ

ユニークなコレクション

Page 10: NSHashTableでDelegatesパターン

optionsenum { // default is strong NSPointerFunctionsStrongMemory NS_ENUM_AVAILABLE(10_5, 6_0) = (0UL << 0), NSPointerFunctionsOpaqueMemory NS_ENUM_AVAILABLE(10_5, 6_0) = (2UL << 0), NSPointerFunctionsMallocMemory NS_ENUM_AVAILABLE(10_5, 6_0) = (3UL << 0), NSPointerFunctionsMachVirtualMemory NS_ENUM_AVAILABLE(10_5, 6_0) = (4UL << 0), NSPointerFunctionsWeakMemory NS_ENUM_AVAILABLE(10_8, 6_0) = (5UL << 0),

NSPointerFunctionsObjectPersonality NS_ENUM_AVAILABLE(10_5, 6_0) = (0UL << 8), NSPointerFunctionsOpaquePersonality NS_ENUM_AVAILABLE(10_5, 6_0) = (1UL << 8), NSPointerFunctionsObjectPointerPersonality NS_ENUM_AVAILABLE(10_5, 6_0) = (2UL << 8), NSPointerFunctionsCStringPersonality NS_ENUM_AVAILABLE(10_5, 6_0) = (3UL << 8), NSPointerFunctionsStructPersonality NS_ENUM_AVAILABLE(10_5, 6_0) = (4UL << 8), NSPointerFunctionsIntegerPersonality NS_ENUM_AVAILABLE(10_5, 6_0) = (5UL << 8),

NSPointerFunctionsCopyIn NS_ENUM_AVAILABLE(10_5, 6_0) = (1UL << 16),};

defaultではhashとisEqual:を使って比較

Page 11: NSHashTableでDelegatesパターン

速度比較10,000個のUUIDをaddObject

class timeNSArray 0.055016NSSet 0.023589NSHashTable 0.022918

Page 12: NSHashTableでDelegatesパターン

速度比較ArrayにcontainsObject:を追加

class timeNSArray 3.670467NSSet 0.021289NSHashTable 0.021819

Page 13: NSHashTableでDelegatesパターン

真意はNSPointerFunctionsWeakMemo

ry[NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory];[NSHashTable weakObjectsHashTable];

Page 14: NSHashTableでDelegatesパターン

Objectが破棄されると自動で削除

NSInteger max = 1000; @autoreleasepool { for (NSInteger i = 0; i < max; i++) { NSString *str = [NSUUID UUID].UUIDString; [ht addObject:str]; NSLog(@"count %ld", ht.allObjects.count); } }

NSLog(@"count %ld %@", ht.allObjects.count, ht.allObjects);

countは反映されないので、allObjects.countが良いかと

Page 15: NSHashTableでDelegatesパターン

何に使おかUIScrollView.delegate

UIWebView.delegate

大人気!でもdelegateは1対1

1対多のパターンが欲しい

Page 16: NSHashTableでDelegatesパターン

NJKScrollProxyパターン?_scrollProxy = [[NJKScrollFullScreen alloc] initWithForwardTarget:self]; // UIScrollViewDelegate andself.tableView.delegate = (id)_scrollProxy; // cast for surpress incompatible warnings_scrollProxy.delegate = self;

delegateは1対1の通知パターンなので仕方ない

Page 17: NSHashTableでDelegatesパターン

1対多と言えばKVOcontentOffsetをKVOで監視したり

ドラッグのスピード等は独自実装しなければならない

KVOはおそらくassignで保持してるので、removeObserver:

忘れるとクラッシュする

Page 18: NSHashTableでDelegatesパターン

そこでDelegatesパターン- (void)addDelegate:(id <NantokaDelegate>)delegate;- (void)removeDelegate:(id <NantokaDelegate>)delegate;- (void)removeAllDelegates;

Page 19: NSHashTableでDelegatesパターン

addDelegate - NSArray- (void)addDelegate:(id <NantokaDelegate>)delegate { if ([self.delegates containsObject:delegate]) { return; } [self.delegates addObject:delegate];}

同じ通知を複数回 投げる必要はないのでcontainsObjectす

Page 20: NSHashTableでDelegatesパターン

addDelegate - NSHashTable- (void)addDelegate:(id <NantokaDelegate>)delegate { [self.delegates addObject:delegate];}

スッキリ!

Page 21: NSHashTableでDelegatesパターン

登録されたdelegatesに通知- (void)scrollViewDidScroll:(UIScrollView *)scrollView { for (id <UIScrollViewDelegate> delegate in self.delegates) { if ([delegate conformsToProtocol:@protocol(UIScrollViewDelegate)]) { if ([delegate respondsToSelector:@selector(scrollViewDidScroll:)]) { [delegate scrollViewDidScroll:scrollView]; } } }}

Page 22: NSHashTableでDelegatesパターン

DegatesパターンにNSHashTableを使うと幸せに

なる理由delegatesをNSArrayで管理するとretainされる

delegateは基本的にasign < weak参照が良い

deallocでdelegate = nilする場合等、循環参照の問題

assignの時は非同期通信等、通信完了前にdelegateが破棄

された場合にクラッシュ

dealloc等でdelegate = nil する必要があった

weakなら何も起こらず安心

Page 23: NSHashTableでDelegatesパターン

NSHashTableならweakで管理できるので解放いらない

Page 24: NSHashTableでDelegatesパターン

delegatesパターンってあるの?

聞いたことないです

議論はちらほらあって、権限を移譲できる人が沢山いるの

は良くないとか

実はAppleが採用してたりとか?

Page 25: NSHashTableでDelegatesパターン

とりあえずプロジェクト内を

検索してみた

Page 26: NSHashTableでDelegatesパターン

あった!AFURLSessionManager

- (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask progress:(NSProgress * __autoreleasing *)progress destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler;- (void)removeAllDelegates;

delegatesはNSMutableDictionaryでした。

Page 27: NSHashTableでDelegatesパターン

weak参照の通知と言えばRealmの更新通知

_token = [[RLMRealm defaultRealm] addNotificationBlock:^(NSString *notification, RLMRealm *realm) }];

_tokenを解放すると監視外れるので、weak参照でblockを保

delegatesではないけど、blockを何かにaddして保持してい

Page 28: NSHashTableでDelegatesパターン

あった!NSHashTable *_notificationHandlers

Page 29: NSHashTableでDelegatesパターン

こんなケースにも使えそうMemoryManager的な

+ (instansetype)sharedManager;- (void)addViewController:(UIViewController *)vc;- (NSArray *)leakedViewControllers;

- (NSArray *)leakedViewControllers { NSMutableArray *r = [@[] mutableCopy]; for (UIViewController *vc in self.hashTable.allObjects) { if (??? vc.parentViewController == nil ????) { [r addObject:vc]; } } return r;}

Leaks繋がなくてもデバッグできそう!

Page 30: NSHashTableでDelegatesパターン

NSHashTableでDelegates まとめ

ユニークなコレクションを作る場合、速度はNSArrayより

高速、NSSetと同じ

NSHashTableを利用したdelegatesパターンならKVOの

removeObserver忘れのようなことも気にしないでよい

NSHashTableの使いどころ

KVO以外の方法で1対多の通知を実現したい場合

非同期処理のOperation管理

リークチェック等にも使えそう

Page 31: NSHashTableでDelegatesパターン

Which

http://which.photos

Whichというアプリをリリースします

larabel, RealmでSQL書かずに実装しました

もちろんdelegatesパターン使いました