Swift:引用泛型类

时间:2017-04-04 16:10:36

标签: swift generics

这是代码,

protocol TestType : AnyObject {

}

class Generic<TestType> : NSObject {

    private let filterFunction : (TestType,String) -> Bool

    init(filter: @escaping (TestType,String) -> Bool) {
        filterFunction = filter
    }
}

class Parent : UIViewController {
    public var generic : Generic<TestType>!
}

class Child : Parent {

    override func viewDidLoad() {
        // ERROR THIS LINE
        generic = Generic<Object>(filter: { (str1, str2) -> Bool in
            return true
        })
    }
}

class Object : TestType {

}

错误是:

Cannot assign value of type 'Generic<Object>' to type 'Generic<TestType>!'

我尝试了许多东西,比如typealias,但是无法编译代码。 问题是我不想要Parent<TestType>Child<TestType>类,因为我希望能够在IB中使用它。

如何在Parent中存储Generic的引用,并在Child中初始化它(动态地,通过设置具体的TestType,如Object或其他)

1 个答案:

答案 0 :(得分:0)

我终于成功做了我想要的事情!

根本不完美,随意评论架构改进(特别是在asBaseProtocol()部分......)

这是我的完整代码( Swift 3.0

<强> DataFilter

protocol DataFilterDelegate : class {
    func didFilter()
    func didUpdateItems()
}

class DataFilter<T> {
    public weak var delegate : DataFilterDelegate?

    private var items : [SelectableItem<T>]?
    private var filteredItems : [SelectableItem<T>]?
    var source: [SelectableItem<T>]? {
        get {
            if filteredItems != nil {
                return filteredItems
            }
            return items
        }
    }

    var filter : (T,String) -> Bool
    var populateCell : (T) -> UITableViewCell

    init(filter : @escaping (T,String) -> Bool, populateCell: @escaping (T) -> UITableViewCell) {
        self.filter = filter
        self.populateCell = populateCell
    }

    func updateItems(_ items: [T]) {
        self.items = [SelectableItem<T>]()
        for item in items {
            self.items?.append(SelectableItem(item))
        }
        delegate?.didUpdateItems()
    }

    func filterItems(text : String) {
        filteredItems = (text == "") ? nil : items?.filter { item in
            filter(item.item, text)
        }
        delegate?.didFilter()
    }

    func selectedItems() -> [T]? {
        guard let items = items else {
            return nil
        }
        var selectedItems = [T]()
        for item in items {
            if item.isSelected {
                selectedItems.append(item.item)
            }
        }
        return selectedItems
    }
}

extension DataFilter where T : FIRDataObject {
    func asBaseProtocol() -> DataFilter<FIRDataObject> {
        return DataFilter<FIRDataObject>(filter: filterAsBaseProtocol(), populateCell: populateCellAsBaseProtocol())
    }

    private func filterAsBaseProtocol() -> ((FIRDataObject,String) -> Bool) {
        return { (object, text) -> Bool in
            self.filter(object as! T, text)
        }
    }

    private func populateCellAsBaseProtocol() -> ((FIRDataObject) -> UITableViewCell) {
        return { (object) -> UITableViewCell in
            self.populateCell(object as! T)
        }
    }
}

ParentViewController类

class ParentViewController : UIViewController {
    public var dataFilter : DataFilter<FIRDataObject>? {
        didSet {
            dataFilter!.delegate = self
        }
    }

    // Some Functions using dataFilter
}

ChildViewController类

class ChildViewController : Parent {

    // Stored as a variable to not have to cast objects to the good type everytime I access dataFilter
    private var patientDataFilter = DataFilter<Patient>(filter: { patient, text in
        patient.firstname.contains(text) ||
            patient.lastname.contains(text)
    }
        , populateCell: { patient in
            let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "Patient")
            cell.textLabel?.text = patient.lastname + " " + patient.firstname
            cell.detailTextLabel?.text = patient.postalCode + " " + patient.city
            return cell
    })

    override func viewDidLoad() {
        super.viewDidLoad()

        dataFilter = patientDataFilter.asBaseProtocol()
    }

    func someFunc() {
        let patient1 = patientDataFilter.source[0].item
        // OR
        let patient2 = dataFilter.source[0].item as! Patient
    }
}