2017-10-31 6 views
1

NSManagedObject 클래스 계층 구조가 있다고 가정합니다.슈퍼 클래스에 바인딩하여 사용자 지정 프로토콜을 스위프트 하위 클래스로 채택

class Dog: NSManagedObject { 
    var name: String? 
} 

class Person: NSManagedObject { 
    var name: String? 
} 

물론 코드를 복제하지 않으실 것입니다.

프로토콜을 선언하십시오.

protocol Has_Name_Protocol { 
    var name: String? {get} 
} 
extension Has_Name_Protocol { 
    static func predicate(name: String?) -> NSPredicate { 
     return predicate(names: [name]) 
    } 

    static func predicate(names: [String?]?) -> NSPredicate { 
     return NSPredicate(format: "name == %@", argumentArray: names?.flatMap({$0})) 
    } 
} 

물론 충분하지 않습니다. 개체를 검색하는 find 메소드를 소개하고 싶습니다.

따라서 프로토콜을 클래스 계층 구조의 가장 낮은 경계에 바인딩해야합니다.

extension Has_Name_Protocol where Self: NSManagedObject { 
    static func find(name: String?, context: NSManagedObjectContext) -> Self? { 
     return nil 
    } 
} 

또한이 프로토콜에 맞는 하위 클래스의 확장을 추가하십시오.

extension Dog: Has_Name_Protocol {} 
extension Person: Has_Name_Protocol {} 

올바른 방법인가요?

답변

1

associatedtype을 추가하여 유형으로 사용할 수 있습니다. 편의상 entityType을 추가해야합니다.

name을 비표준으로 선언하는 것을 고려하십시오. 이렇게하면 모든 언 래핑 및 플랫 매핑이 수행되지 않습니다. 그리고 find 방법 throws하고 패스를 통해 코어 데이터 오류

테스트되지 않은 코드 물론

protocol HasNameProtocol { 
    associatedtype FetchableType: NSManagedObject = Self 
    static var entityName : String { get } 
    var name: String {get} 

    static func predicate(name: String) -> NSPredicate 
    static func find(name: String, context: NSManagedObjectContext) throws -> [FetchableType] 

} 

extension HasNameProtocol where FetchableType == Self {  

    static var entityName : String { 
     return NSStringFromClass(self).components(separatedBy: ".").last! 
    } 

    static func predicate(name: String) -> NSPredicate { 
     return NSPredicate(format: "name == %@", name) 
    } 

    static func find(name: String, context: NSManagedObjectContext) throws -> [FetchableType] { 
     let request = NSFetchRequest<FetchableType>(entityName: entityName) 
     let predicate = predicate(name: name) 
     request.predicate = predicate 
     return try context.fetch(request) 
    } 
} 
+0

생각해 보았지만 너무 복잡해 보입니다. 그러나 하위 클래스 확장에서 프로토콜을 채택하지 않으면 작동하지 않습니다. – gaussblurinc

+1

물론 NSManagedObject 하위 클래스의 프로토콜을 채택해야합니다. – vadian

0

, 당신은 코드를 복제하지 싶습니다.

이것은 프로토콜을 사용하는 이유가 아닙니다. Protocols are more than a bag of syntax. 먼저 "name의 이러한 사용법은 공유 구현이 중요한 의미를 갖는 의미 론적 동일성을 지니고 있습니까?" "이 수업에는 동일한 키 입력이 포함되어 있습니까?"라는 질문을해서는 안됩니다. 그것은 우연히 일어날 수 있으며 의미 론적으로 다른 경우 다른 방식으로 진화 할 수 있기 때문에 병합하는 좋은 이유가 아닙니다.

extension Has_Name_Protocol { 
    static func predicate(name: String?) -> NSPredicate { 
     return predicate(names: [name]) 
    } 

    static func predicate(names: [String?]?) -> NSPredicate { 
     return NSPredicate(format: "name == %@", argumentArray: names?.flatMap({$0})) 
    } 
} 

프로토콜의 확장에서 원하는 일관성이 부족합니다. nameString 일 때 String?을 전달하는 것이 자연스러운 이유는 무엇입니까?

func predicate(property: String, values: [String]) -> NSPredicate { 
    return NSPredicate(format: "\(property) == %@", argumentArray: values) 
} 

func namePredicate(names: [String]) -> NSPredicate { 
    return predicate(property: "name", values: names) 
} 

이렇게하면 가치있는 추상화 계층을 만드는 대신 구문을 바꿔 쓰는 것처럼 보입니다.

static func find(name: String?, context: NSManagedObjectContext) -> Self? 

이 서명은 혼란 스럽습니다. name은 왜 선택입니까? 이것이 적용되는 모든 것이 이름을 가져야한다고 말했을 때 "이름이없는 것을 발견하는 것"이란 의미는 무엇입니까?

find의 많은 구현이 구문과 의미에서 모두 동일하다는 것을 알게되면 기본 개념이 구문상의 편의로 유용합니다. 그러나 이러한 것들이 "그들은 이름이 있습니다. ."그것이 유일한 유일한 유사점이라면 그 프로토콜은 Named이 아니며 Has_Name_Protocol이 아니어야합니다.

+0

구체적인 데이터 예제의 일반적인 질문입니다. "Signature is confusing", 아니요, Core Data 생성 필드의 경우 혼동하지 않습니다. 다음 , "Named"는 다른 평범한 이름과 같은 프로토콜의 나쁜 이름입니다. 왜 프로토콜이 모듈 수준으로 범위가 지정 되었기 때문에 – gaussblurinc

+0

일반적인 것을 의미하지만, 구체적인 예를 만들면 이해할 수 없을 것입니다. 그는 똑같은 문제. Swift를 과도하게 사용하려고 시도하는 것은 개발자 혼란을 발견 한 # 1 원인입니다. 3 가지 구현이 생길 때까지 기다렸다가 일반을 빌드하십시오. 핵심 데이터는 문제를 변경하지 않습니다. 필드가 선택 사항이 아닌 경우 모델에서 선택 사항으로 표시하지 말고 Core Data가 선택적 특성을 작성하게하지 마십시오. 이것은 프로젝트 전체에서 코드를 극적으로 단순화합니다. 'Has_Name_Protocol'은 정확히'Named'와 같이 일반적인 것입니다. 일반성은 문제를 암시합니다 (충분하지는 않습니다). –