객체에 키 값 옵저버가 연결되어 있는지 어떻게 알 수 있습니까?


142

객관적인 c 객체에 removeObservers를 지시하면 키 경로와 해당 키 경로가 등록되지 않은 경우 슬픔이 발생합니다. 처럼 -

'키 경로 "theKeyPath"에 대한 옵저버는 옵저버로 등록되지 않았기 때문에 옵저버를 제거 할 수 없습니다.'

객체에 등록 된 관찰자가 있는지 확인하는 방법이 있습니까? 그래서이 작업을 수행 할 수 있습니다

if (object has observer){
  remove observer
}
else{
  go on my merry way
}

이 시나리오에서는 뷰 컨트롤러가 할당 해제되고 "제거 할 수 없음"예외가 발생하는 iOS 8의 이전 앱을 업데이트했습니다. 나는 호출하여 그 생각 addObserver:viewWillAppear:및 대응 removeObserver:viewWillDisappear:, 통화가 제대로 짝을했다. try-catch 솔루션을 구현하고 원인을 자세히 조사하기 위해 의견을 남길 수 있도록 빠른 수정을해야합니다.
bneely

나는 비슷한 것을 다루고 있으며 디자인을 더 깊이 들여다보고 관찰자를 다시 제거 할 필요가 없도록 조정해야한다는 것을 알았습니다.
Bogdan

이 답변에서 제안한 것처럼 부울 값을 사용하면 나에게 가장 효과적이었습니다. stackoverflow.com/a/37641685/4833705
Lance Samaria

답변:


315

removeObserver 호출을 시도해보십시오

@try{
   [someObject removeObserver:someObserver forKeyPath:somePath];
}@catch(id anException){
   //do nothing, obviously it wasn't attached because an exception was thrown
}

12
1+ 좋은 답변, 저를 위해 일했으며 편집하기 전에 당신의 고백에 동의합니다.
Robert

25
내가 동의했을 가능성이있는 삭제 된 rant에 대해 찬성했다.
Ben Gotow

12
다른 우아한 솔루션이 없습니까? 이것은 사용량 당 최소 2ms가 걸린다 ... tableviewcell에서 그것을 상상해라
João Nunes

19
프로덕션 코드에서는 안전하지 않으며 언제든지 실패 할 가능성이 있다고 말하지 않기 때문에 하향 조정되었습니다. Cocoa에서는 프레임 워크 코드를 통한 예외 발생이 옵션이 아닙니다.
Nikolai Ruhe

6
신속한 2.1에서이 코드를 사용하는 방법. {self.playerItem? .removeObserver (self, forKeyPath : "status") 시도} NSError {print (error.localizedDescription)} 경고로 오류를 잡으십시오.
Vipulk617

37

진짜 질문은 왜 당신이 그것을 관찰하고 있는지 알지 못하는 이유입니다.

관찰중인 객체의 클래스 에서이 작업을 수행하는 경우 중지하십시오. 무엇을 관찰하든 계속 관찰해야합니다. 관찰자가 모르는 상태에서 관찰자의 알림을 차단하면 문제가 발생할 것으로 예상합니다. 보다 구체적으로, 이전에 관찰 된 객체로부터 업데이트를받지 않기 때문에 관찰자의 상태가 오래되지 않을 것으로 예상하십시오.

관찰 객체의 클래스에서이 작업을 수행하는 경우 관찰중인 객체를 기억하십시오 (또는 관찰중인 객체 중 하나만 관찰하는 경우). 이것은 관측이 역동적이고 다른 두 관련이없는 물체 사이에 있다고 가정하고있다. 관찰자가 관찰을 소유 한 경우 관찰을 생성 또는 유지 한 후에 관찰자를 추가하고 관찰을 해제하기 전에 관찰자를 제거하십시오.

관찰자로 객체를 추가하고 제거하는 것은 일반적으로 관찰자의 클래스에서 발생해야하며 관찰 된 객체에서는 발생하지 않아야합니다.


14
사용 사례 : viewDidUnload 및 dealloc에서 옵저버를 제거하려고합니다. 이것은 두 번 제거하고 viewController가 메모리 경고에서 언로드 된 다음 해제되면 예외를 throw합니다. 이 시나리오를 어떻게 처리 할 것을 제안합니까?
bandejapaisa

2
@ bandejapaisa : 내 대답에서 말한 거의 : 관찰 중인지 추적하고 관찰중인 경우에만 중지하려고합니다.
피터 Hosey

41
아니요, 흥미로운 질문은 아닙니다. 이를 추적 할 필요는 없습니다. 코드 경로가 추가 된 위치에 도달했는지 여부를 신경 쓰지 않고, 할당 해제 상태에서 모든 리스너를 간단히 등록 취소 할 수 있어야합니다. NSNotificationCenter의 removeObserver와 같이 작동해야합니다. 실제로는 가지고 있는지 여부는 신경 쓰지 않습니다. 이 예외는 단순히 존재하지 않는 버그를 생성하는 것인데, 이는 API 디자인이 잘못되었습니다.
Glenn Maynard

