objective-c - 저장 - 파이어 베이스 데이터베이스 만들기




NSProxy 및 키 값 관측 (2)

NSProxy 는 아직 존재하지 않는 사람들을위한 개체로 잘 작동하는 것 같습니다. 예를 들어.

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    return [self.target methodSignatureForSelector:sel];
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    [invocation invokeWithTarget:self.target];
}

위의 코드는 메소드 호출을 프록시가 나타내는 대상으로 투명하게 전달합니다. 그러나 KVO 관찰 및 대상에 대한 알림을 처리하지 않는 것 같습니다. NSTableView 전달할 개체를 서있는 NSProxy 하위 클래스를 사용하려고했지만 다음 오류가 발생합니다.

Cannot update for observer <NSAutounbinderObservance 0x105889dd0> for
 the key path "objectValue.status" from <NSTableCellView 0x105886a80>,
 most likely because the value for the key "objectValue" has changed
 without an appropriate KVO notification being sent. Check the 
KVO-compliance of the NSTableCellView class.

KVO를 준수하는 투명 NSProxy 를 만드는 방법이 있습니까?


나는 OP의 똑같은 유스 케이스 (바인딩 없음)를 갖고 있지는 않지만 내 것은 비슷했다. NSProxy 서브 클래스를 만들어 서버에서 실제로로드 된 다른 객체로 표시한다. 로드하는 동안 다른 객체가 프록시에 가입 할 수 있으며 프록시는 객체가 도착하자마자 KVO를 전달합니다.

모든 관찰자를 기록하는 간단한 NSArray 속성이 프록시에 있습니다. 실제 객체가로드 될 때까지 프록시는 valueForKey: nil 을 반환합니다. realObject 가 도착하면 프록시는 실제 객체에 addObserver:forKeyPath:options:context: 를 호출 한 다음 런타임의 마법을 통해 realObject 모든 속성을 realObject 하고 다음을 수행합니다.

    id old = object_getIvar(realObject, backingVar);
    object_setIvar(realObject, backingVar, nil);
    [realObject willChangeValueForKey:propertyName];
    object_setIvar(realObject, backingVar, old);
    [realObject didChangeValueForKey:propertyName];

이것은 작동하는 것 같습니다, 적어도 나는 KVO 준수 오류를 아직받지 못했습니다. 그것은 의미가 있지만, 먼저 모든 속성이 nil이고 그 다음 nil에서 실제 값으로 변경됩니다. 위의 첫 성명서에서 ipmcc가 말했듯이이 게시물은 단지 확인 일뿐입니다! 그가 제안한 두 번째 대리모는 실제로 필요하지 않습니다. 관찰자를 직접 추적해야합니다.


이 문제의 요점은 NSObject 에서 Key-Value Observing의 삶과 NSProxyNSObject 에서 상속받지 않는다는 것입니다. NSProxy 객체가 자체 목록을 유지해야한다는 접근법이 필요하다고 합리적으로 확신합니다 (외부 사람들이 관측하고자하는 바가 무엇인지). 이것만으로도 NSProxy 구현에 상당한 가중치가 추가됩니다.

목표 준수

프록시의 관찰자가 실제로 실제 객체를 관찰하도록 시도한 것처럼 보입니다. 즉, 대상이 항상 채워져 있고 모든 호출을 대상으로 전달한 경우 addObserver:... 전달할 수도 있습니다 addObserver:...removeObserver:... 호출. 이 문제는 당신이 말하기 시작했습니다 :

NSProxy는 아직 존재하지 않는 사람들을위한 객체로서 잘 작동하는 것 같습니다.

완벽을 기하기 위해이 접근 방법에 대해 설명하고 왜 작동하지 않는지에 대해 설명합니다 (적어도 일반적인 경우).

이 작업을 수행하려면 NSProxy 서브 클래스가 대상이 설정되기 전에 호출 된 등록 메소드의 호출을 수집 한 다음 대상이 설정되면 대상에 전달해야합니다. 이것은 제거 작업도 처리해야한다고 생각할 때 신속하게 털이됩니다. 관찰 객체가 dealloc 될 수 있기 때문에 이후에 제거 된 관측을 추가하기를 원하지 않을 것입니다. 또한 관측자를 유지하기 위해 관측자를 추적하는 방법으로 인해 의도하지 않은 유지주기가 발생하지 않도록하는 것이 좋습니다. 처리해야 할 대상 값에서 다음과 같은 가능한 전환이 발생합니다.

  1. 목표는 초기화시 nil 이었고 나중에는 비 nil 되었습니다.
  2. 대상이 nil 이 아닌 것으로 설정되어 나중에 nil 됩니다.
  3. 타겟이 nil 이 아닌 것으로 설정된 다음 다른 nil 아닌 값으로 변경됩니다.
  4. 타겟이 nil (init이 아님)이었고, 나중에 nil 이되지 않습니다.

