如何利用Objective C寫乙個精美的DSL

2021-08-08 18:32:42 字數 3459 閱讀 9232

在程式開發中,我們總是希望能夠更加簡潔、更加語義化地去表達自己的邏輯,鏈式呼叫是一種常見的處理方式。我們常用的 masonry、 expecta 等第三方庫就採用了這種處理方式。

像這種用於特定領域的表達方式,我們叫做 dsl (domain specific language),本文就介紹一下如何實現乙個鏈式呼叫的 dsl.

我們舉乙個具體的例子,比如我們用鏈式表示式來建立乙個 uiview,設定其 frame、backgroundcolor, 並新增至某個父 view。

對於最基本的 objective-c (在 ios4 block 出現之前),如果要實現鏈式呼叫,只能是這個樣子的:

鏈式呼叫可以用兩種方式來實現:

在返回值中使用屬性來儲存方法中的資訊

比如,masonry 中的.left .right .top .bottom等方法,呼叫時會返回乙個masconstraintmaker類的例項,裡面有left/right/top/bottom等屬性來儲存每次呼叫時的資訊;

再比如,expecta 中的方法.notto方法會返回乙個expexpect類的例項,裡面有個 bool 屬性self.negative來記錄是否呼叫了.notto

再比如,上例中的 .with 方法,我們可以直接return self;

使用 block 型別的屬性來接受引數

比如 masonry 中的.offset(15)方法,接收乙個 cgfloat 作為引數,可以在masconstraintmaker類中新增乙個 block 型別的屬性:

比如例子中的.position(x, y),可以給的某類中新增乙個屬性:

在呼叫.position(x, y)方法時,執行這個block,返回 viewmaker 的例項保證鏈式呼叫得以進行。

從語義層面上,需要界定哪些是助詞,哪些是需要接受引數的。為了保證鏈式呼叫能夠完成,需要考慮傳入什麼,返回什麼。

還是以上面的例子來講:positionsizebgcolor這些。

下面我們分別從四段來看,如何實現這樣乙個表示式:

(1) 賓語

在 alloca(uiview) 的語義中,我們確定了賓語是 a uiview。由於確定 uiview 是在 intoview 截止那時,所以我們需要建立乙個中間類來儲存所有的中間條件,這裡我們用 viewmaker 類。

另外我們可以注意到alloca是乙個函式,而uiview無法直接傳遞到這個函式中,語法就要變成alloca([uiview class])而失去了簡潔性。所以我們需要先定義乙個巨集來「吞」掉中括號和class這個方法:

(2) 助詞

很多時候,為了讓 dsl 的語法看起來更加連貫,我們需要一些助詞來幫助,例如 masonry 裡面的 make.top.equalto(superview.mas_top).with.offset(padding.top) 這句中的 with 就是這樣乙個助詞。

而這個助詞和我們學過的語法一樣,通常沒有什麼實際效果,簡單返回self就可以。

需要注意的是,返回自己,就沒有辦法阻止使用者不斷呼叫自己.with.with.with,為了避免這種情況,可以新生成乙個類,每個類都擁有自己所在層次的方法,避免躍層呼叫。

這樣就有效防止了,.with.with.with這樣的語法。但是實際上,我們要根據真實的需要來進行開發,使用 dsl 的使用者是為了更好的表達性,所以並不會寫出.with.with.with這樣的**,這樣的防護性措施就顯得有點不必要了。

不過使用類來區分助詞還有另外幾個小好處,就是它可以確保在語法提示的時候,viewclasshelper這個類只有.with這樣乙個語法提示,而viewmaker不出現.with語法提示;並且同時確保.with一定要出現。

(3) 修飾部分——定語

像例子中的positionsizebgcolor這些都是定語部分,用來修飾uiview,他們以屬性的形勢存在於viewmaker的例項中,為了支援鏈式表達,所以實現的時候,都會繼續返回self

我們來試著實現下:

(4) 終結詞

「終結詞」這個實在是在現代語法裡面找不到對應關係了,但是在 dsl 中,這一段尤為重要。viewmaker的例項從頭至尾收集了很多的修飾,需要最後的乙個表達詞語來產生最後的結果,這裡就稱為」終結詞」。例如在 expecta 這個開源庫裡面的equal就是把真正的行為表現出來的時候,tonotto都不會真正觸發行為。

在我們的例子裡,終結詞.intoview(asuperviwe)可以這樣實現:

這樣,乙個終結詞就寫好了。

最終**的彙總:

這種鏈式呼叫能夠使程式更加清晰,在特定場景下使程式的可讀性更強。這種手段在swift也是相同道理,大家可以善加利用,讓自己的**更加美觀。

攻擊者是如何利用「隱寫技術」的?

據稱,在不久的將來,隱寫技術將越來越多地用於攻擊中。那麼,攻擊者如何使用隱寫術,以及企業應該如何抵禦基於隱寫術的攻擊?nick lewis 隱寫術是指在普通郵件中隱藏秘密訊息的技術。通過使用隱寫術 例如加密技術 攻擊者可儘量減少他們的攻擊被發現的機會。但與加密一樣,有效使用隱寫技術也需要正確的部署。...

python函式如何寫 python如何寫函式

python函式的定義 定義函式,也就是建立乙個函式,可以理解為建立乙個具有某些用途的工具。定義函式需要用 def 關鍵字實現,具體的語法格式如下 def 函式名 形參列表 由零條到多條可執行語句組成的 塊 return 返回值 其中,用 括起來的為可選擇部分,即可以使用,也可以省略。此格式中,各部...

python如何寫日誌 python如何寫日誌

這篇文章介紹了 python 的 logging 模組,包括它的設計以及針對更多複雜案例的適用方法。這篇文章不是寫給開發者的文件,它更像是乙個指導手冊,來說明 python 的 logging 模板是如何搭建的,並且激發感興趣的人深入研究。為什麼使用 logging 模組?也許會有開發者會問,為什麼...