1
@GlennMaynard : 답변에서 말했듯이“관찰자가 모르는 상태에서 알림을 차단하면 문제가 발생할 것으로 예상됩니다. 보다 구체적으로, 이전에 관찰 된 객체로부터 업데이트를받지 않기 때문에 관찰자의 상태가 오래되지 않을 것으로 예상합니다.” 모든 관찰자는 자체 관찰을 끝내야합니다. 그렇지 않으면 이상적으로는 눈에 잘 띄어 야합니다.
Peter Hosey

3
이 질문에는 다른 코드의 옵저버를 제거하는 것에 대한 언급이 없습니다 .
Glenn Maynard

25

FWIW는 [someObject observationInfo]것 같다 nil경우 someObject어떤 관찰자가 없습니다. 그러나 문서화되지 않은이 동작은 신뢰할 수 없습니다. 또한 observationInfo특정 관찰자를 얻기 위해 읽는 방법을 모르겠습니다 .


특정 옵저버를 검색하는 방법을 알고 있습니까? objectAtIndex:원하는 결과를 얻지 못합니다.)
Eimantas

1
@MattDiPasquale 코드에서 관찰 정보를 읽는 방법을 알고 있습니까? 인쇄물에서는 잘 나오지만 무효를 가리키는 포인터입니다. 어떻게 읽어야합니까?
neeraj

observationInfo는 Xcode의 디버깅 페이퍼 (제목에 "magic"이있는 것)에 문서화 된 디버깅 방법입니다. 찾아 볼 수 있습니다. 누군가가 당신의 물체를 관찰하고 있는지 알 필요가 있다면 뭔가 잘못하고 있다고 말할 수 있습니다. 아키텍처와 논리를 재고하십시오. 어려운 방법을 배웠습니다.)
Eimantas

출처 :NSKeyValueObserving.h
nefarianblack

코믹한 막 다른 골목이지만 여전히 도움이되는 답변에 1을 더한 것
Will Von Ullrich

4

이를 수행하는 유일한 방법은 관찰자를 추가 할 때 플래그를 설정하는 것입니다.


3
모든 곳에서 BOOL을 사용하면 관찰자를 추가하고 제거하는 것을 처리하는 KVO 래퍼 객체를 만드는 것이 좋습니다. 관찰자를 한 번만 제거 할 수 있습니다. 우리는 이와 같은 객체를 사용했으며 작동합니다.
bandejapaisa

항상 관찰하지 않는 것이 좋습니다.
Andre Simon

4

관찰자를 객체에 추가하면 다음 NSMutableArray과 같이 추가 할 수 있습니다 .

- (void)addObservedObject:(id)object {
    if (![_observedObjects containsObject:object]) {
        [_observedObjects addObject:object];
    }
}

객체를 관찰하지 않으려면 다음과 같이 할 수 있습니다.

for (id object in _observedObjects) {
    if ([object isKindOfClass:[MyClass class]]) {
        MyClass *myObject = (MyClass *)object;
        [self unobserveMethod:myObject];
    }
}
[_observedObjects removeAllObjects];

단일 객체를 관찰 할 수없는 경우 _observedObjects배열 에서 객체를 제거하십시오 .

- (void)removeObservedObject:(id)object {
    if ([_observedObjects containsObject:object]) {
        [_observedObjects removeObject:object];
    }
}

1
이 멀티 스레드 세계에서 일어나는 경우에, 당신은 당신의 배열이 스레드 있는지 확인해야합니다
shrutim

객체에 대한 강력한 참조를 유지하면 객체가 목록에 추가 될 때마다 유지 횟수가 증가하고 참조가 배열에서 제거되지 않으면 할당이 해제되지 않습니다. 약한 참조를 유지하기 위해 NSHashTable/ NSMapTable를 사용 하는 것이 좋습니다.
atulkhatri

3

내 생각에-이것은 retainCount 메커니즘과 유사하게 작동합니다. 현재 시점에서 관찰자가 있는지 확신 할 수 없습니다. 확인하더라도 :self.observationInfo 미래에 옵저버가 있는지 여부를 확실하게 알 수 없습니다.

마찬가지로 retainCount . 아마도 observationInfo 메소드는 그다지 쓸모없는 것은 아니지만 디버그 목적으로 만 사용합니다.

결과적으로 메모리 관리와 마찬가지로해야합니다. 관찰자를 추가 한 경우 필요하지 않은 경우 관찰자를 제거하십시오. viewWillAppear / viewWillDisappear 등의 메소드를 사용하는 것과 같습니다. 예 :

