在Swift中使用泛型的EXC_BAD_ACCESS

时间:2014-12-07 13:49:41

标签: ios swift exc-bad-access generics

相关问题:Generic completion handler in Swift

在我写的Swift应用程序中,我正在下载JSON,我想将其转换为模型对象。现在,我这样做:

func convertJSONData<T: Entity>(jsonData: NSData?, jsonKey: JSONKey, _: T.Type) -> [T]? {
        var entities = [T]()
        if let data = jsonData {

            // Left out error checking for brevity

            var json = JSON(data: data, options: nil, error: nil)
            var entitiesJSON = json[jsonKey.rawValue]

            for (index: String, subJson: JSON) in entitiesJSON {

                // Error: EXC_BAD_ACCESS(code=EXC_I386_GPFLT)

                let entity = T(json: subJson)
                entities.append(entity)
            }
        }
        return entities
    }

符合Entity的每个对象都会实现init(json: JSON)JSONSwiftyJSON库中定义的类型。这也是枚举看起来有点奇怪的原因。

我在此方法中调用convertJSONData()

public func performJSONRequest<T where T: Entity>(jsonRequest: JSONRequest<T>) {
        var urlString = ...
        Alamofire.request(.GET, urlString, parameters: nil, encoding: .JSON).response { (request, response, data, error) -> Void in
                var books = self.convertJSONData(data as? NSData, jsonKey: jsonRequest.jsonKey, T.self)
                jsonRequest.completionHandler(books, error)
        }
    }

调用EXC_BAD_ACCESS(code=EXC_I386_GPFLT)时出现运行时T(json: subJSON)错误。没有编译器警告或错误。虽然我在上面的代码中遗漏了错误检查,但实际代码中存在错误检查,error为零。

我不确定这是编译器错误还是我的错,并且非常感谢任何帮助。

1 个答案:

答案 0 :(得分:3)

这里发生了一些事情,我怀疑问题出在实现Entity协议的类的初始化器中。

假设代码类似于以下内容:

protocol Entity {
    init(json: JSON)
}

class EntityBase: Entity {
    var name: String = ""
    required init(json: JSON) { // required keyword is vital for correct type inference
        if let nameFromJson = json["name"].string {
            self.name = nameFromJson
        }
    }

    func getName() -> String { return "Base with \(name)" }
}

class EntitySub: EntityBase {
    convenience required init(json: JSON) {
        self.init(json: json)  // the offending line
    }

    override func getName() -> String { return "Sub with \(name)" }
}  

代码在子类中使用self.init(json: json)进行编译,但实际上尝试使用便捷方法初始化实例会产生EXC_BAD_ACCESS

删除子类上的初始值设定项,或者只需实现required init并调用super。

class EntitySub: EntityBase {
    required init(json: JSON) {
        super.init(json: json)
    }

    override func getName() -> String { return "Sub with \(name)" }
}  


jsonData转换为Entity的方法(在.NonejsonData时稍微修改为专门返回nil):

func convertJSONData<T:Entity>(jsonData: NSData?, jsonKey: JSONKey, type _:T.Type) -> [T]? {
    if let jsonData = jsonData {
        var entities = [T]()

        let json = JSON(data: jsonData, options:nil, error:nil)
        let entitiesJSON = json[jsonKey.rawValue]

        for (index:String, subJson:JSON) in entitiesJSON {

            let entity:T = T(json: subJson)

            entities.append(entity)

        }

        return entities
    }

    return .None
}