2014-12-07 19 views
5

questione connessa: Generic completion handler in SwiftEXC_BAD_ACCESS utilizzando Generics a Swift

In un'applicazione Swift sto scrivendo, sto scaricando JSON e voglio convertirlo in oggetti del modello. In questo momento, sto facendo che in questo modo:

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 
    } 

ogni oggetto conforme alla Entity attrezzi init(json: JSON). JSON è un tipo definito nella libreria SwiftyJSON. Questa è anche la ragione per cui l'enumerazione sembra un po 'strana.

io chiamo convertJSONData() in questo metodo:

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) 
     } 
    } 

ottengo un runtime EXC_BAD_ACCESS(code=EXC_I386_GPFLT) errore di chiamare T(json: subJSON). Non ci sono avvisi o errori del compilatore. Anche se ho omesso il controllo degli errori nel codice sopra, c'è il controllo degli errori nel codice effettivo e error è nullo.

Non sono sicuro che si tratti di un bug del compilatore o di una mia colpa e qualsiasi aiuto che lo comprenda è molto apprezzato.

+0

è la classe Entity e la 'init' pubblico o locale il progetto? –

+0

In realtà è un protocollo, indicato come 'pubblico'. I metodi di protocollo non possono essere contrassegnati come 'public', ma poiché' Entity' stesso è, non vedo come ciò potrebbe causare il problema – wander

+0

che tipo riporta il debugger per 'entitiesJSON'? –

risposta

3

Diverse cose stanno succedendo qui e ho il sospetto che il problema risieda nell'inizializzatore della classe che implementa il protocollo Entity.

Supponendo che il codice simile al seguente:

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)" } 
} 

Il codice viene compilato con self.init(json: json) nel sotto-classe, ma in realtà il tentativo di inizializzare l'istanza utilizzando i risultati metodo comodo in un EXC_BAD_ACCESS.

Rimuovere l'inizializzatore nella sottoclasse o semplicemente implementare required init e chiamare super.

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

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


Il metodo per convertire il jsonData a un Entity (leggermente modificato per tornare specificamente .None quando jsonData è 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 
} 
+0

Non sto creando sottoclassi e sto usando 'struct's. Ho fatto un piccolo riassunto mostrando il codice che ho. È davvero così semplice. https://gist.github.com/wandersiemers/cb3d486c7d370d35eeab – wander

+0

nel 'init (json: JSON)' prima di chiamare 'init (id: Int, name: String)' tira i valori nelle proprietà e salta la chiamata a ' self.init'. Dovrebbe darti la possibilità di identificare un problema con i dati JSON se ce n'è uno. –

+1

Arresta prima addirittura a colpire la prima riga dell'attuale implementazione di 'init (json: JSON)'. Penso che ci sia qualcosa di sbagliato in ciò che il compilatore pensa sia il tipo reale che rappresenta T ma non riesco a capire * perché *. – wander

Problemi correlati