# 1의 경우 문제가 발생합니다. KVO 관측자가 objectValue 관찰했을 때 (즉, 항상 프록시가 될 것이므로) 여기 괜찮을 것입니다. 그러나 관측자가 proxy / real-object, 즉 objectValue.status 통과하는 keyPath를 관찰했다고 말합니다. 이것은 KVO 기계가 관측 대상에서 valueForKey: objectValue 를 호출하고 프록시를 다시 얻었을 때 프록시에서 valueForKey: status 를 호출하여 되돌릴 수 nil 합니다. 타겟이 non- nil 되면 KVO는 그 값이 KVO를 준수하지 않는 것으로부터 변경된 것으로 간주하고 인용 한 오류 메시지가 표시됩니다. 일시적으로 타겟이 status 에 대해 nil 을 반환하도록 강요 한 경우 해당 동작을 켜고 -[target willChangeValueForKey: status] 를 호출하고 비헤이비어를 해제 한 다음 -[target didChangeValueForKey: status] 를 호출 할 수 있습니다. 어쨌든, 같은 함정을 가지고 있기 때문에 사건 # 1에서 여기서 멈출 수 있습니다 :

  1. willChangeValueForKey: 를 호출하면 아무 일도하지 않을 것입니다 (즉, KVO 기계가 nil 로의 전환 도중 내부 상태를 업데이트하지 못할 수도 있습니다)
  2. 어떤 대상 객체가 일시적으로 거짓말을하고 valueForKey에서 nil 을 반환하는 메커니즘을 갖도록 강요합니다. 명시된 욕망이 "투명한 프록시"일 때 모든 키에 대해 꽤 까다로운 요구 사항처럼 보입니다.
  3. 그것은 심지어 setValue : forKey :를 호출하는 것을 의미합니까? 우리는 그 가치들을 주변에 유지합니까? 진짜 표적을 기다리고 있니? 우리가 던집니까? 거대한 공개 문제.

