作者:Soroush Khanlou,原文鏈接,原文日期:2017-01-12
譯者:Cwift;校對:walkingway;定稿:CMB
我經(jīng)常觀察一個類型的實例變量,這樣我就可以更深入地理解這個類型設計的初衷。一旦你知曉該類型的底層結(jié)構(gòu),它的用法也就隨之浮出水面了。反之亦然:如果你沒看過一個對象內(nèi)部成員的布局情況,那么不可能準確把握該對象的功能。這種情況對于蘋果的閉源類型尤其明顯。
一個很好的例子是 NSDate 類型。當我開始編程時,就試著去了解如何使用 NSDate 以及它所有的兄弟對象,比如 NSDateComponents、NSDateFormatter 以及 NSCalendar,那真是一段艱難的歲月。為什么你需要使用 NSCalendar 在原本的日期上增加兩天?這些類之間的邊界劃分讓人捉摸不定,這使得當你想要尋找某些特定的功能時,無法準確定位到某個具體的對象中。
對我來說,關鍵的啟示是理解 NSDate 在底層的真實面目,是什么原因使得部分功能散落到其他類中。NSDate 只是一個花哨的包裝器。僅此而已,文檔也揭示了這一事實:
NSDate對象封裝了單個的時間點,獨立于任何特定的日歷系統(tǒng)或者時區(qū)。
所有的 NSDate 都存儲了一個浮點數(shù),這個浮點數(shù)代表了從 2001 年 1 月 1 日 00:00:00 UTC 起的秒數(shù)。這些秒數(shù)與時區(qū)、星期幾、月份、夏令時、閏秒或者閏年一點關系都沒有。如果所需的計算基于秒數(shù)就可以完成,那么它會在 NSDate 上進行。否則,就要借助其他的類型了。
列舉一下單純依靠這個浮點數(shù)能做的操作:比較(earlierDate,laterDate)、判斷相等以及計算時間間隔(依舊返回一個浮點數(shù))。distantFuture 和 distantPast 也是顯而易見的,它們是以面向未來和過去兩個維度來計算出期望的時間(浮點數(shù)表示)。
對于其他功能,你必須使用其他的類和對象。例如,要及時地向一個時刻中增加一天的時間,可以選擇向一個 NSDate 中增加 24*60*60 秒的方式,不過最好的方式是通過 NSCalendar 來操作,避免遇到夏令時間、閏秒/天的問題 ,以及其他可能隨時間出現(xiàn)的非標準問題。這篇博客介紹了使用 NSCalendar 進行這些計算的情況。
因為 NSDate 不存儲日期中與我們所期望的月份相關的任何信息,如果要更改該月份,則必須使用一個了解情況并且能夠把日期拆分成各個“組件”的對象。為此,我們要用到 NSDateComponents,看看你能否弄懂它內(nèi)部存儲數(shù)據(jù)的方式。
當我在編寫我自己的 Promise 庫時,發(fā)現(xiàn)了另一個有趣的例子,通過研究一個對象存儲屬性的布局來了解該對象的性質(zhì)。如果查看每個 Promise 對象的存儲屬性,你會看到三樣東西:
public final class Promise<Value> {
private var state: State<Value>
private let lockQueue = DispatchQueue(label: "promise_lock_queue", qos: .userInitiated)
private var callbacks: [Callback<Value>] = []
每個 promise 都有它的當前狀態(tài)(如 .pending,.fulfilled 或者 .rejected),一個確保線程安全的隊列,以及當 promise 完成時或者被拒絕時調(diào)用的回調(diào)數(shù)組。
當我寫完這個庫時,看了一些 Signal/Observable 的實現(xiàn),看看能否理解它們。我發(fā)現(xiàn) JensRavens/Interstellar 的實現(xiàn)是最直接的。我查看了這個庫中每個 Signal 對象的實例屬性,發(fā)現(xiàn)了一個非常相似的結(jié)構(gòu):
public final class Signal<T> {
private var value: Result<T>?
private var callbacks: [Result<T> -> Void] = []
private let mutex = Mutex()
它包含了存儲當前狀態(tài)的部分,存儲回調(diào)的部分以及存儲線程安全原語的部分。順序有些不同,他們使用了互斥體而不是隊列,但原理是相同的。這兩種類型之間的唯一區(qū)別是語義上的:promises 可以在完成時清除它們的回調(diào)(釋放自己以及捕獲的變量),而 signals 必須保持它們的回調(diào)。
我認為這個原則也可以幫助設計自己的類型??纯茨阏谔幚淼膶ο蟮膶傩浴C恳粋€屬性都有目的性嗎?它是否對該對象的整體特性有幫助?是否有些屬性在該對象的一些實例中能夠用到,而在另一些實例中用不到?如果是的話,這些屬性可能屬于其他對象。確保類型的實例變量被嚴格控制并且充分利用,確保我們的每一個對象在應用中都有著明確的定位。
本文由 SwiftGG 翻譯組翻譯,已經(jīng)獲得作者翻譯授權,最新文章請訪問 http://swift.gg。