-(void) viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self addObserver:nil forKeyPath:@"" options:NSKeyValueObservingOptionNew context:nil];
}

-(void) viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    [self removeObserver:nil forKeyPath:@""];
}

또한 특정 검사가 필요합니다. 옵저버 배열을 처리하고 검사에 사용하는 자체 클래스를 구현하십시오.


[self removeObserver:nil forKeyPath:@""]; 전에 갈 필요 : [super viewWillDisappear:animated];
여호수아 하트

@JoshuaHart 왜?
quarezz

그것은 해체 방법이기 때문에 (dealloc). 어떤 종류의 분류 방법을 재정의하면 super last를 호출합니다. 마찬가지로 : - (void) setupSomething { [super setupSomething]; … } - (void) tearDownSomething { … [super tearDownSomething]; }
여호수아 하트

viewWillDisapear 는 분리 방법이 아니며 dealloc과 관련이 없습니다. 탐색 스택을 앞으로 밀면 viewWillDisapear 가 호출되지만 뷰는 메모리에 남아 있습니다. 나는 당신이 setup / teardown의 논리로 어디로 가고 있는지 알지만 여기에서 그렇게하면 실제 이점이 없습니다. 기본 클래스에 논리가 있고 현재 관찰자와 충돌 할 수있는 경우에만 수퍼 앞에 제거를 배치하려고합니다.
quarezz

3

[someObject observationInfo]nil관찰자가없는 경우 반환

if ([tableMessage observationInfo] == nil)
{
   NSLog(@"add your observer");
}
else
{
  NSLog(@"remove your observer");

}

Apple docs에 따르면 : 측면 정보는 수신자에 등록 된 모든 관찰자에 대한 정보를 식별하는 포인터를 리턴합니다.
FredericK


2

관찰자 패턴의 요점은 관찰 된 클래스가 "밀봉"되도록하는 것입니다. 관찰되는지 여부를 알거나 신경 쓰지 않아야합니다. 이 패턴을 명시 적으로 해제하려고합니다.

왜?

당신이 가지고있는 문제는 당신이하지 않을 때 관찰되고 있다고 가정한다는 것입니다. 이 개체는 관측을 시작하지 않았습니다. 수업에서이 프로세스를 제어하도록하려면 알림 센터 사용을 고려해야합니다. 이렇게하면 수업 시간에 데이터를 볼 수있는 시간을 완전히 제어 할 수 있습니다. 따라서 누가보고 있는지는 상관하지 않습니다.


10
그는 청취자 가 무언가를 듣고 있는지 어떻게 알아낼 수 있는지 묻고 있는데, 관찰중인 물체가 어떻게 관찰되는지 알아낼 수는 없습니다.
Glenn Maynard

1

나는 그 catch catch 솔루션의 팬이 아니므로 대부분의 시간을 할애하는 것은 해당 클래스 내부의 특정 알림에 대한 구독 및 구독 취소 메소드를 작성하는 것입니다. 예를 들어 다음 두 가지 방법은 전역 키보드 알림에 개체를 구독하거나 구독 취소합니다.

@interface ObjectA : NSObject
-(void)subscribeToKeyboardNotifications;
-(void)unsubscribeToKeyboardNotifications;
@end

이러한 메서드 내에서 구독 상태에 따라 true 또는 false로 설정된 개인 속성을 사용합니다.

@interface ObjectA()
@property (nonatomic,assign) BOOL subscribedToKeyboardNotification
@end

@implementation

-(void)subscribeToKeyboardNotifications {
    if (!self.subscribedToKeyboardNotification) {
        [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(onKeyboardShow:) name:UIKeyboardWillShowNotification object:nil];
        [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(onKeyboardHide:) name:UIKeyboardWillHideNotification object:nil];
        self.subscribedToKeyboardNotification = YES;
    }
}

-(void)unsubscribeToKeyboardNotifications {
    if (self.subscribedToKeyboardNotification) {
        [[NSNotificationCenter defaultCenter]removeObserver:self name:UIKeyboardWillShowNotification object:nil];
        [[NSNotificationCenter defaultCenter]removeObserver:self name:UIKeyboardWillHideNotification object:nil];
        self.subscribedToKeyboardNotification = NO;
    }
}
@end

0

Adam의 답변 외에도 다음과 같은 매크로를 사용하는 것이 좋습니다.

#define SafeRemoveObserver(sender, observer, keyPath) \
@try{\
   [sender removeObserver:observer forKeyPath:keyPath];\
}@catch(id anException){\
}

사용법의 예

- (void)dealloc {
    SafeRemoveObserver(someObject, self, somePath);
}

1
예외가 발생한다는 것은 얼마나 미쳤습니까? 아무것도 첨부되어 있지 않으면 왜 아무것도하지 않습니까?
Aran Mulholland
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.