如何在Swift4上使用通用T.Type类时调用正确的构造函数?

时间:2018-03-30 15:40:17

标签: ios swift xcode

我有一个包含一些表的数据库,每个表代表我项目的一个对象。我想写一个通用函数来通过SQL读取一个表并创建一个带有记录的对象。所以,我的函数的参数是:表名和对象类型。下面的代码是我执行此操作的函数。在func的最后,我尝试调用我想做的事情,但是有一个特定的对象,那不是我想要的。

func readAll<T>(objeto: String, typeClass: T.Type) -> [T] {
    var ret : [T] = []

    // STATEMENT DATA
    let queryString = "SELECT * FROM \(objeto);"
    var queryStatement: OpaquePointer? = nil

    // STATEMENT DATA TYPE
    let queryString2 = "PRAGMA table_info(\(objeto));"
    var queryStatement2: OpaquePointer? = nil

    // 1
    if sqlite3_prepare_v2(db,queryString,-1,&queryStatement,nil) != SQLITE_OK {
        print("Error to compile readAll \(objeto) 1")
        return ret
    }

    if sqlite3_prepare_v2(db,queryString2,-1,&queryStatement2,nil) != SQLITE_OK {
        print("Error to compile readAll \(objeto) 2")
        return ret
    }

    var listNameColumns : [String] = []

    while( sqlite3_step(queryStatement2) == SQLITE_ROW ) {
        listNameColumns.append( String(cString: sqlite3_column_text(queryStatement2, 2)!) )
    }

    // 2
    while ( sqlite3_step(queryStatement) == SQLITE_ROW ) {
        var dict: [String:Any] = [:]

        for i in 0...listNameColumns.count-1 {
            let nameColumn = String(cString: sqlite3_column_name(queryStatement,Int32(i))!)
            switch (sqlite3_column_type(queryStatement, Int32(i))) {

            case SQLITE_TEXT:
                dict[nameColumn] = String(cString: sqlite3_column_text(queryStatement, Int32(i))!)
                break

            case SQLITE_INTEGER:
                dict[nameColumn] = sqlite3_column_int(queryStatement, Int32(i))
                break

            case SQLITE_FLOAT:
                dict[nameColumn] = sqlite3_column_double(queryStatement, Int32(i))
                break

            default:
                print("Tipo desconhecido.")
                break
            }
        }

        ret.append(ResPartner(dict: dict)) <------ HERE IS MY QUESTION!
    }

    // 3
    sqlite3_finalize(queryStatement2)
    sqlite3_finalize(queryStatement)

    return ret
}

这里有两个对象,它们有点不同,但构建器的工作方式和字段相同。

class ResPartner {

    static let fieldsResPartner : [String] = ["id","company_type_enum_for_customer","name","contact_address","customer_account_number","customer_group_id","segment_id","subsegment_id","economic_group_id","street","category_id","type_stablishment_id","final_user","final_taxpayer","cnpj_cpf","inscr_est","ccm","cnae","phone","phone_extension","mobile","fax","email","email_extra","website","lang"]

    var attributes : [String:Any] = [:]

    init(dict : [String:Any]) {
        for k in dict.keys {
            if(ResPartner.fieldsResPartner.contains(k)) {
                attributes[k] = dict[k]
            }
        }
    }

    func toString() {
        for k in attributes.keys{
            print("\(k) - \(attributes[k]!)")
        }
    }
}

class Product {

       static let fieldsProducts : [String] =  ["id","name","default_code","display_name","categ_id","company_ax_id","destination_type","fiscal_class_code","multiple","taxes_id","uom_id","uom_po_id","__last_update","active","create_date","create_uid","currency_id","invoice_police","item_ids","list_price","price","pricelist_id","type"]

