23跃脊、【Swift】不透明類型 - Opaque Types

  • 場景:返回值使用泛型,會暴露具體類型
    • 不同于返回一個協(xié)議類型的值荧止,不透明類型保持了類型的身份——編譯器可以訪問類型的信息,但模塊的客戶端不能

不透明類型解決的問題

  • 泛型例子-使用 ASCII 繪制圖像
protocol Shape {
    func draw() -> String
}
struct Triangle: Shape {
    var size: Int
    func draw() -> String {
        var result = [String]()
        for length in 1...size {
            result.append(String(repeating: "*", count: length))
        }
        return result.joined(separator: "\n")
    }
}
let smallTriangle = Triangle(size: 3)
print(smallTriangle.draw())
// *
// **
// ***
  • 使用泛型:翻轉(zhuǎn)圖形
struct FlippedShape<T: Shape>: Shape {
    var shape: T
    func draw() -> String {
        let lines = shape.draw().split(separator: "\n")
        return lines.reversed().joined(separator: "\n")
    }
}
let flippedTriangle = FlippedShape(shape: smallTriangle)
print(flippedTriangle.draw())
// ***
// **
// *
  • 定義了一個 JoinedShape<T: Shape, U: Shape> 結(jié)構(gòu)體阶剑,把兩個圖形垂直地結(jié)合在一起
JoinedShape<FlippedShape<Triangle>, Triangle>
struct JoinedShape<T: Shape, U: Shape>: Shape {
    var top: T
    var bottom: U
    func draw() -> String {
        return top.draw() + "\n" + bottom.draw()
    }
}
let joinedTriangles = JoinedShape(top: smallTriangle, bottom: flippedTriangle)
print(joinedTriangles.draw())
// *
// **
// ***
// ***
// **
// *
  • 弊端:暴露了返回值類型跃巡,而且調(diào)用接口需要關(guān)心返回值類型

返回不透明類型

  • 不透明類型當(dāng)作泛型反義詞
  • 下面函數(shù)返回類型由調(diào)用時決定
func max<T>(_ x: T, _ y: T) -> T where T: Comparable { ... }
  • x 、 y 值的類型決定了T 的具體類型
  • 不透明類型:函數(shù)返回了一個梯形而沒有暴露圖形的類型
struct Square: Shape {
    var size: Int
    func draw() -> String {
        let line = String(repeating: "*", count: size)
        let result = Array<String>(repeating: line, count: size)
        return result.joined(separator: "\n")
    }
}
 
func makeTrapezoid() -> some Shape {
    let top = Triangle(size: 2)
    let middle = Square(size: 2)
    let bottom = FlippedShape(shape: top)
    let trapezoid = JoinedShape(
        top: top,
        bottom: JoinedShape(top: middle, bottom: bottom)
    )
    return trapezoid
}
let trapezoid = makeTrapezoid()
print(trapezoid.draw())
// *
// **
// **
// **
// **
// *
  • 聲明了它的返回類型為 some Shape牧愁,函數(shù)返回一個遵循 Shape 協(xié)議的類型素邪,而不需要標(biāo)明具體類型,只要類型遵循 Shape 協(xié)議

  • 泛型 + 不透明返回類型

func flip<T: Shape>(_ shape: T) -> some Shape {
    return FlippedShape(shape: shape)
}
func join<T: Shape, U: Shape>(_ top: T, _ bottom: U) -> some Shape {
    JoinedShape(top: top, bottom: bottom)
}
 
let opaqueJoinedTriangles = join(smallTriangle, flip(smallTriangle))
print(opaqueJoinedTriangles.draw())
// *
// **
// ***
// ***
// **
// *
  • 函數(shù)代碼有多處 return 時猪半,保證都是相同類型

  • 返回類型也可用泛型類型參數(shù)兔朦,但必須保證單一類型

  • 例子:包含方塊特殊處理的圖形翻轉(zhuǎn)函數(shù)的錯誤版本:

func invalidFlip<T: Shape>(_ shape: T) -> some Shape {
    if shape is Square {
        return shape // Error: return types don't match
    }
    return FlippedShape(shape: shape) // Error: return types don't match
}
func `repeat`<T: Shape>(shape: T, count: Int) -> some Collection {
    return Array<T>(repeating: shape, count: count)
}
  • 無論什么圖形傳入,repeat(shape:count:) 都會創(chuàng)建和返回這個圖形的數(shù)組

  • 依舊滿足返回不透明類型的函數(shù)必須返回同一類型的約束

不透明類型和協(xié)議類型的區(qū)別

  • 與使用協(xié)議類型作為函數(shù)返回類型非常相似

  • 區(qū)別于它們是否保存類型特征

    • 不透明類型引用為特定的類型(調(diào)用者不能看到)
    • 議類型可以引用到任何遵循這個協(xié)議的類型
  • 比如磨确,這里有一個版本的 flip(_:) 它返回一個協(xié)議類型的值而不是不透明類型:

func protoFlip<T: Shape>(_ shape: T) -> Shape {
    return FlippedShape(shape: shape)
}
  • protoFlip(_:) 返回的值并不要求總是返回相同的類型——只要遵循 Shape 協(xié)議就好
  • protoFlip(:) 使得 API 要求遠(yuǎn)比 flip(:) 要松沽甥。它保留了返回多種類型的彈性
