0

현재 iOS 용 양식 컨트롤러를 다시 쓰고 있습니다. 그것은 모델에 바인딩 된 사용자 지정 개체이며, 양식 필드 편집, 이전/다음 필드로 이동, 사용자 지정 키보드 처리, 데이터 유효성 검사 등을 처리합니다.양방향 KVO : 컨트롤러 업데이트 모델

첫 번째 버전은 양식을 저장하기위한 plist 값이 있으면 폼 컨트롤러는 모든 데이터 자체를 보유합니다. 이제 저장소 (모델)와 양식 컨트롤러를 분리하려고합니다. 따라서 KVO를 사용하여 정착했습니다.

간단히 말하면 부재중 시간대를 편집하도록 설계된 양식이 있다고 가정 해 보겠습니다. 따라서 두 개의 필드가 있습니다 : leaveDatereturnDate. 다음과 같이

내 모델은 다음과 같습니다

@interface Absence 
    @property (strong, nonatomic) NSDate *leaveDate; 
    @property (strong, nonatomic) NSDate *returnDate; 
    @property (readonly, nonatomic) BOOL isValid; 
@end 

내 폼 컨트롤러는이 객체를 가리키는 속성 model 있습니다.

사용자가 내 XIB의 "leave date"텍스트 필드를 누르면 Form Controller는 내 모델의 leaveDate의 현재 값을 기반으로 날짜 선택 도구를 입력하고 표시합니다. 사용자가 다른 날짜를 선택하면 양식 컨트롤러가 setValue:forKey:을 사용하여 모델을 업데이트합니다.

isValid 속성 (+keyPathsForValuesAffectingIsValid 사용) leaveDatereturnDate 영향 것으로 선언되고, 폼 컨트롤러는 즉시 제출 버튼을 활성화/비활성화,이 속성의 변화를 시청 등록했습니다.

지금까지 모든 것이 매력처럼 작동합니다. 이제 꼬인 부분 :

열려있는 동안 내 양식 컨트롤러가 모델의 변경 사항을 처리 할 수있게하려고합니다. 예 : 모델에 "지난 3 일 이상 부재 중이어야합니다"라는 규칙이 있습니다. 사용자가 휴가 날짜를 변경하면 총 기간이 3 일을 초과하지 않으면 반환 날짜가 자동으로 조정됩니다.

그래서 내 양식 컨트롤러는 모든 속성의 변경 사항을 수신하기 위해 등록해야합니다. 문제는 속성을 변경하고 변경 내용을 수신한다는 것입니다.

그런 식으로 사용자가 leaveTime을 변경할 때 양식 컨트롤러는 setValue:forKey:을 사용하여 모델을 업데이트하지만 방금 한 바로 그 변경 사항에 대한 KVO 알림을 즉시받습니다. 이것은 불필요하고 잠재적으로 해롭다 (나는 방금 나 자신에게 변화를 가져왔다, 나는 방금 그것을했다는 말을들을 필요가 없다).

[self.model removeObserver:self forKeyPath:self.currentField.key]; 
[self.model setValue:newValue forKey:self.currentField.key]; 
[self.model addObserver:self forKeyPath:self.currentField.key options:NSKeyValueObservingOptionNew context:nil]; 
그것은 작동하고

을하지만 추한 및 성능 - :

유일한 방법의 주위에 지금 바로 새 값을 설정하기 전에 다음과 같이 직후 등록 재 등록 해제 될 때까지 발견 현명한 나는 그게 좋다고 생각하지 않는다.

더 나은 방법에 대한 설명이 있습니까?

TL; DR

ControllerAModel의 등록 KVO 옵저버이다.

ControllerB 업데이트 Model ==>ControllerA은 KVO 알림을받습니다. 괜찮아.

ControllerA 업데이트 Model ==>ControllerA은 KVO 알림을받습니다. 나는 이것을 원하지 않는다.

+0

방금 ​​전에 변경 한 KVO 알림을 듣는 것에 대해 위험한 것은 무엇입니까? 나는 그 옵션이 KVO 관측자로서 자신을 제거한 다음 다시 추가하는 것보다 덜 복잡하고 잠재적으로 해롭다 고 생각합니다. – occulus

+0

모델을 업데이트하기 전에 마지막으로 수행 한 작업이'textField.text = @ "something";'일 때'textField.text = @ "something"; "을 실행하고 싶지 않습니다. 그것이 자주 발생하는 더 값 비싼 UI 업데이트 (큰 컨트롤의 다시 그리기, 이미지 처리 ...) (슬라이더와 같은 개별 컨트롤)를 상상해보십시오. – Cyrille

+0

알림을받는 것이 멱등 한 작업임을 확신 할 수 있으면 동일한 알림을 여러 번받는 것이 중요하지 않아야합니다. – occulus

답변

2

성능에 대한 우려가 있습니다. 나는 없을거야. 드로잉은 메인 실행 루프에 의해 통합되므로, textField.text = @"foo";을 설정하면 드로잉, 이미지 처리 등이 인라인되지 않도록해야합니다. 일반적으로 그와 같은 setter는 값을 설정 한 다음 플래그를 설정하는 [self setNeedsDisplay]을 호출하고 (매우 값 싸게) 실행 루프의 끝에서 그리기 시스템이 단일 다시 그리기를 트리거합니다. textField.text을 천 번 설정할 수 있으며 그리기 작업은 한 번만 수행해야합니다.

코멘트 작성자가 제안했듯이 컨트롤러가 여러 업데이트를 허용하도록 설정해야합니다. 세터와 함께 여러 가지 일을하고 있다면 그렇게하지 마십시오. 세터는 "벙어리"해야합니다. 그들은 값을 설정하고 필요하다면 플래그를 설정해야합니다 (예 : setNeedsDisplay). 이와 같은 상황에서는 세터에서 "실제 작업"을하지 않는 것이 좋습니다.

또 다른 주석가가 제안했듯이 UI를 업데이트하지 않아도되고 KVO가 모든 관찰자에게 변경 사항을 적용하도록 할 수 있습니다. 을 포함하여 변경을 초래 한 컨트롤러는입니다.

정말 이러한 접근 방식 중 일부는 효과가 있지만 성능에 대한 우려는 근거없는 것으로 판단됩니다. 성능 문제가있는 경우 문제는 여러 업데이트가 아니라 각 업데이트 중에 플래그를 설정하고 나중에 작업해야 할 때 실제 작업을 수행한다는 것입니다.