    public var default_code: String!
    public var display_name: String!
    public var id: Int!
    public var name: String!
    public var destination_type: String!
    public var company_ax_id: Int!
    public var categ_id: Int!
    public var fiscal_class_code: String!
    public var taxes_id: Int!
    public var uom_id: Int!
    public var uom_po_id: Int!
    public var multiple: Int!
    public var last_update: String!
    public var active: Bool!
    public var create_date: String!
    public var create_uid: Int!
    public var currency_id: Int!
    public var invoice_police: String!
    public var item_ids: [Int]!
    public var list_price: String!
    public var price: Float!
    public var pricelist_id: Int!
    public var type: String!

    init() {

    }

    init( dict : [String:Any] ) {
        self.default_code = dict["default_code"] as! String
        self.display_name = dict["display_name"] as! String
        self.id = dict["id"] as! Int
        self.name = dict["name"] as! String
        self.destination_type = dict["destination_type"] as! String
        self.company_ax_id = dict["company_ax_id"] as! Int
        self.categ_id = dict["categ_id"] as! Int
        self.fiscal_class_code = dict["fiscal_class_code"] as! String
        self.taxes_id = dict["taxes_id"] as! Int
        self.uom_id = dict["uom_id"] as! Int
        self.uom_po_id = dict["uom_po_id"] as! Int
        self.multiple = dict["multiple"] as! Int
        self.last_update = dict["last_update"] as! String
        self.active = dict["active"] as! Bool
        self.create_date = dict["create_date"] as! String
        self.create_uid = dict["create_uid"] as! Int
        self.currency_id = dict["currency_id"] as! Int
        self.invoice_police = dict["invoice_police"] as! String
        self.item_ids = dict["item_ids"] as! [Int]
        self.list_price = dict["list_price"] as! String
        self.price = dict["price"] as! Float
        self.pricelist_id = dict["pricelist_id"] as! Int
        self.type = dict["type"] as! String
    }
}

所以,我的问题是,我如何调用由参数传递的T.Type类的构造函数?我读过关于协议,扩展,其他帖子的内容,但没有解决我的问题。

2 个答案:

答案 0 :(得分:2)

您可以使用协议约束您的通用:

  1. 定义用字典初始化的协议:

    protocol DictionaryInitializable {
        init(dict: [String: Any])
    }
    
  2. 使两种类型符合该类型(您必须按照Xcode的提示将required添加到init方法中,例如:

    class Product: DictionaryInitializable {
    
        ...
    
        required init(dict: [String: Any]) {
            ...
        }
    }
    

    class ResPartner: DictionaryInitializable {
    
        static let fieldsResPartner = ...
    
        var attributes: [String: Any] = [:]
    
        required init(dict: [String: Any]) {
            for k in dict.keys where ResPartner.fieldsResPartner.contains(k) {
                attributes[k] = dict[k]
            }
        }
    
        func toString() { ... }
    }
    
  3. 更改您的方法声明,以明确T必须符合您的新协议:

    func readAll<T: DictionaryInitializable>(objeto: String, typeClass: T.Type) -> [T] {
        var ret: [T] = []
    
        ... 
    
        // 2
        while ( sqlite3_step(queryStatement) == SQLITE_ROW ) {
            var dict: [String: Any] = [:]
    
            ...                
    
            ret.append(T(dict: dict)) // You can now use `T` here
        }
    
        return ret
    }
    
  4. 你会这样称呼:

    let list = db_respartner.readAll(objeto: "res_partner", typeClass: ResPartner.self)
    

答案 1 :(得分:1)

使用init方法创建协议

 protocol Mappable {       
        init(dictionary:[String:AnyObject]) // Changed based on your requirement.
 }

在ResPartner中符合您的协议。

class ResPartner: Mappable {
    required init(dictionary : [String : AnyObject]) {
        // initialize here
    }
}

在产品中使用您的协议。

class Product: Mappable {
    required init(dictionary : [String : AnyObject]) {
        // initialize here
    }
}

创建自定义对象

func readAll<T:Mappable>(objeto: String, typeClass: T.Type) -> [T] {
        var ret : [T] = []

        // intialize your variable
        let obj = typeClass.init(dictionary: ["":"" as AnyObject])
        return ret
}