86

オブジェクトのコピーを返すプロトコル P があります。

protocol P {
    func copy() -> Self
}

P を実装するクラス C:

class C : P {
    func copy() -> Self {
        return C()
    }
}

Selfただし、次のエラーが発生したときに戻り値を入力するかどうか:

型 'C' の戻り式を戻り型 'Self' に変換できません

私も返してみCました。

class C : P {
    func copy() -> C  {
        return C()
    }
}

その結果、次のエラーが発生しました。

非最終クラス「C」のメソッド「copy()」はSelf、プロトコル「P」に準拠するために戻る必要があります

ie doを前class Cに付けた場合を除いて、何も機能しません。final

final class C : P {
    func copy() -> C  {
        return C()
    }
}

ただし、C をサブクラス化したい場合は、何も機能しません。これを回避する方法はありますか?

4

9 に答える 9

10

実際には、プロトコルによって要求されたときに簡単に戻ることができるトリックがあります ( gist ):Self

/// Cast the argument to the infered function return type.
func autocast<T>(some: Any) -> T? {
    return some as? T
}

protocol Foo {
    static func foo() -> Self
}

class Vehicle: Foo {
    class func foo() -> Self {
        return autocast(Vehicle())!
    }
}

class Tractor: Vehicle {
    override class func foo() -> Self {
        return autocast(Tractor())!
    }
}

func typeName(some: Any) -> String {
    return (some is Any.Type) ? "\(some)" : "\(some.dynamicType)"
}

let vehicle = Vehicle.foo()
let tractor = Tractor.foo()

print(typeName(vehicle)) // Vehicle
print(typeName(tractor)) // Tractor
于 2016-01-16T21:46:09.797 に答える
2

ロブの提案に従って、これは関連する型でより一般的にすることができます。このアプローチの利点を示すために、例を少し変更しました。

protocol Copyable: NSCopying {
    associatedtype Prototype
    init(copy: Prototype)
    init(deepCopy: Prototype)
}
class C : Copyable {
    typealias Prototype = C // <-- requires adding this line to classes
    required init(copy: Prototype) {
        // Perform your copying here.
    }
    required init(deepCopy: Prototype) {
        // Perform your deep copying here.
    }
    @objc func copyWithZone(zone: NSZone) -> AnyObject {
        return Prototype(copy: self)
    }
}
于 2015-08-31T16:53:51.983 に答える
1

私は同様の問題を抱えていて、役立つかもしれないものを思いついたので、解決策を探したときに最初に見つけた場所の1つであるため、将来の参考のために共有したいと思います.

上で述べたように、問題は copy() 関数の戻り値の型があいまいであることです。これは、copy() -> C と copy() -> P 関数を分離することで非常に明確に説明できます。

したがって、プロトコルとクラスを次のように定義するとします。

protocol P
{
   func copy() -> P
}

class C:P  
{        
   func doCopy() -> C { return C() }       
   func copy() -> C   { return doCopy() }
   func copy() -> P   { return doCopy() }       
}

戻り値の型が明示的である場合、これはコンパイルされ、期待される結果を生成します。コンパイラが戻り値の型を (独自に) 決定する必要がある場合はいつでも、状況があいまいであることに気づき、P プロトコルを実装するすべての具象クラスで失敗します。

例えば:

var aC:C = C()   // aC is of type C
var aP:P = aC    // aP is of type P (contains an instance of C)

var bC:C         // this to test assignment to a C type variable
var bP:P         //     "       "         "      P     "    "

bC = aC.copy()         // OK copy()->C is used

bP = aC.copy()         // Ambiguous. 
                       // compiler could use either functions
bP = (aC as P).copy()  // but this resolves the ambiguity.

bC = aP.copy()         // Fails, obvious type incompatibility
bP = aP.copy()         // OK copy()->P is used

結論として、これは、基本クラスの copy() 関数を使用していないか、明示的な型コンテキストを常に持っている状況で機能します。

具象クラスと同じ関数名を使用すると、どこでも扱いにくいコードになることがわかったので、プロトコルの copy() 関数に別の名前を使用することになりました。

最終結果は次のようになります。

protocol P
{
   func copyAsP() -> P
}

class C:P  
{
   func copy() -> C 
   { 
      // there usually is a lot more code around here... 
      return C() 
   }
   func copyAsP() -> P { return copy() }       
}

もちろん、私のコンテキストと機能は完全に異なりますが、質問の精神で、私は可能な限り与えられた例に近づこうとしました.

于 2015-08-23T08:51:06.543 に答える