返回

KVO keyPath 实现源码解析

IOS

kvc 简介

  KVO(key-value observing)是一种观察对象属性变化的机制,能够在属性值改变时自动调用相关方法,从而实现监听对象属性变化的功能。KVO 广泛应用于 Cocoa 开发中,比如自动更新 UI 和数据绑定。
  实现 KVO 的关键步骤包括:给属性添加观察者、调用 KVO 方法监听属性变化、在属性值改变时调用观察者方法。

keyPath 实现源码解析

static Class _class_by_className(const char *name)
{
  Class cls = objc_lookUpClass(name);
  if (!cls) {
    cls = objc_NSClassFromString(name);
  }

  return cls;
}

static inline Class
class_forKeyPath(const char *keyPath)
{
  const char *current = keyPath;
  while (*current == '.')
    current++;

  NSString *className;
  if (current)
    className = [NSString stringWithUTF8String:current];
  else
    className = [NSString stringWithUTF8String:"NSObject"];

  return _class_by_className([className UTF8String]);
}

  这段代码用于通过 keyPath 获取对应的类。它首先跳过 keyPath 中的第一个点,然后根据剩余的字符串构造一个类名,最后使用 objc_lookUpClass() 或 objc_NSClassFromString() 函数获取对应的类。

@implementation NSDictionary (NSString)

- (id) valueForKey:(NSString *)key
{
  if (!key)
    return nil;

  NSString *classname = NSStringFromClass([self class]);

  const char *class_name = classname.UTF8String;

  if (class_name[0] == '_')
    return nil;

  Class class = class_forKeyPath(keyPath);
  if (!class)
    return nil;

  NSMethodSignature *signature = [class methodSignatureForSelector:@selector(valueForKey:)];
  if (!signature)
    return nil;

  NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
  if (!invocation)
    return nil;

  [invocation setSelector:@selector(valueForKey:)];
  [invocation setTarget:self];
  [invocation setArgument:&key atIndex:2];

  [invocation invoke];

  id result = nil;
  [invocation getReturnValue:&result];

  return result;
}

@end

  这段代码是 NSDictionary 类实现的 valueForKey 方法。它首先检查 key 是否为 nil,如果是则返回 nil。然后它获取 NSDictionary 类的类名并检查它是否以“”开头。如果以“”开头,则返回 nil。接下来,它使用 class_forKeyPath() 函数获取 keyPath 对应的类。如果找不到对应的类,则返回 nil。

  如果找到了对应的类,则获取该类的 valueForKey: 方法的签名。如果没有找到签名,则返回 nil。然后,它创建一个 NSInvocation 对象来调用 valueForKey: 方法。设置调用方法、调用对象和参数后,它调用 invoke 方法来执行该方法。

  最后,它从 NSInvocation 对象中获取返回值并将其返回。