底層結(jié)構(gòu)

作者: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ù)能做的操作:比較(earlierDatelaterDate)、判斷相等以及計算時間間隔(依舊返回一個浮點數(shù))。distantFuturedistantPast 也是顯而易見的,它們是以面向未來和過去兩個維度來計算出期望的時間(浮點數(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。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容