func protoFlip<T: Shape>(_ shape: T) -> Shape {
    if shape is Square {
        return shape
    }
 
    return FlippedShape(shape: shape)
}
// 函數(shù)返回的兩個翻轉(zhuǎn)過的圖形可能擁有完全不同的類型
  • protoFlip(_:) 具有更少的特定返回類型信息,意味依賴類型信息的操作無法完成

  • 比如乏奥, == 運(yùn)算符無法比較函數(shù)返回的結(jié)果

let protoFlippedTriangle = protoFlip(smallTriangle)
let sameThing = protoFlip(smallTriangle)
protoFlippedTriangle == sameThing  // Error
  • 錯誤原因

    • Shape 并沒有 == 作為自身協(xié)議的需求
    • 嘗試添加摆舟,會遇到 == 運(yùn)算符需要知道左手實(shí)際參數(shù)和右手實(shí)際參數(shù)的類型
  • 彈性的代價就是返回值無法使用某些運(yùn)算

  • 另一個問題是圖形轉(zhuǎn)換不能嵌套

  • 相反,不透明類型保持了具體類型的特征邓了。Swift 可以推斷相關(guān)類型

protocol Container {
    associatedtype Item
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}
extension Array: Container { }
  • 不能使用 Container 作為函數(shù)的返回類型恨诱,因?yàn)檫@個協(xié)議有一個關(guān)聯(lián)類型
  • 也不能使用它作為范型返回類型的約束,因?yàn)樗诤瘮?shù)體外沒足夠信息推斷它到底成為什么范型類型
// Error: Protocol with associated types can't be used as a return type.
func makeProtocolContainer<T>(item: T) -> Container {
    return [item]
}
 
// Error: Not enough information to infer C.
func makeProtocolContainer<T, C: Container>(item: T) -> C {
    return [item]
}
  • 使用不透明類型 some Container 作為返回類型則能夠表達(dá)期望的 API 約束——函數(shù)返回一個容器骗炉,但不指定特定的容器類型:
func makeOpaqueContainer<T>(item: T) -> some Container {
    return [item]
}
let opaqueContainer = makeOpaqueContainer(item: 12)
let twelve = opaqueContainer[0]
print(type(of: twelve))
// Prints "Int"
  • 不透明容器的具體類型是 [T]
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末照宝,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子痕鳍,更是在濱河造成了極大的恐慌硫豆,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件笼呆,死亡現(xiàn)場離奇詭異熊响,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)诗赌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門汗茄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人铭若,你說我怎么就攤上這事洪碳』福” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵弹澎,是天一觀的道長哎垦。 經(jīng)常有香客問我,道長嫂侍,這世上最難降的妖魔是什么儿捧? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮挑宠,結(jié)果婚禮上菲盾,老公的妹妹穿的比我還像新娘。我一直安慰自己各淀,他們只是感情好懒鉴,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著碎浇,像睡著了一般临谱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上南捂,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天吴裤,我揣著相機(jī)與錄音,去河邊找鬼溺健。 笑死麦牺,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的鞭缭。 我是一名探鬼主播剖膳,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼岭辣!你這毒婦竟也來了吱晒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤沦童,失蹤者是張志新(化名)和其女友劉穎仑濒,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體偷遗,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡墩瞳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了氏豌。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片喉酌。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出泪电,到底是詐尸還是另有隱情般妙,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布相速,位于F島的核電站碟渺,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏突诬。R本人自食惡果不足惜止状,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望攒霹。 院中可真熱鬧,春花似錦浆洗、人聲如沸催束。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抠刺。三九已至,卻和暖如春摘昌,著一層夾襖步出監(jiān)牢的瞬間速妖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工聪黎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留罕容,地道東北人。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓稿饰,卻偏偏與公主長得像锦秒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子喉镰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353

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

  • 不透明類型 具有不透明返回類型的函數(shù)或者方法會隱藏返回值的類型信息旅择,函數(shù)不在提供具體的類型組委返回值,而是根據(jù)它所...
    枯樹戀閱讀 892評論 0 2
  • 案例代碼下載 不透明的類型 函數(shù)或方法返回的不透明類型會隱藏其返回值的類型信息侣姆。不是提供具體類型作為函數(shù)的返回類型...
    酒茶白開水閱讀 1,336評論 0 1
  • 這是 Swfit 5.0.1新增的特性 具有不透明返回類型的函數(shù)或方法隱藏其返回值的類型信息生真。返回值不是作為函數(shù)的...
    微笑中的你閱讀 669評論 0 0
  • 今年 WWDC 最重要的關(guān)注點(diǎn)是什么?Swift捺宗!Swift 5.0 ABI 達(dá)到穩(wěn)定柱蟀,Swift 5.1 達(dá)到 ...
    iOS亮子閱讀 475評論 0 2
  • 具有不透明返回類型的函數(shù)或方法會隱藏返回值的類型信息。 函數(shù)不再提供具體的類型作為返回類型偿凭,而是根據(jù)它支持的協(xié)議來...
    DevXue閱讀 695評論 0 0