39.用handler塊降低代碼分散程度

《編寫高質量iOS與OS X代碼的52個有效方法》--第六章 第39條
(ps:此乃讀書筆記,加深記憶,僅供大家參考)


第39條:用handler塊降低代碼分散程度

為用戶界面編碼時,一種常用的范式就是“異步執(zhí)行任務”(perform task asynchro nously)。這種范式的好處在于:處理用戶界面的顯示及觸摸操作所用的線程,不會因為要執(zhí)行I/O或網絡通信這類耗時的任務而阻塞。這個線程通常稱為主線程(main thread)。某些情況下,如果應用程序在一定時間內無響應,那么就會自動終止?!跋到y(tǒng)監(jiān)控器”(system watchdog)在發(fā)現(xiàn)某個應用程序的主線程已經阻塞了一段時間之后,就會令其終止。

異步方法在執(zhí)行完任務之后,需要以某種手段通知相關代碼。實現(xiàn)此功能有很多辦法。常用的技巧是設計一個委托協(xié)議(參見第23條),令關注此事的對象遵從該協(xié)議。對象成為delegate之后,就可以在相關事件發(fā)生時(例如某個異步任務執(zhí)行完畢時)得到通知了。

如果改用塊來寫的話,代碼會更清晰。塊可以令這種API變得更緊致,同時也令開發(fā)者調用起來更加方便。

typedef void (^EOCNetworkFetcherCompletionHandler)(NSData *data);

@interface EOCNetworkFetcher : NSObject
- (id)initWithURL:(NSURL *)url;
- (void)startWithCompletionHandler: (EOCNetworkFetcherCompletionHandler)handler;
@end

這和使用委托協(xié)議很像,不過多了個好處,就是可以在調用start方法時直接以內聯(lián)形式定義completion handler,以此方式來使用“網絡數(shù)據(jù)獲取器”(network fetcher),可以令代碼比原先易懂很多。

與使用委托模式的代碼相比,用塊寫出來的代碼閑的更為整潔。委托模式有個缺點:如果類要分別使用多個獲取器下載不同的數(shù)據(jù),那么就得在delegate回調方法里根據(jù)傳入的獲取器參數(shù)來切換。

異步執(zhí)行任務完畢后所需運行的業(yè)務邏輯,和啟動異步任務所用的代碼放在了一起。無須保存獲取器,也無需再回調方法里切換,每個completion handler的業(yè)務邏輯,都是和相關獲取器對象一起來定義的。

NSURL *url = [[NSURL alloc] initWithString:@"http:www.baidu.com"];
EOCNetworkFetcher *fetcher = [[EOCNetworkFetcher alloc] initWithURL:url];
[fetcher startWithCompletionHandler:^(NSData *data) {
    _fetchedFooData = data;
}];

這種寫法還有其他用途,比如,現(xiàn)在很多基于塊的API都使用塊來處理錯誤??梢苑謩e用兩個處理器來處理操作失敗的情況和操作成功的情況。也可以把處理失敗情況所需的代碼,與處理正常情況所用的代碼,都封裝到同一個completion handler塊里。

采用兩個獨立的處理程序:

typedef void (^EOCNetworkFetcherCompletionHandler)(NSData *data);
typedef void (^EOCNetworkFetcherErrorHandler)(NSError *error);

@interface EOCNetworkFetcher : NSObject
- (id)initWithURL:(NSURL *)url;
- (void)startWithCompletionHandler:(EOCNetworkFetcherCompletionHandler)completion failureHandler:(EOCNetworkFetcherErrorHandler)failure;
@end

這種API設計風格很好,由于成功和失敗的情況要分別處理,所以調用此API的代碼也就會按照邏輯,把應對成功和失敗情況的代碼分開來寫,這將令代碼更易讀懂。

把處理成功情況和失敗情況所用的代碼全放在一個塊里:

typedef void (^EOCNetworkFetcherCompletionHandler)(NSData *data, NSError *error);

@interface EOCNetworkFetcher : NSObject
- (id)initWithURL:(NSURL *)url;
- (void)startWithCompletionHandler:(EOCNetworkFetcherCompletionHandler)completion;
@end

