簡體   English   中英

在Swift中將受約束的關聯類型的協議用作屬性

[英]Use protocol with constrained associated type as property in Swift

我正在嘗試實現具有關聯類型的數據源協議

protocol DataSourceCompatible {
    associatedtype CellModel
    func cellModelForItem(at indexPath: IndexPath) -> CellModel
}

協議AddressBookViewModelType繼承自基本協議,並將關聯的值約束到另一個協議

protocol AddressBookViewModelType: class, DataSourceCompatible where CellModel == AddressBookCellModelType {
}

AddressBookViewModelAddressBookViewModelType協議的具體實現

class AddressBookViewModel: AddressBookViewModelType {
    func cellModelForItem(at indexPath: IndexPath) -> AddressBookCellModelType {
        let contact = sectionedContacts[indexPath.section][indexPath.row]
        return AddressBookCellModel(contact: contact)
    }
}

代碼可以正常編譯,但是當我在viewcontroller上將viewmodel聲明為屬性時,編譯器將失敗, Protocol 'AddressBookViewModelType' can only be used as a generic constraint because it has Self or associated type requirements

class AddressBookViewController: UIViewController {
    private var viewModel: AddressBookViewModelType!

    func configure(viewModel: AddressBookViewModelType) {
        self.viewModel = viewModel
    }
...
}

我記得看到類型擦除可能解決了這個問題,但是我對類型擦除的概念並不熟悉。 有沒有辦法解決這個問題?

更新:

這里的AddressBookCellModelType和AddressBookCellModel有什么關系?

這是實現協議的結構。

protocol AddressBookCellModelType {
    var name: String { get }
    var photo: UIImage? { get }
    var isInvited: Bool { get }
}

struct AddressBookCellModel: AddressBookCellModelType {
 ....
}

您是否嘗試過僅將其用作通用名稱,例如警告/錯誤提示:

class AddressBookViewController<T: AddressBookViewModelType> : UIViewController {
    private var viewModel: T!

    func configure(viewModel: T) {
        self.viewModel = viewModel
    }
    ...
}

您需要使用變量T的屬性初始化控制器,以便可以推斷出類型。

為了擴展我在注釋中的問題,查看此代碼看起來很靈活,而無需添加AddressBookCellModelTypeAddressBookViewModelType ,這也將擺脫麻煩,同時仍然可以在DataSourceCompatible上通用。

// This protocol is fine and very useful for making reusable view controllers. Love it.
protocol DataSourceCompatible {
    associatedtype CellModel
    func cellModelForItem(at indexPath: IndexPath) -> CellModel
}

// No need for a protocol here. The struct is its own interface.
// This ensures value semantics, which were being lost behind the protocol
// (since a protocol does not promise value semantics)    
struct AddressBookCellModel {
    var name: String
    var photo: UIImage?
    var isInvited: Bool
}

// AddressBookViewModel conforms to DataSourceCompatible
// Its conformance sets CellModel to AddressBookCellModel without needing an extra protocol
class AddressBookViewModel: DataSourceCompatible {
    let sectionedContacts: [[AddressBookCellModel]] = []
    func cellModelForItem(at indexPath: IndexPath) -> AddressBookCellModel {
        return sectionedContacts[indexPath.section][indexPath.row]
    }
}

class AddressBookViewController: UIViewController {
    private var viewModel: AddressBookViewModel!

    func configure(viewModel: AddressBookViewModel) {
        self.viewModel = viewModel
    }
}

通過這種方式,可以實現通用的VC,而無需引入所需的更多組件:

class DataSourceViewController<DataSource: DataSourceCompatible>: UIView {
    private var viewModel: DataSource.CellModel!

    func configure(viewModel: DataSource.CellModel) {
        self.viewModel = viewModel
    }
}

let vc = DataSourceViewController<AddressBookViewModel>()

只是Swift規范,您不能使用“具有關聯類型的協議”作為類型聲明。 原因是編譯器在編譯時不知道關聯的類型實際上是什么,這違反了Swift的“類型安全性”。

解決方案是使用您所說的類型擦除器,或者使類型通用。

暫無
暫無

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

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