2015-06-17 11 views
7

Sto affrontando con generici in Swift. Ho l'estensione alla classe NSManagedObject e volevo creare l'inizializzatore che è disponibile solo per le classi che implementano alcuni protocolli definiti. Ora ho qualcosa di simile in basso, ma questo non funziona e nemmeno la compilazione. Potresti aiutarmi a farlo funzionare?Come creare un inizializzatore di convenienza generico in Swift?

public extension NSManagedObject { 
    public convenience init<Self: Nameable>(context: NSManagedObjectContext)  { 
     let entity = NSEntityDescription.entityForName(Self.entityName(), inManagedObjectContext: context)! 
     self.init(entity: entity, insertIntoManagedObjectContext: context) 
    } 
} 

public protocol Nameable { 
    static func entityName() -> String 
} 

Xcode dice: "Il parametro generico 'Self' non viene utilizzato nella firma della funzione".

+1

Dal momento che non hai fatto nulla che possa essere Nominato, non è chiaro cosa stai cercando di fare qui. Sembra non avere uno scopo. - Inoltre, come si risolve questo generico? - Inoltre, il sé ha un significato, quindi non dovresti abusarne come nome di un segnaposto. – matt

+0

Ho altre sottoclassi 'NSManagedObject' e cerco di renderle in grado di chiamare' let obj = MyClass (context: context) ', ma solo se implementano' Nameable'.Vedo che questa è una dichiarazione errata e dovrebbe essere più simile a questa: 'estensione NSManagedObject dove NSManagedObject: Nameable' ma non funziona in questo modo. –

+1

Ma non puoi fermare un'istanza dal chiamare 'let obj = MyClass (context: context)' sulla base di quale classe è questa istanza! Non ha alcun senso, in nessuna lingua. Se c'è un inizializzatore 'MyClass (context: context)', _anyone_ può chiamarlo (se può vederlo). Questa è la natura della programmazione orientata agli oggetti, se vedi cosa intendo. Né vedo alcun motivo per cui vorresti farlo. – matt

risposta

10

come Matt già spiegato, non è possibile definire un inizializzatore che è limitata ai tipi di attuazione di un protocollo. In alternativa, si possibile definire una funzione globale, invece:

public protocol Nameable { 
    static func entityName() -> String 
} 

func createInstance<T : NSManagedObject where T: Nameable>(type : T.Type, context : NSManagedObjectContext) -> T { 
    let entity = NSEntityDescription.entityForName(T.entityName(), inManagedObjectContext: context)! 
    return T(entity: entity, insertIntoManagedObjectContext: context) 
} 

che viene poi utilizzato come

let obj = createInstance(Entity.self, context) 

È può evitare il parametro di tipo aggiuntivo se si definisce il metodo come

func createInstance<T : NSManagedObject where T: Nameable>(context : NSManagedObjectContext) -> T { ... } 

e usarlo come

let obj : Entity = createInstance(context) 

o

let obj = createInstance(context) as Entity 

dove il tipo sta dedotta dal contesto.

+0

Questo mi sembra il più vicino a quello che volevo avere, ma ancora, c'è la necessità di passare il tipo nei parametri del metodo e volevo evitare questo . Come descritto nella mia risposta, ho sbagliato a capire il problema;) Grazie per questo frammento comunque! –

+1

@TomaszSzulc: Vedi l'aggiornamento (ma non sono sicuro di quale versione sia migliore) –

+0

Mi piace questa soluzione;) Grazie! –

2

Mi sembra che si sta descrivendo qualcosa di simile:

class Thing {} 

func makeANewThing<T:ThingMaker>(caller:T) -> Thing { 
    let t = Thing() 
    return t 
} 

protocol ThingMaker { 
} 

class Dog : ThingMaker { 
} 

class Cat { // not a ThingMaker 
} 

let t = makeANewThing(Dog()) // ok 
let t2 = makeANewThing(Cat()) // illegal 

Nella vita reale, presumo che makeANewThing sarebbe in realtà fare qualcosa con la sua caller, ma il punto è che può essere solo chiamato passando un caller che ha adottato ThingMaker.

Questo è probabilmente il meglio che puoi fare in Swift 1. Se vuoi iniettare un metodo solo in classi che adottano un certo protocollo, allora quello che vuoi è un'estensione di protocollo - ma è disponibile solo in Swift 2.

+0

Tuttavia, nel tuo esempio attuale, avrai ulteriori difficoltà: non sarai in grado di recuperare la proprietà statica 'name' di qualcosa digitato come Nameable, per ragioni che spiego qui: http: // stackoverflow. com/a/30824093/341994 Ancora una volta, la soluzione è l'aggiornamento a Swift 2. – matt

0

Ok, grazie per i vostri commenti su questa discussione. Ho realizzato con @matt commenti che non posso fare quello che ho pensato perché non è nemmeno possibile. Non volevo creare sottoclassi per farlo funzionare e non era possibile con la mia comprensione del problema.

Infine ho trovato un altro modo per ottenere il nome dell'entità. Ha pro e contro ma ho deciso di usarlo per ora. Quindi ho creato l'estensione per NSManagedObject per farlo funzionare.

extension NSManagedObject { 

    class func entityName() -> String { 
     let fullClassName = NSStringFromClass(object_getClass(self)) 
     let nameComponents = split(fullClassName) { $0 == "." } 
     return last(nameComponents)! 
    } 

    public convenience init(context: NSManagedObjectContext) { 
     let name = self.dynamicType.entityName() 
     let entity = NSEntityDescription.entityForName(name, inManagedObjectContext: context)! 
     self.init(entity: entity, insertIntoManagedObjectContext: context) 
    } 
} 

E poi

obj = Obj(context: ctx) 
Problemi correlati