Swift 泛型:类型声明中的 where

Swift Generics: where in type declaration

我正在尝试制作一个 API 允许调用者将 UIView 的子类和一个块传递给一个函数,稍后接收者将使用给定的 UIView 子类实例和另一个参数。我已经分解了这个问题,以便它在操场上运行:

import UIKit

struct SomeStruct<T where T: UIView> {

    let view: T
    let block: ((T, Int) -> Void)

    //some computed variables here that rely on T being a UIView
}

class SomeClass {

    var data: SomeStruct<UIView>? //Ideally, SomeStruct<? where ? : UIView>

    func doSomethingLater<V where V: UIView>(view: V, block: ((V, Int) -> Void)) {

        //Cannot convert value of type '(V, Int) -> Void' to expected argument type '(_, Int) -> Void'
        self.data = SomeStruct(view: view, block: block)
    }
}


然后调用者会像这样使用 SomeClass

let c = SomeClass()

let label = UILabel()
c.doSomethingLater(b) { (label, idx) in
    //l is type UILabel
}   

let button = UIButton(type: .Custom)
c.doSomethingLater(b) { (button, idx) in
    //b is type UIButton
} 

我想要限制为 UIView 的子类而不是 UIView 本身的原因是,从块内,调用者不需要将参数转换为它的原始类型

所以,我的问题是:在声明变量类型时有没有办法使用 where 子句?

我可以看到实现此目的的多种方法。使用哪一个取决于您,具体取决于您希望如何组织代码。

  1. 使 SomeClass 本身通用:

    class SomeClass<V: UIView> {
        var data: SomeStruct<V>?
        ...
    }
    
  2. 使 SomeStruct 符合仅公开 UIView 的协议(具有 no 关联类型),因此您仍然可以编写计算属性:

    protocol SomeStructType {
        var view: UIView { get }
    }
    
    extension SomeStructType {
        // add computed properties based on self.view...
    }
    
    struct ConcreteStruct<V: UIView>: SomeStructType {
        let realView: V
        let block: (T, Int) -> Void
        var view: UIView { return realView }  // satisfy the protocol
    }
    
    class SomeClass {
        var data: SomeStructType?
        ...
        func doSomethingLater<V: UIView>(view: V, block: (V, Int) -> Void) {
            data = ConcreteStruct(realView: view, block: block)
        }
    }
    
  3. 而不是结构,使用非泛型 class 的泛型子 class(这与 #2 非常相似)

    class SomeData {
        var view: UIView { fatalError("abstract method") }
        ...
    }
    
    class ConcreteData<V: UIView>: SomeData {
        let realView: V
        let block: (V, Int) -> Void
        override var view: UIView { return realView }  // override the getter
        init(view: V, block: (V, Int) -> Void) { ... }
    }
    
    class SomeClass {
        var data: SomeData?
        ...
        func doSomethingLater<V: UIView>(view: V, block: (V, Int) -> Void) {
            data = ConcreteData(realView: view, block: block)
        }
    }
    

你根本不需要 SomeClass

struct S<T> {
    let a: T
    let b: (T, Int) -> Void
}

let s = S(a: "a") { (i, j) in
    // see, that a is NOT accessible here!!!!
    print("1:",i,j)
}

s.b("b", 1) // compiler take care about types, so (String,Int) is the only option

// to be able change b later !!!!!
struct S1<T> {
    let a: T
    var b: (T, Int) -> Void
}

var s1 = S1(a: 100) { _ in }
s1.b = { label, index in
    print("2:",s1, label, index)
}

s1.b(200,2)

class C {
    var i: Int = 0
}
let c:C? = C()
var s2 = S1(a: c) { _ in } // so later i can modify c
s2.b = { label, index in
    s2.a?.i = index
}
print("3:",c, c?.i)
s2.b(nil, 100)
print("4:",c, c?.i)

它打印

 1: b 1
 2: S1<Int>(a: 100, b: (Function)) 200 2
 3: Optional(C) Optional(0)
 4: Optional(C) Optional(100)

let c2 = C()
c2.i = 200

// modify c.i to max of c2.i and some value
s2.b = { c, i in
    if let j = c?.i {
        s2.a?.i = max(j,i)
    }
}

s2.b(c2, 50) // s2 still modify c
print("5:",c, c?.i)

s2.b(c2, 250) // s2 still modify c
print("6:",c, c?.i)

打印

5: Optional(C) Optional(200)
6: Optional(C) Optional(250)