一般在這種情況下,我喜歡問我自己:“如果問題交給Apple,他會怎么做呢?”當然,我們都知道Apple肯定知道怎么做,因為從某一層面上看,Apple的文檔就是一本用來指導我們如何使用設計模式的指導書。
因此我們需要去研究一下Apple分別是在什么情況下使用delegate和block,如果我們發現了Apple做這種選擇的套路,我們就可以構建出一些規則,可以幫助在我們在自己的代碼中做相同選擇。
要找出Apple使用delegate的場景很簡單,我們只要搜索官方文檔中的“delegate”,就會獲取到很多使用delegation的類。
但是搜索Apple中有關使用blocks的文檔就有點困難了,因為我們不能直接搜索文檔中的“^” 。然而,Apple聲明方法時有很好的命名習慣(這也是我們精通iOS開發的一項必備技能)。例如:一個以NSString為參數的方法,方法的selector就會有String字眼,像initWithString;dateFromString;StartSpeaingString。
當Apple的方法使用block,這個方法將會有“Handler”,“Completion”或者簡單的“Block”作為selector;因此我們可以在標準的iOS API文檔中搜索這些關鍵詞,用以構建一個可信任的block用例列表。
1.大多數delegate protocols 都擁有幾個消息源。
以我正在看的GKMatch為例(A GKMatch object provides a peer-to-peer network between a group of devices that are connected to Game Center,是iOS API中用來提供一組設備連接到Game Center點對點網絡的對象)。從這個類中可以看到消息的來源分別是:當從其他玩家那接收到數據、當玩家切換了狀態、當發生錯誤或者當一個玩家應該被重新邀請。這些都是不同的事件。如果Apple在這里使用block,那么可能會有以下兩種解決方式:
可以對應每一個事件注冊相應的block,顯然這種方式是不合理的。( If someone writes a class that does this in Objective-C, they are probably an asshole.)
創建一個可以接受任何可能輸入的block
1
void (^matchBlock)(GKMatchEvent eventType, Player *player, NSData *data, NSError *err);
很明顯這種方式既不簡便又不易讀,所以你可能從未看過這樣的解決方案。如果你看過這樣的解決方式,但是這顯然是一個糟糕至極的代碼行,你不會有精力去維護這個。
因此,我們可以得出一個結論:如果對象有超過一個以上不同的事件源,使用delegation。
2.一個對象只能有一個delegate
由于一個對象只能有一個delegate,而且它只能與這個delegate通信。讓我們看看CLLocationManager 這個類,當發現地理位置后,location manager 只會通知一個對象(有且只有一個)。當然,如果我們需要更多的對象去知道這個更新,我們最好創建其他的location manager。
這里有的人可能想到,如果CLLocationManager是個單例呢?如果我們不能創建CLLocationManager的其他實例,就必須不斷地切換delegate指針到需要地理數據的對象上(或者創建一個只有你理解的精密的廣播系統)。因此,這樣看起來,delegatetion在單例上沒有多大意義。
關于這點,最好的印證例子就是UIAccelerometer。在早期版本的iOS中,單例的 accelerometer 實例有一個delegate,導致我們必須偶爾切換一下。這個愚蠢的問題在之后的IOS版本被修改了,現在,任意一個對象都可以訪問CMMotionManager block,而不需要阻止其他的對象來接收更新。
因此,我們可以得出另一個結論:“如果一個對象是單例,不要使用delegation”。
3.一般的delegate方法會有返回值
如果你觀察一些delegate方法(幾乎所有的dataSource方法)都有一個返回值。這就意味著delegating對象在請求某些東西的state(對象的值,或者對象本身),而一個block則可以合理地包含state或者至少是推斷state,因此block真正是對象的一個屬性。
新聞熱點
疑難解答