이 접근법의 한 가지 가능한 수정은 실제 대상이 nil NSMutableDictionary 비어있는 NSMutableDictionary 일 때 대리 대상을 사용하고 대리모에게 KVC / KVO 호출을 전달하는 것입니다. 이것은 willChangeValueForKey:nil 로 의미있게 호출 할 수 없다는 문제를 해결합니다. 관찰의 목록을 유지했다고 가정 할 때, KVO가 사건 1에서 목표를 세우는 것과 관련된 다음 순서를 용인 할 것이라는 낙관은 아닙니다.

  1. 외부 관찰자 호출 -[proxy addObserver:...] , 대리 대리 대리에게 전달
  2. 프록시 호출 -[surrogate willChangeValueForKey: 대상이 설정되기 때문에 -[surrogate willChangeValueForKey: ]
  3. 프록시 호출 -[surrogate removeObserver:... ] 대리모에서
  4. 프록시 호출 -[newTarget addObserver:...] 새 대상에서 -[newTarget addObserver:...]
  5. 프록시 호출 -[newTarget didChangeValueForKey: ]로 통화 # 2의 균형을 조정합니다.

이것이 나에게도 같은 오류로 이어지지는 않을 것이라는 것은 분명하지 않습니다. 이 모든 접근법은 정말 엉망이되어 가고 있습니다. 그렇지 않습니까?

나는 몇 가지 다른 아이디어를 가지고 있었지만, # 1은 상당히 사소한 것이고 # 2와 # 3은 충분히 단순하지 않거나 자신감을 불어 넣어서 코드를 작성하는 데 시간을 소비하게 만들 수 있습니다. 하지만, 후손을 위해, 어때요 :

1. 프록시에 NSObjectController 사용

물론, 그것은 컨트롤러를 통과하기 위해 여분의 키를 사용하여 keyPaths를 gums지만, 이것은 NSObjectController's 일종의 일종의 권리, 맞죠? 그것은 nil 컨텐츠를 가질 수 있으며 모든 관찰 설정 및 해제를 처리합니다. 투명한 호출 전달 프록시의 목표를 달성하지는 못하지만, 예를 들어 비동기 적으로 생성 된 객체에 대한 목표를 세우는 것이 목표라면 비동기 생성 작업을 통해 최종적으로 전달할 수 있습니다. 컨트롤러에 객체. 이것은 아마도 최저 노력 접근이지만, '투명'요구 사항을 실제로 다루지는 않습니다.

2. 프록시에 NSObject 서브 클래스 사용하기

NSProxy's 주요 기능은 그 안에 마법이 있다는 것이 아닙니다 . 주된 기능은 NSObject 구현 (모든) 구현에 포함되어 있지 않다는 것입니다. 원하지 않는 모든 NSObject 동작을 무시하고 포워딩 메커니즘으로 다시 되돌리려면 NSProxy 제공하지만 KVO 지원 메커니즘을 통해 동일한 순 가치를 얻을 수 있습니다 그 자리에 남았습니다. 거기에서 관찰 된 대상의 모든 주요 경로를보고 대리인의 문제를 재방송합니다. willChange... didChange... 외부의 관찰자가 자신의 didChange... 임을 알 수 있도록 대상에서 알림을 변경합니다. 대리.

... 그리고 지금은 정말 미친 일이 있습니다.

3. (Ab) 런타임을 사용하여 NSProxy 서브 클래스에 NSObject KVC / KVO 동작을 가져옵니다.

런타임을 사용하여 NSObject (예 : class_getMethodImplementation([NSObject class], @selector(addObserver:...)) )에서 KVC 및 KVO와 관련된 메소드 구현을 class_getMethodImplementation([NSObject class], @selector(addObserver:...)) 다음 해당 메소드를 추가 할 수 있습니다 (예 : class_addMethod([MyProxy class], @selector(addObserver:...), imp, types) )를 프록시 서브 클래스에 추가합니다.

이것은 공개 KVO 메서드가 호출하는 NSObject 모든 개인 / 내부 메서드를 파악한 다음 그 메서드를 상속하는 메서드 목록에 추가하는 추측 및 확인 프로세스로 이어질 가능성이 높습니다. KVO 준수를 유지하는 내부 데이터 구조가 NSObject ivars에서 유지되지 않는다고 가정하는 것이 논리적 인 것처럼 보입니다. ( NSObject.h 는 ivars가 없음을 의미합니다. 요즘 의미는 아닙니다.) 모든 NSObject 인스턴스가 공간 가격. 또한 KVO 알림 스택 추적에서 많은 C 함수를 볼 수 있습니다. 아마 당신이 NSProxy가 KVO의 1 등석 참가자가되기에 충분한 기능을 도입 한 시점에 도달했다고 생각합니다. 그 시점부터이 솔루션은 NSObject 기반 솔루션처럼 보입니다. 당신은 목표를 관찰하고 그들이 당신에게서 온 것처럼 통보를 재방송합니다. 추가적으로 목표를 변경합니다. 변경 사항을 변경 / 변경합니다. 호출 전달 메커니즘에서 KVO 공용 API 호출을 입력 할 때 플래그를 설정 한 다음 공개 API가 실행될 때 플래그를 지울 때까지 호출 된 모든 메소드를 가져 오려고 시도하여이 중 일부를 자동화 할 수도 있습니다 호출 반환 - 그 방법을 가져 오지 않으면 프록시의 투명성이 손상되지 않는다는 것을 보장하려고하는 장애가 있습니다.

KVO가 런타임에 클래스의 동적 하위 클래스를 생성하는 메커니즘에 이것이 떨어질 것으로 의심됩니다. 이 메커니즘의 세부 사항은 불투명하며, 아마도 NSObject 에서 가져 오는 개인 / 내부 메서드를 파악하는 또 다른 긴 열차로 이어질 것입니다. 결국이 접근법은 내부 구현 세부 사항이 변경되지 않도록 완전히 연약합니다.

...결론적으로

초록에서 문제는 KVO가 중요한 공간에 일관성 있고, 알기 쉽고, 지속적으로 업데이트되는 (알림을 통해) 상태를 기대한다는 사실로 귀결됩니다. ( -setValue:forKey: 또는 편집 가능한 바인딩을 지원하려면 해당 목록에 "mutable"을 추가하십시오.) 일류의 참가자가되는 것은 더러운 속임수를 -setValue:forKey: NSObjects 라는 것을 의미합니다. 사슬에서 이러한 단계 중 하나가 다른 내부 상태로 전화하여 기능을 구현하는 경우 그 특권이지만 KVO 준수에 대한 모든 의무를 이행 할 책임이 있습니다.

이러한 이유로, 나는 이러한 솔루션 중 하나라도 노력할 가치가 있다면 " NSObject 를 프록시로 사용하고 NSProxy 하지 말라"고 생각합니다. 그래서 귀하의 질문의 정확한 성격을 얻으려면 KVO 규격 인 NSProxy 서브 클래스를 만드는 방법이있을 있지만, 그럴 가치가있는 것처럼 보이지는 않습니다.





nsproxy