Attribute in Swift -Part 1


最近參加朋友舉辦的開發(fā)者聚會爷肝,我分享了 Swift 的特性,可以寫出更有 Swift 味道的代碼陆错,也更加規(guī)范灯抛。

解決的問題

通常來說,編程語言的特性出現(xiàn)的原因是為了解決開發(fā)中不愉快的問題音瓷。接下來要介紹的特性提供了關(guān)于聲明和類型的更多信息对嚼。

Format

和大多數(shù)情況一樣,特性的格式有帶/不帶參數(shù)兩個版本

  • 不帶參數(shù)版本
    @attribute name
  • 帶參數(shù)版本
    @attribute name(attribute arguments)
    接下來開始介紹各個特性了绳慎。

autoclosure - 自動封裝成無參數(shù)的閉包

被修飾的參數(shù)會自動帶上閉包屬性

//沒有使用 autoclosure猪半,調(diào)用函數(shù)顯得別扭
func printWhenTrue(pred: () -> Bool) {
     if (prod()) {
          print(“I am waiting for you.")
     }
}
func printWhenTrue{ 2 > 1 }

// 使用 autoclosure,調(diào)用函數(shù)自然得多
func printWhenTrue(prod: @autoclosure () -> Bool) {
     if (prod()) {
          print(“I am here.")
     }
}
func( 2 > 1 )

學(xué)會了之后偷线,思考在什么場景適合使用磨确。先看系統(tǒng)是如何使用的,看 fatalError 的定義声邦。

// Unconditionally print a `message` and stop execution.
@noreturn public func fatalError(
                       @autoclosure message: () -> String = default,   
                       file: StaticString = default,
                       line: UInt = default)

autoclosure 的用途是使調(diào)用變得簡單乏奥,試想一下,如果 message 參數(shù)是一個閉包{ xxx } 顯得多么的累贅亥曹〉肆耍總結(jié)出來是,autoclosure 讓調(diào)用者用的舒心媳瞪,在調(diào)用有可能變得更加簡單的場景下都考慮是否加上該特性骗炉,如斷言就十分合適。

noescape - 參數(shù)與函數(shù)的生命周期綁定

參數(shù)將不會被存儲用作后續(xù)的計算,其用來確保不會超出函數(shù)調(diào)用的生命周期蛇受。光看這段文字比較難理解句葵,通過代碼理解起來更加輕松。

//這是一個輔助理解的函數(shù),參數(shù) code 的生命周期和 doItMore 一樣
func doItMore(@noescape code: (Int) -> ()) {}

//參數(shù) code 和 doIt 的生命周期一樣
func doIt(@noescape code: () -> ()) {
    code()
    doItMore(code)
/* what we CANNOT do *****
// pass it as a non-`@noescape` parameter
dispatch_async(dispatch_get_main_queue(), code)
// store it
let _code:() -> () = code
// capture it in another non-`@noescape` closure
let __code = { code() }
*/
}

任何改變參數(shù) code 生命的行為都會被拒絕乍丈。code()沒有改變生命周期剂碴,doItMore(code)也沒有改變生命周期,因為函數(shù)doItMore(_:)的參數(shù)帶上了 noescape轻专,然而dispatch_async改變了生命周期忆矛,_code強引用參數(shù)code,也改變了生命周期请垛,__code也是如此催训。

因為參數(shù)的生命與函數(shù)綁定,因此不能是執(zhí)行異步操作的閉包宗收,其中一個適用的場景是瞳腌,開源庫規(guī)范使用者的使用方法,避免難以察覺的 bug镜雨。
值得一提的是,noescape 帶來了一個好處儿捧,不需要顯式的使用 self.xxx荚坞。

class Bar {
    var i = 0
    func some() {
        doIt {
            //并不需要寫成 print(self.i)
            print(i)
        }
    }
}

noreturn - 函數(shù)或方法就不會返回到它的調(diào)用者中去

系統(tǒng) API fatalErrorpreconditionFailure 使用了 noreturn,其定義如下菲盾。

@noreturn public func fatalError(
                  @autoclosure message: () -> String = default, 
                  file: StaticString = default, 
                  line: UInt = default)

@noreturn public func preconditionFailure(
                  @autoclosure message: () -> String = default,
                  file: StaticString = default, 
                  line: UInt = default)

調(diào)用 fatalError 后颓影,程序就會崩潰,打印出錯誤日志懒鉴,調(diào)用 preconditionFailure后诡挂,效果和 fatalError一樣。

這兩個 API 均改變了程序正常的執(zhí)行流程临谱,你的程序已經(jīng)失去控制了璃俗。這效果適用于程序錯誤處理,如斷言悉默,在不滿足條件可以讓程序崩潰城豁。同樣,你也可以自定義自己的函數(shù)抄课,在出錯打印出你關(guān)心的信息唱星。

對于一個沒有用 noreturn 特性標記的函數(shù)或方法,可以將它重寫為用該特性標記的。相反,對于一個已經(jīng)用 noreturn 特性標記的函數(shù)或方法,則不可以將它重寫為沒使用該特性標記的跟磨。借助這特性间聊,可以重寫某函數(shù),當在不該調(diào)用的地方調(diào)用時抵拘,直接讓程序崩潰哎榴。

inline - 內(nèi)聯(lián)函數(shù)

內(nèi)聯(lián)函數(shù)用得好,才能提高效率。通常來說叹话,簡短的函數(shù)應(yīng)當使用內(nèi)聯(lián)函數(shù)偷遗,冗長的函數(shù)不適宜用內(nèi)聯(lián)函數(shù),不長不短的函數(shù)由系統(tǒng)決定驼壶,不要干預(yù)就好氏豌。Swift 也是這樣的原則,用inline帶上參數(shù)的形式顯式指定是否內(nèi)聯(lián)热凹。

// @inline(never) 顯式指定永遠不實用內(nèi)聯(lián)函數(shù)
@inline(never) func randomInt() -> Int{
    return Int(arc4random_uniform(UInt32.max))
}

// @inline(__always) 顯式指定永遠使用內(nèi)聯(lián)函數(shù)
@inline(__always) func randomInt() -> Int{
    return Int(arc4random_uniform(UInt32.max))
}

//有時候是內(nèi)聯(lián)泵喘,有時候又不是,有系統(tǒng)決定
func randomInt() -> Int{
    return Int(arc4random_uniform(UInt32.max))
}

inline 是一個小技巧般妙,簡短的內(nèi)聯(lián)函數(shù)不見得高效非常多纪铺,感覺這個特性的最佳使用場景是提高代碼的逼格

NSCopying - 修飾類的存儲型變量屬性

使屬性的setter與屬性值的一個副本合成,這個值由copyWithZone(_:)方法返回,而不是屬性本身的值碟渺。但是鲜锚,該屬性的類型必需遵循 NSCopying協(xié)議。在 Swift 中苫拍,有引用類型和值類型芜繁,NSCopying 只適用于引用類型,而值類型是自動復(fù)制的绒极。

熟悉 Objective-C 朋友骏令,應(yīng)該知道 @property (nonatomic, copy) NSString *myString 的涵義,給 myString賦值時垄提,會拷貝一份榔袋,他們的內(nèi)存地址并不相同。在 Swift 中铡俐,NSCopying 就是做這樣的事情凰兑。學(xué)習(xí)最好不要眼高手低,寫一段代碼熟悉特性审丘。

// 基礎(chǔ)類聪黎,輔助理解
class Foo : NSObject, NSCopying {
    var bar = "bar"
    // 只有遵循 NSCopying 協(xié)議的屬性才可被 NSCopying 修飾
    func copyWithZone(zone: NSZone) -> AnyObject {
        let copy = Foo()
        copy.bar = bar
        return copy
    }
}
class Test : NSObject {
    // NSCopying 修飾 foo
    @NSCopying  var foo : Foo?
    convenience override init() {
        self.init(foo: nil)
    }
    init(foo : Foo?) {
        self.foo = foo
        super.init()
    }
}

接下來看 NSCopying 如何發(fā)揮作用。

  • 只有在初始化方法被調(diào)用之后备恤,NSCopying 才會發(fā)揮作用稿饰,不然和普通的引用類型的屬性沒有區(qū)別,下面用兩段類似的代碼來說明露泊。
let foo = Foo()
foo.bar = "initial"
let test = Test(foo: foo)
print(foo === test.foo) // true
foo.bar = "changed"
print(test.foo!.bar) // "changed"
let foo = Foo()
foo.bar = "initial"
let test = Test()
test.foo = foo
print(foo === test.foo) // false
foo.bar = "changed"
print(test.foo!.bar) // "initial"
  • 訪問屬性時不會賦值一個值喉镰,再返回給調(diào)用者,用代碼驗證惭笑。
  let foo = Foo()
  foo.bar = "initial"
  let test = Test()
  test.foo = foo
  foo.bar = "changed"

  //在訪問屬性時侣姆,是沒有 copy
  let readFoo = test.foo!
  print(readFoo === test.foo!) // true
  readFoo.bar = "changed"
  print(test.foo!.bar) // "changed"

使用場景是想用引用類型生真,在合適的時候又能像值類型那樣深復(fù)制賦值。不過不建議這樣捺宗,會增加應(yīng)用的復(fù)雜度柱蟀,如非要這么做,請深入思考蚜厉。

寫在最后

覺得 Swift 的特性很有意思长已,也有很多,我會在 Part 2 繼續(xù)介紹昼牛。在學(xué)習(xí)這么細節(jié)的知識時术瓮,跑操場(Playground)是很有幫助的,建議讀者們也跑跑操場贰健,理解更加深刻胞四。

在分享過程中,我關(guān)注這些特性的應(yīng)用場景是什么伶椿,不僅要懂知識點辜伟,也要知道最佳使用模式。讀者在閱讀時候如果想到更多的應(yīng)用場景脊另,請留下你的評論导狡,感謝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末尝蠕,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子载庭,更是在濱河造成了極大的恐慌看彼,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件囚聚,死亡現(xiàn)場離奇詭異靖榕,居然都是意外死亡,警方通過查閱死者的電腦和手機顽铸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門茁计,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谓松,你說我怎么就攤上這事星压。” “怎么了鬼譬?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵娜膘,是天一觀的道長。 經(jīng)常有香客問我优质,道長竣贪,這世上最難降的妖魔是什么军洼? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮演怎,結(jié)果婚禮上匕争,老公的妹妹穿的比我還像新娘。我一直安慰自己爷耀,他們只是感情好甘桑,可當我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著畏纲,像睡著了一般扇住。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上盗胀,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天艘蹋,我揣著相機與錄音,去河邊找鬼票灰。 笑死女阀,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的屑迂。 我是一名探鬼主播浸策,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼惹盼!你這毒婦竟也來了庸汗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤手报,失蹤者是張志新(化名)和其女友劉穎蚯舱,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體掩蛤,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡枉昏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了揍鸟。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片兄裂。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖阳藻,靈堂內(nèi)的尸體忽然破棺而出晰奖,到底是詐尸還是另有隱情,我是刑警寧澤腥泥,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布畅涂,位于F島的核電站,受9級特大地震影響道川,放射性物質(zhì)發(fā)生泄漏午衰。R本人自食惡果不足惜立宜,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望臊岸。 院中可真熱鬧橙数,春花似錦、人聲如沸帅戒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逻住。三九已至钟哥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瞎访,已是汗流浹背腻贰。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留扒秸,地道東北人播演。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像伴奥,于是被迫代替她去往敵國和親写烤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫拾徙、插件洲炊、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,059評論 4 62
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young閱讀 3,783評論 1 10
  • 86.復(fù)合 Cases 共享相同代碼塊的多個switch 分支 分支可以合并, 寫在分支后用逗號分開。如果任何模式...
    無灃閱讀 1,354評論 1 5
  • 1尼啡、范型范型所解決的問題 函數(shù)暂衡、方法、類型:類玄叠,結(jié)構(gòu)體古徒,枚舉拓提,元組類型读恃,協(xié)議參數(shù),返回值代态,成員函數(shù)參數(shù)寺惫,成員屬性類...
    我是小胡胡分胡閱讀 819評論 0 1
  • 是誰 在如刀的月下行走 刺穿城市的心臟 是誰 在富足的原野火把高舉 讓土地重回蒼涼 是誰 在金黃的九月歌唱收獲 播...
    lolo007閱讀 172評論 0 5