2015-12-15 39 views
15

當在協議擴展中實現返回Self的靜態協議函數時,擴展中的函數實現時會出現錯誤(最小簡化方案顯示爲無上下文):非最終類中的方法必須返回`Self`以符合協議

import Foundation 

protocol P { 
    static func f() -> Self 
    static func g() -> Self 
} 

extension P { 
    static func f() -> Self { // Method 'f()' in non-final class 'NSData' must return `Self` to conform to protocol 'P' 
     return g() 
    } 
} 

extension NSData: P { 
    static func g() -> Self { 
     return self.init() 
    } 
} 

Self更換用P上發生錯誤的行使編譯器段錯誤(SIG 11)(這似乎輸送類型不匹配錯誤的有效的方式)。

改變的f()聲明返回P,以及對錯誤行與P更換Self,導致編譯成功,但失去式精密(並要求部隊在每次調用點向下轉型,加上記錄在Self要求詳情)。

是否有任何其他解決此問題的方法不會失去通用返回類型?

EDIT:進一步的細節,以補償缺少的上下文:P是一個公共協議,它由庫被暴露,用於各種類型的,以符合(並重寫g()),f()NSData所以壓倒一切的不是一個選項。最好不要將f()更改爲協議擴展以外的內容,因爲它在很多地方被庫內部使用。鑑於這兩種選擇,將f()的返回類型更改爲P是更好的選擇。

+2

「導致編譯器出現segfault(sig 11)(這似乎是傳達類型不匹配錯誤的有效方式)」。請確保您向Apple報告。 – JeremyP

回答

1

這對我的作品....

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

class C { 
    required init() {} 
} 
extension C: P { 
    static func foo() -> Self { 
     return self.init() 
    } 
} 

let c = C() 
let c2 = C.foo() 
print(c.dynamicType, c2.dynamicType) // C C 

好,我看你注意,所以我做了一個更新

protocol P { 
    static func foo()->Self 
    static func bar()->Self 
} 
extension P { 
    static func foo() -> Self { // Method 'f()' in non-final class 'NSData' must return `Self` to conform to protocol 'P' 
     return bar() 
    } 
} 
// the class C must be final ... 
final class C { 
} 
// otherwise the compiler can not decide the type of Self in 
extension C: P { 
    static func bar() -> Self { 
     return self.init() 
    } 
} 

let c = C() 
let c2 = C.foo() 
print(c.dynamicType, c2.dynamicType) // C C 

與NSData的,如果你想做出同樣的,麻煩的是將NSData聲明爲final。

+0

這對我有幫助嗎?我提到我提供的例子是簡化的 - 我不能只刪除真正的函數'g'。 – Greg

1

您需要覆蓋NSData擴展中的f()。

基本的問題是(我認爲)編譯器不知道Self是什麼時候編譯f在協議擴展中,我認爲它認爲它必須是它正在應用它的類的確切類型。對於NSData,情況可能並非如此,因爲你可能有它的一個子類。

+0

對不起,如果我不清楚;不幸的是,在'NSData'中覆蓋'f'不是一個選項,因爲這只是許多類的一個例子,它們將符合我的控制。我已經用更多的細節更新了這個問題。 似乎我必須等待Swift 3.0才能成爲可能而沒有解決方法... – Greg

1

從Swift 2.1開始,我只能通過使結構(或'最終'類)符合協議來避免給定的錯誤。目前,您可以將協議實現限制爲引用類型(「僅限類」),但我沒有看到類似的值類型限制。

在這個特殊情況下,我會說委託模式適合。如果合適,您可以將f的默認實現移動到協議擴展中,並讓子類實現覆蓋委託屬性。

protocol P { 
    static var delegate : (() -> Self)?; 
} 

extension P { 
    static func f() -> Self { 
     // Use delegate if available, or use default implementation 
    } 
} 

extension NSData : P { 
    static var delegate : (() -> NSData)? = subImplementation; 

    func subImplementation() -> NSData { 
     // Some NSData-specific implementation 
    } 
} 
0

我有同樣的問題,你知道嗎?

這是我出來處理特定情況的另一種方式。 而不是使用需要靜態函數返回Self的協議,也許你可以考慮定義一個初始化器所需的協議。

像這樣:

protocol SomeProtocol { 
    init(someParameter: Int) 
} 

不要忘記,以紀念與required關鍵字初始化實施。

class SomeClass: SomeProtocol { 
    required init(someParameter: Int) { 

    } 
} 

希望這可以幫助。

3

在斯威夫特3或4:

import Foundation 

protocol P { 
    static func f() -> Self 
    static func g() -> Self 
} 

extension P { 
    static func f() -> Self { 
     return g() 
    } 
} 

extension Data: P { 
    static func g() -> Data { 
     return self.init() 
    } 
} 

或者你可以用一個最後的子類替換類:

import Foundation 

protocol P { 
    static func f() -> Self 
    static func g() -> Self 
} 

extension P { 
    static func f() -> Self { 
     return g() 
    } 
} 

import Foundation 

final class MyData: NSData {} 
extension MyData: P { 
    static func g() -> Self { 
     return self.init() 
    } 
} 

如果NSData的是,不能輕易地被繼承的類集羣之一(你會看到一個帶有__CFRequireConcreteImplementation的棧跟蹤),你可能不得不爲真實的NSData創建一個最終的類包裝,而不是使用子類。

+0

我解決了我的問題,正如你所說的那樣讓我的課程成爲'final'。謝謝! –

+0

你確定你已經測試過嗎?但它在Swift 3中不起作用。它仍然抱怨'非最終類'Data'中的方法'g()'必須返回'Self'以符合協議'P'。' – ozgur

+0

@OzgurVatansever我不記得了,但可能我做了。我沒有再使用Swift 3,但是我編輯了包含Sw​​ift 4中編譯的完整示例。 – Jano

相關問題