Swift 2.0: метод класса расширения протокола, возвращающий Self

Чтобы расширить некоторые функциональные возможности моих NSManagedObject подклассов, я определил ряд протоколов:

protocol ManagedObjectFindable {
    static func find(format:String, arguments: [AnyObject]?, inContext context:NSManagedObjectContext, entityName:String?) -> Self?
}

protocol UniquelyIdentifiable: ManagedObjectFindable {

    var identifier: NSNumber? { get }
    static func findWithIdentifier(identifier: Int, inContext context:NSManagedObjectContext) -> Self?
}

Таким образом, каждый NSManagedObject, имеющий идентификатор в своей модели данных, может соответствовать UniquelyIdentifiable.

Для этой цели я использую расширения протокола Swift 2.0, где:

extension UniquelyIdentifiable {

    static func findWithIdentifier(identifier: Int, inContext context:NSManagedObjectContext) -> Self? {
        return self.find("identifier == %lld", arguments: [NSNumber(longLong: Int64(identifier))], inContext: context, entityName:nil)
    }
}

Где найти определяется как:

extension NSManagedObject: ManagedObjectFindable {
    /** returns single entity if found, nil otherwise */
    class func find(format:String, arguments: [AnyObject]?, inContext context:NSManagedObjectContext, entityName:String? = nil) -> Self? {

        let objectEntityName:String
        if let name = entityName {
            objectEntityName = name
        } else {
            objectEntityName = String(self)
        }

        let fetchRequest = NSFetchRequest()
        fetchRequest.entity = NSEntityDescription.entityForName(objectEntityName, inManagedObjectContext: context)
        fetchRequest.fetchLimit = 1
        fetchRequest.predicate = NSPredicate(format: format, argumentArray: arguments)

        var persistentEntityº:NSManagedObject?
        context.performBlockAndWait {

            do {
                let fetchResults = try context.executeFetchRequest(fetchRequest)
                if (fetchResults.count != 0){
                    persistentEntityº = fetchResults.first as? NSManagedObject
                }
            } catch {}
        }

        if let persistentEntity = persistentEntityº {
            return _safeObjectSelfCast(persistentEntity)
        } else {
            return nil
        }
    }
}
func _unsafeObjectSelfCast<T>(obj: AnyObject!) -> T { return obj as! T }
func _safeObjectSelfCast<T>(obj: AnyObject) -> T?   { return obj as? T }

Теперь эти методы корректно возвращают Self? и компилятор молчит о времени кодирования, однако при компиляции выдает эту ошибку Method 'findWithIdentifier(_:inContext:)' in non-final class must return 'Self' to conform to protocol 'UniquelyIdentifiable'

Теперь дело в том, что если вместо реализации этого метода в расширении протокола я просто расширил бы свой подкласс NSManagedObject, все будет хорошо, но это убивает цель расширений протокола, когда вы полностью дублируете один и тот же код в десятках ваших NSManagedObject подклассы.

Любое обходное решение, или я действительно что-то упускаю?


person ambientlight    schedule 19.11.2015    source источник
comment
Ваш код компилируется без ошибок в моем Xcode 7.1.1   -  person Martin R    schedule 19.11.2015
comment
@MartinR: попробуйте добавить в NSMOSubclass: extension SomeManagedObjectSubclass: UniquelyIdentifying {} В этом случае это не будет...   -  person ambientlight    schedule 19.11.2015


Ответы (1)


Короткий ответ: заменить Self? в расширении на NSManagedObject?.

Длинный ответ: Self в требовании к протоколу выступает в качестве заполнителя для класса, который будет реализовывать этот протокол. Итак, если у вас есть

protocol SomeProtocol {
   func returnSomething() -> Self
}

Это означает, что если вы реализуете ее на Int, функция returnSomething() должна возвращать Int, а если вы реализуете ее на Double, она должна возвращать Double.

Поскольку вы реализуете UniquelyIdentifiable на NSManagedObject, а ваш протокол имеет требование Self?, вы должны вернуть NSManagedObject?.

person Said Sikira    schedule 19.11.2015
comment
В моем случае он должен возвращать экземпляр необязательного (подкласс NSManagedObject), а не необязательный (NSManagedObject), да, это сработает, но вы хотите вернуть необязательный (NSManagedObjectSubclass) причину возврата Self? вместо NSManagedObject? это потому, что у вас может быть много разных подклассов NSManagedObject, и если подкласс соответствует этому протоколу, он уже будет реализовывать findWithIdentifier, который находится в реализации расширения протокола по умолчанию. - person ambientlight; 19.11.2015