2017-04-04 80 views
2

下面的代码,斯威夫特:引用泛型类

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中使用它。

我怎么能存储在母公司通用的基准,并在儿童初始化(动态,通过设置具体TestType像物体或其他)

+0

比较[夫特通用胁迫误解](http://stackoverflow.com/q/41976844/2976878)&[如何存放Class类型的值< ClassImplementingProtocol>在Swift中类型为\ [String:Class \]的词典中?](http://stackoverflow.com/q/38590548/2976878) - “Generic ”不能转换为“Generic ”。 – Hamish

+0

为什么不创建一个'Generic '实例? – Hamish

+0

Thx @Hamish,你的帖子非常有帮助。在我的代码中,父类是具有一些搜索功能的扩展TableviewController。我希望我的子控制器能够继承这些功能,并根据这些项目给父视图控制器提供项目和过滤器的列表,以便很好地工作。 –

回答

0

我终于成功做什么,我想要的!

并不完美可言,随时为架构的改进意见(特别是在asBaseProtocol()部分...)

这是我的完整代码(雨燕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 
    } 
}