KVO 요약 및 FBKVOcontroller
In order to be considered KVO-compliant for a specific property, a class must ensure the following:
문서에서 언급한 바와 같이 하나의 클래스의 속성이 KVO를 지원하도록 해야 한다. 이 클래스는 속성에 대해 KVC를 만족시키고 이 클래스는 KVO에 대한 알림을 보낼 것이다.
KVO 알림을 보낼 수 있는 두 가지 기술이 있습니다.
Automatic Change Notification
NSObject provides a basic implementation of automatic key-value change notification. Automatic key-value change notification informs observers of changes made using key-value compliant accessors, as well as the key-value coding methods.
NSObject는 자동으로 변경되는 알림을 제공합니다.자동 알림은 두 가지가 있는데 하나는 속성을 사용하는 setter 방법이고 하나는 KVC를 사용합니다.
KVO의 원리
KVO의 실현은runtime에 의존한다. 애플 문서에서 KVO의 실현을 언급한 적이 있다.
Automatic key-value observing is implemented using a technique called isa-swizzling.
The isa pointer, as the name suggests, points to the object's class which maintains a dispatch table. This dispatch table essentially contains pointers to the methods the class implements, among other data.
When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class. As a result the value of the isa pointer does not necessarily reflect the actual class of the instance.
애플은 KVO를 위해 이사-swizzling을 사용했다.피관찰자의 속성에 관찰자를 추가하면 피관찰자의 이사 지침이 바뀌어 원래의 진정한 클라스가 아닌 중간의 클라스를 가리킨다.구체적으로 말하면 새로운 클래스를 만들 것이다. 이 클래스는 피관찰자로부터 계승되고 관찰된 속성의 setter 방법을 다시 썼다. 다시 쓴 setter 방법은 원래의 setter 방법을 호출하는 전후에 모든 관찰자에게 변화할 가치가 있음을 알린다
willChangeValueForKey
와 didChangeValueForKey
로 알린다.isa의 바늘을 새로 만든 하위 클래스에 가리킨다.-(void)setName:(NSString *)newName{
[self willChangeValueForKey:@"name"]; //KVO
[super setValue:newName forKey:@"name"]; //
[self didChangeValueForKey:@"name"]; //KVO }
다음 테스트 코드를 살펴보십시오.
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end
@implementation Person
@end
#import
#import "Person.h"
int main(int argc, char * argv[]) {
Person *p = [[Person alloc] init];
PrintDescriptionid(p);
[p addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
PrintDescriptionid(p);
return 0;
}
static NSArray *ClassMethodNames(Class c)
{
NSMutableArray *array = [NSMutableArray array];
unsigned int methodCount = 0;
Method *methodList = class_copyMethodList(c, &methodCount);
unsigned int i;
for(i = 0; i < methodCount; i++)
[array addObject: NSStringFromSelector(method_getName(methodList[i]))];
free(methodList);
return array;
}
static void PrintDescriptionid( id obj)
{
NSString *str = [NSString stringWithFormat:
@"NSObject class %s
Libobjc class %s
Super Class %s
implements methods ",
class_getName([obj class]),
class_getName(object_getClass(obj)),
class_getName(class_getSuperclass(object_getClass(obj))),
[ClassMethodNames(object_getClass(obj)) componentsJoinedByString:@", "]];
printf("%s
", [str UTF8String]);
}
log:
//
NSObject class Person
Libobjc class Person
Super Class NSObject
implements methods <.cxx_destruct name="" setname:="">
//
NSObject class Person
Libobjc class NSKVONotifying_Person
Super Class Person
implements methods
object_getClass(obj)는 obj 대상isa가 가리키는 클래스를 가져옵니다.log에서 알 수 있듯이 관찰자를 첨가한 후obj대상isa지침지향
NSKVONotifying_Person
이라는 종류는 부류Person
였고NSKVONotifying_Person
에서
라는 몇 가지 방법을 실현했다.Manual Change Notification
속성 알림을 완전히 제어하려면 다시 써야 합니다
automaticallyNotifiesObserversForKey:
+(BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{
BOOL automatic = NO;
if ([key isEqualToString:@"name"]) {
automatic = NO;
}
else {
automatic = [super automaticallyNotifiesObserversForKey:key];
}
return automatic;
}
실행하기 전의 코드 로그:
// KVO
Libobjc class Person
Super Class NSObject
implements methods <.cxx_destruct name="" setname:="">
// KVO
NSObject class Person
Libobjc class Person
Super Class NSObject
implements methods <.cxx_destruct name="" setname:="">
이때도
NSKVONotifying_Person
이라는 종류를 만들지 않을 것이다.수동으로 알림을 보내기 위해서, 값을 바꾸기 전에 willChangeValueForKey를 호출하고, 값을 바꾸면didChangeValueForKey를 호출합니다.KVO 사용:
[self.tableView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
이 방법은tableView에 관찰자를 추가하여tableView의content Offset 속성의 변화를 모니터링하는 것이다.이 방법은 방법의 호출자(self.tableView)와 관찰자(self)의 인용 계수를 증가시키지 않는다.
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(@"%@", change);
}
관찰자 중에서 이 방법을 실현하고 피관찰자의 피관찰자의 속성이 변화할 때 이 방법을 사용한다.
마지막으로 관찰자를 제거하는 것을 잊지 마세요.
- (void)dealloc
{
[self.tableView removeObserver:self forKeyPath:@"contentOffset"];
}
FBKVOController
페이스북에서 시작된 FBKVOcontroller 프레임워크는 KVO를 쉽게 사용할 수 있다.
FBKVOcontroller를 사용하면 위의 코드를 다음과 같이 바꿀 수 있습니다.
[self.KVOController observe:self.tableView keyPath:@"contentOffset" options:NSKeyValueObservingOptionNew block:^(id _Nullable observer, id _Nonnull object, NSDictionary * _Nonnull change) {
NSLog(@"%@", change);
}];
FBKVOcontroller에 NSObject의category가 있는데 NSObject에 두 가지 속성을 추가했습니다
@property (nonatomic, strong) FBKVOController *KVOController;
@property (nonatomic, strong) FBKVOController *KVOControllerNonRetaining;
KVOController를 사용할 때 관찰된 대상에 대해 강한 인용을 하고 KVOcontrollerNonRetaining을 사용하면 관찰된 대상에 대해 약한 인용을 한다.
FBKVOcontroller 클래스에는 다음과 같은 인스턴스 변수가 있습니다.
NSMapTable *> *_objectInfosMap;
NSMapTable의 키는 관찰된 대상을 저장하고 초기화 방법에서 강한 인용이나 약한 인용으로 설정할 수 있다.그것의value는 에 저장됩니다FBKVOInfo 대상은 주로 피관찰자의 키Path 등에 대한 정보이다.FBKVOcontroller 사용
-observer:keyPath:options:block:
대상의 속성 변화를 관찰할 때 사용한FBKVOShared Controller라는 클래스는 하나의 예로 관찰자를 추가한 실례적인 방법입니다.- (void)observe:(id)object info:(nullable _FBKVOInfo *)info
{
if (nil == info) {
return;
}
// register info
pthread_mutex_lock(&_mutex);
[_infos addObject:info];
pthread_mutex_unlock(&_mutex);
// add observer
[object addObserver:self forKeyPath:info->_keyPath options:info->_options context:(void *)info];
if (info->_state == _FBKVOInfoStateInitial) {
info->_state = _FBKVOInfoStateObserving;
} else if (info->_state == _FBKVOInfoStateNotObserving) {
// this could happen when `NSKeyValueObservingOptionInitial` is one of the NSKeyValueObservingOptions,
// and the observer is unregistered within the callback block.
// at this time the object has been registered as an observer (in Foundation KVO),
// so we can safely unobserve it.
[object removeObserver:self forKeyPath:info->_keyPath context:(void *)info];
}
}
또한 변화를 관찰하는 방법을 실현했다.
- (void)observeValueForKeyPath:(nullable NSString *)keyPath
ofObject:(nullable id)object
change:(nullable NSDictionary *)change
context:(nullable void *)context
{
NSAssert(context, @"missing context keyPath:%@ object:%@ change:%@", keyPath, object, change);
_FBKVOInfo *info;
{
// lookup context in registered infos, taking out a strong reference only if it exists
pthread_mutex_lock(&_mutex);
info = [_infos member:(__bridge id)context];
pthread_mutex_unlock(&_mutex);
}
if (nil != info) {
// take strong reference to controller
FBKVOController *controller = info->_controller;
if (nil != controller) {
// take strong reference to observer
id observer = controller.observer;
if (nil != observer) {
// dispatch custom block or action, fall back to default action
if (info->_block) {
NSDictionary *changeWithKeyPath = change;
// add the keyPath to the change dictionary for clarity when mulitple keyPaths are being observed
if (keyPath) {
NSMutableDictionary *mChange = [NSMutableDictionary dictionaryWithObject:keyPath forKey:FBKVONotificationKeyPathKey];
[mChange addEntriesFromDictionary:change];
changeWithKeyPath = [mChange copy];
}
info->_block(observer, object, changeWithKeyPath);
} else if (info->_action) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[observer performSelector:info->_action withObject:change withObject:object];
#pragma clang diagnostic pop
} else {
[observer observeValueForKeyPath:keyPath ofObject:object change:change context:info->_context];
}
}
}
}
}
KVOcontroller가 dealloc를 호출하면 관찰자가 제거됩니다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.