此種API調用方式如下:

EOCNetworkFetcher *fetcher = [[EOCNetworkFetcher alloc] initWithURL:url];
[fetcher startWithCompletionHandler:^(NSData *data, NSError *error) {
    if (error) {
        //Handler failure
    } else {
        //Handler success
    }
}];

這種方法需要在塊代碼中檢測傳入的error變量,并且要把所有邏輯代碼都放在一處。這種寫法的缺點是:由于全部邏輯都寫在一起,所以會令塊變得比較長,且比較復雜。然而只用一個塊的寫法也有好處,那就是更為靈活。

把成功情況和失敗情況放在同一個塊中,還有個優(yōu)點:調用API的代碼可能會在處理成功相應的過程中發(fā)現(xiàn)錯誤。比方說,返回的數(shù)據(jù)可能太短了。這種情況需要和網絡數(shù)據(jù)獲取器所認定的失敗情況按同一方式處理。此時,如果采用單一塊的寫法,那么就能把這種情況和所認定的失敗情況統(tǒng)一處理了。

總體來說,筆者建議使用同一塊來處理成功與失敗的情況,蘋果公司似乎也是這樣設計其API的。例如,Twitter框架中的TWRequest及MapKit框架中的MKLocalSearch都只是用一個handler塊。

有時需要在相關時間點執(zhí)行回調操作,這種情況也可以使用handler塊。比方說,調用網絡數(shù)據(jù)獲取器的代碼,也許想在每次有下載進度時都得到通知。這可以通過委托模式實現(xiàn)。不過也可以使用本節(jié)的handler塊,把處理下載進度的handler定義成塊類型,并新增一個此類型的屬性:

typedef void (^EOCNetworkFetcheProgressHandler)(float progress);

@property (nonatomic, copy) EOCNetworkFetcheProgressHandler progressHandler;

這種寫法很好,因為它還是能把所有業(yè)務邏輯都放在一起,也就是把創(chuàng)建網絡數(shù)據(jù)獲取器和定義progress handler所用的代碼寫在一處。

基于handler來設計API還有個原因,就是某些代碼必須運行在特定的線程上。因此,最好能由調用API的人來決定handler應該運行在那個線程上。NSNotificationCenter就屬于這種API,它提供了一個方法,調用者可以經由此方法來注冊想要接收的通知,等到相關事件發(fā)生時,通知中心就會執(zhí)行注冊好的那個塊。調用者可以指定某個塊應該安排在哪個執(zhí)行隊列里,然而這不是必需的。若沒有指定隊列,按默認方式執(zhí)行。

- (id <NSObject>)addObserverForName:(nullable NSString *)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block

要點

  • 在創(chuàng)建對象時,可以使用內聯(lián)的handler塊將相關業(yè)務邏輯一并聲明。
  • 在有多個實例需要監(jiān)控時,如果采用委托模式,那么經常需要根據(jù)傳入的對象來切換,而若改用handler塊來實現(xiàn),則可直接將塊與相關對象放在一起。
  • 設計API時如果用到了handler塊,那么可以增加一個參數(shù),使調用者可通過此參數(shù)來決定應該把塊安排在哪個隊列上執(zhí)行。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評論 6 538
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,034評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,413評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,449評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,165評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,559評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,781評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 49,327評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,084評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,278評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,495評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,927評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,172評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,010評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,241評論 2 375

推薦閱讀更多精彩內容

  • 用 handler 塊 降低代碼分散程度 為用戶界面編碼時, 一種常見的范式是 '異步執(zhí)行任務', 這種范式的好處...
    dingzhijie閱讀 601評論 0 1
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,807評論 18 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,692評論 25 708
  • 有時候,想想生活,其實也不過如此。不在世間營營,就已經知足。閑時就去遠方,看看生命和死亡的詩;縱使忙碌也不對生活...
    胡然乎閱讀 165評論 0 0
  • 平凡的日子里 忽然有一天 千里之外 有個人對你說 我想你 那么 我很慶幸 我在你的世界里存在過 我也很慶幸 我會被...
    YIERLHY閱讀 272評論 0 0