2015-06-17 3 views
7

Я занимаюсь дженериками в Свифт. У меня есть расширение до класса NSManagedObject и вы хотите создать инициализатор, который доступен только для классов, которые реализуют определенный мной протокол. Теперь у меня есть что-то вроде ниже, но это не работает и даже не компилируется. Не могли бы вы помочь мне заставить его работать?Как создать универсальный инициализатор удобства в 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 говорит: «Общий признак« Я »не используется в сигнатуре функции».

+1

Поскольку вы ничего не сделали, чтобы принять Именимый, неясно, что вы даже пытаетесь сделать здесь. Кажется, у него нет цели. - Также, как это общее решение будет разрешено? - Кроме того, «Я» имеет значение, поэтому вы не должны злоупотреблять им как имя заполнителя. – matt

+0

У меня есть другие подклассы 'NSManagedObject' и пытаюсь сделать их доступными для' let obj = MyClass (context: context) ', но только если они реализуют' Nameable'.Я вижу, что это неверное объявление и должно быть больше похоже: 'extension NSManagedObject, где NSManagedObject: Nameable', но он также не работает таким образом. –

+1

Но вы не можете остановить экземпляр от вызова 'let obj = MyClass (context: context)' на основе того, что это за класс! Это не имеет никакого смысла, на любом языке. Если есть инициализатор 'MyClass (контекст: контекст)', _anyone_ может вызывать его (если они видят его). Это характер объектно-ориентированного программирования, если вы понимаете, что я имею в виду. Я также не вижу причин, по которым вы хотели бы это сделать. – matt

ответ

10

Как матовое уже пояснялось, вы не можете определить инициализатор, который является ограниченным типам, реализующих протокол. Кроме того, вы могли бы определить глобальную функцию вместо:

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

, который затем используется в качестве

let obj = createInstance(Entity.self, context) 

Вы можете избежать дополнительного параметра типа, если вы определяете метод , как

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

и использовать его как

let obj : Entity = createInstance(context) 

или

let obj = createInstance(context) as Entity 

где тип теперь выводится из контекста.

+0

Это похоже на то, что я хотел иметь, но все же есть требование передать тип в параметрах метода, и я хотел избежать этого , Как описано в моем ответе, я ошибся в своем понимании проблемы;) Спасибо за этот фрагмент в любом случае! –

+1

@TomaszSzulc: Смотрите обновление (но я не уверен, какая версия лучше) –

+0

Мне нравится это решение;) Спасибо! –

2

Мне кажется, что вы описываете что-то вроде этого:

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 

В реальной жизни, я полагаю, что makeANewThing бы на самом деле сделать что-то с его caller, но дело в том, что это может быть только вызванный путем передачи caller, который принял ThingMaker.

Это, вероятно, самое лучшее, что вы можете сделать в Swift 1. Если вы хотите вставить метод только в классы, которые принимают определенный протокол, то то, что вы хотите, является расширением протокола, но доступно только в Swift 2.

+0

Однако в вашем фактическом примере у вас появятся дополнительные трудности: вы не сможете получить статическое свойство 'name' того, что было введено как имя, по причинам, которые я объясняю здесь: http: // stackoverflow. com/a/30824093/341994 Опять же, решение заключается в обновлении до Swift 2. – matt

0

Хорошо, спасибо за ваши комментарии по этой теме. Я понял с комментариями @matt, что я не могу делать то, что думал, потому что это даже невозможно. Я не хотел создавать подкласс, чтобы он работал, и это невозможно с моим пониманием проблемы.

Наконец-то я нашел другой способ получить имя сущности. У этого есть плюсы и минусы, но я решил использовать его сейчас. Затем я создал расширение для NSManagedObject, чтобы заставить его работать.

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

А потом

obj = Obj(context: ctx)