簡體   English   中英

EXC_BAD_ACCESS使用協議組合

[英]EXC_BAD_ACCESS using protocol composition

我想配置一個具有多個表現形式的對象; 我為他們創建了協議; 這樣我就可以把它們組合成一個具體的。

protocol Presentable {

}

protocol TextPresentable: Presentable {
    var text:String { get }
}

protocol ImagePresentable: Presentable {
    var image:String { get }
    var images:[String] { get }
}

具體結構:

struct ConcretePresentable: TextPresentable, ImagePresentable {
    var text:String { return "Text" }
    var image:String { return "Image" }
    var images:[String] { return ["A", "B"] }
}

接下來的事情是有一個演示者,我會檢查傳入的演示者是否真的有效:

typealias TextAndImagePresentable = protocol<TextPresentable, ImagePresentable>

struct ConcretePresenter {
    func configureWithPresentable(presentable: Presentable) {
        guard let textAndImagePresentable = presentable as? TextAndImagePresentable else {
            return
        }

        print(textAndImagePresentable.text)
        print(textAndImagePresentable.image)
        print(textAndImagePresentable.images)
    }
}

要配置:

    let concretePresentable = ConcretePresentable()
    let concretePresenter = ConcretePresenter()

    concretePresenter.configureWithPresentable(concretePresentable)

如果我在操場上運行它,並且所有代碼都在同一個地方,那就沒問題了。 但是,一旦我將它放入一個項目並將其拆分為多個文件( ConcretePresenter.swiftConcretePresentable.swift持有具體結構和Presentable.swift ,其中包含協議),我得到一個EXC_BAD_ACCESS

為什么會這樣?

作為免責聲明,我不一定覺得這個答案非常令人滿意,但確實有效。


所以,一旦我注意到,文字和圖像性能正在每個人的地方(該值返回text正在被返回的image屬性,反之亦然),我想這個問題有一些東西做什么斯威夫特與管理做指針在這里。

因此,出於好奇,我想為協議添加一個真正的標量值。 我在TextPresentable協議中添加了一個value屬性作為Int

protocol Presentable {}

protocol TextPresentable: Presentable {
    var text:String { get }
    var value: Int { get }
}

protocol ImagePresentable: Presentable {
    var image:String { get }
    var images:[String] { get }
}

然后我設置具體實現來返回一些已知值。 在這里,我們返回0

struct ConcretePresentable: TextPresentable, ImagePresentable {
    var text:String { return "SomeText" }
    var value: Int { return 0 }
    var image:String { return "SomeImage" }
    var images:[String] { return ["A", "B"] }
}

這段代碼運行之后,我們仍然得到同樣的崩潰,但我發現value ,這實在不應該有打印問題0是不是打印一些非常大的數字: 4331676336 這根本不對。

我還將images從數組更改為字典,以查看錯誤是否仍然存在 - 確實如此。 似乎崩潰與集合有關,而不是特定於數組。


從這里開始,我嘗試了其他一些東西。

我嘗試將ConcretePresentable類而不是結構。

class ConcretePresentable: TextPresentable, ImagePresentable

這導致了同樣的行為。

我嘗試使ConcretePresentable符合typealias而不是協議獨立:

struct ConcretePresentable: TextAndImagePresentable

這導致了同樣的行為。

我試着立刻做上述兩件事:

class ConcretePresentable: TextAndImagePresentable

但仍然是相同的行為。


我確實提出了一種讓它工作的方法。 制作符合您的typealias的兩個協議的協議,並使ConcretePresentable符合:

protocol TextAndImagePresentable: TextPresentable, ImagePresentable {}

struct ConcretePresentable: TextAndImagePresentable {
    // ...
}

這里的問題是,如果你不明確地使ConcretePresentable符合協議,它會失敗的guard let即使不符合TextPresentableImagePresentable

struct使用值語義,因此復制屬性。 Swift應該將此報告為錯誤,因為您嘗試從兩個派生自相同基本協議的協議繼承。 在類中這將起作用,但在struct中它不會因為struct的值語義。 如果你決定將一個變量添加到Presentable協議,那么Swift會混淆哪些變量帶入結構。 來自TextPresentable或ImagePresentable

您應該使用@protocol Presentable : class ,然后將ConcretePresentable轉換為類來修復此問題。

protocol Presentable : class {
}

class ConcretePresenter {...

我在Swift用戶上詢問過這個並填寫了一個bug。

確認是編譯器中的錯誤:

https://bugs.swift.org/browse/SR-4477

現在由Joe Groff修復:

Joe Groff在2小時前添加了評論

合並。 應該在將來的快照中修復。

問題是在中投PresentableTextAndImagePresentable guard let成功,但卻創造了一個無效的價值(我不確切知道為什么)。

檢查它的一種方法是在執行命令時查看控制台:

print(textAndImagePresentable.text)
print(textAndImagePresentable.image)
print(textAndImagePresentable.images)

這將打印:

Image
Text
Program ended with exit code: 9

避免它的一種方法是更改​​方法簽名以避免轉換:

func configureWithPresentable(presentable: TextAndImagePresentable) {
    print(presentable.text)
    print(presentable.image)
    print(presentable.images)
}

並且,在我看來,如果presentable不符合這兩種協議,則不會發生任何事情,因此在方法簽名上划分它更有意義。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM