美文网首页工作生活
KVC底层原理探究本质-GNUstep

KVC底层原理探究本质-GNUstep

作者: 哈哈西 | 来源:发表于2019-11-12 17:22 被阅读0次

    这里是我学习过程中找到的一条探究思路,希望可以开启你的新世纪大门,少走点弯路吧,书籍推荐《Objective-C高级编程 iOS与OS X多线程和内存管理》,这里面有通过GNUStep源码分析了OC的内存管理方面的原理。gnustep-base-1.26.0 源码注释笔记。KVC底层,KVC底层原理,KVC本质,KVC原理,GNUstep

    前半部分是结论,后半部分是验证,重点!!!

    KVC

    KVC全称Key Value Coding(键值编码),是可以通过对象属性名称直接对属性值进行编码(赋值及访问)。而不需要调用明确的存取方法。这样就可以运行时动态访问和修改对象的属性,而不是在编译时确定。

    赋值 setValue:forKey:的原理

    1. 按照setKey:、_setKey:的顺序查找方法
    2. 如果找到了方法就传递参数,调用方法
    3. 如果找不到,就去检查是否可以直接访问实例变量(+accessInstanceVariablesDirectly),如果NO就是不可以,那么就调用setValue:forUndefineKey:并抛出异常NSUnknownKeyException
    4. 如果+accessInstanceVariablesDirectly返回YES,就按照_key,_isKey,key,_isKey的顺序查找成员变量
    5. 如果找到了成员变量,就直接赋值。
    6. 如果 _key、_isKey、key、isKey的顺序没有查找到成员变量就调用setValue:forUndefinedKey: 并抛出异常 NSUnknownKeyException

    核心:setValue:forKey:->setKey、_setKey->accessInstanceVariablesDirectly->_key、_isKey、key、isKey

    取值 valueForKey:的原理

    1. 按照getKey,key,_isKey,_key的顺序查找方法
    2. 如果找到了就传递参数,调用方法
    3. 如果没找到,就检查是否可以直接访问实例变量(+accessInstanceVariablesDirectly),如果不可以,就调用setValue:forUndefineKey:并抛出异常NSUnknownKeyException
    4. 如果+accessInstanceVariablesDirectly返回YES,就按照_key、_isKey、key、isKey的顺序查找成员变量
    5. 如果找到了成员变量,就直接取值
    6. 如果 _key、_isKey、key、isKey的顺序没有查找到成员变量就调用valueForUndefinedKey:并抛出异常NSUnknownKeyException

    核心:valueForKey:->getKe、key、_isKey、_key->accessInstanceVariablesDirectly->_key、_isKey、key、isKey

    看到这里不要着急,精彩在后面!!!


    GNUstep是Cocoa框架的互换框架。也就是说,GNUstep的源代码虽不能说与苹果的Cocoa实现完全相同,但是从使用者的角度来看,两者的行为和实现是一样的,或者说非常相似。理解了GNUstep源代码也就是相当于理解了苹果的Cocoa实现。

    static void
    SetValueForKey(NSObject *self, id anObject, const char *key, unsigned size)
    {
      SEL       sel = 0;
      const char    *type = 0;
      int       off = 0;
    
      if (size > 0)
        {
          const char    *name;
          char      buf[size + 6];
          char      lo;
          char      hi;
          //C 库函数 char *strncpy(char *dest, const char *src, size_t n) 把 src 所指向的字符串复制到 dest,最多复制 n 个字符。当 src 的长度小于 n 时,dest 的剩余部分将用空字节填充。
          strncpy(buf, "_set", 4);
          strncpy(&buf[4], key, size);
          lo = buf[4];
          hi = islower(lo) ? toupper(lo) : lo;
          buf[4] = hi;
          buf[size + 4] = ':';
          buf[size + 5] = '\0';
    
          //到这时buf里面存的字符串是_setKey:,下面遇到的 &buf[1],buf分别代表setKey:,_setKey:
          name = &buf[1];   // setKey:
          type = NULL;
          sel = sel_getUid(name);
          if (sel == 0 || [self respondsToSelector: sel] == NO)
        {
          name = buf;   // _setKey:
          sel = sel_getUid(name);
          if (sel == 0 || [self respondsToSelector: sel] == NO)
            {
              sel = 0;
                //此处判断:是否可以直接访问实例变量,英语重要性
              if ([[self class] accessInstanceVariablesDirectly] == YES)
            {
                //清0 buf,lo = key
              buf[size + 4] = '\0';
              buf[3] = '_';
              buf[4] = lo;
              name = &buf[3];   // _key
              if (GSObjCFindVariable(self, name, &type, &size, &off) == NO)
                {
                    //hi = Key
                  buf[4] = hi;
                  buf[3] = 's';
                  buf[2] = 'i';
                  buf[1] = '_';
                  name = &buf[1];   // _isKey
                  if (GSObjCFindVariable(self,
                name, &type, &size, &off) == NO)
                {
                  buf[4] = lo;
                  name = &buf[4];   // key
                  if (GSObjCFindVariable(self,
                    name, &type, &size, &off) == NO)
                    {
                      buf[4] = hi;
                      buf[3] = 's';
                      buf[2] = 'i';
                      name = &buf[2];   // isKey
                      GSObjCFindVariable(self,
                    name, &type, &size, &off);
                    }
                }
                }
            }
            //其中任何一次找到key,都会调用GSObjCFindVariable,实现赋值操作。
            }
          else
            {
              GSOnceFLog(@"Key-value access using _setKey: is deprecated:");
            }
        }
        }
      GSObjCSetVal(self, key, anObject, sel, type, size, off);
    }
    //下面这段代码是KVC取值操作原理
    static id ValueForKey(NSObject *self, const char *key, unsigned size)
    {
      SEL       sel = 0;
      int       off = 0;
      const char    *type = NULL;
    
      if (size > 0)
        {
          const char    *name;
          char      buf[size + 5];
          char      lo;
          char      hi;
    
          strncpy(buf, "_get", 4);
          strncpy(&buf[4], key, size);
          buf[size + 4] = '\0';
          lo = buf[4];
          hi = islower(lo) ? toupper(lo) : lo;
          buf[4] = hi;
    
          name = &buf[1];   // getKey
          sel = sel_getUid(name);
          if (sel == 0 || [self respondsToSelector: sel] == NO)
        {
          buf[4] = lo;
          name = &buf[4];   // key
          sel = sel_getUid(name);
          if (sel == 0 || [self respondsToSelector: sel] == NO)
            {
                  buf[4] = hi;
                  buf[3] = 's';
                  buf[2] = 'i';
                  name = &buf[2];   // isKey
                  sel = sel_getUid(name);
                  if (sel == 0 || [self respondsToSelector: sel] == NO)
                    {
                      sel = 0;
                    }
            }
        }
    
          if (sel == 0 && [[self class] accessInstanceVariablesDirectly] == YES)
        {
          buf[4] = hi;
          name = buf;   // _getKey
          sel = sel_getUid(name);
          if (sel == 0 || [self respondsToSelector: sel] == NO)
            {
              buf[4] = lo;
              buf[3] = '_';
              name = &buf[3];   // _key
              sel = sel_getUid(name);
              if (sel == 0 || [self respondsToSelector: sel] == NO)
            {
              sel = 0;
            }
            }
          if (sel == 0)
            {
              if (GSObjCFindVariable(self, name, &type, &size, &off) == NO)
            {
                      buf[4] = hi;
                      buf[3] = 's';
                      buf[2] = 'i';
                      buf[1] = '_';
                      name = &buf[1];   // _isKey
              if (!GSObjCFindVariable(self, name, &type, &size, &off))
                        {
                           buf[4] = lo;
                           name = &buf[4];      // key
                   if (!GSObjCFindVariable(self, name, &type, &size, &off))
                             {
                                buf[4] = hi;
                                buf[3] = 's';
                                buf[2] = 'i';
                                name = &buf[2]; // isKey
                                GSObjCFindVariable(self, name, &type, &size, &off);
                             }
                        }
            }
            }
        }
        }
      return GSObjCGetVal(self, key, sel, type, size, off);
    }
    

    赋值setValue:forKeyPath原理

    - (void) setValue: (id)anObject forKeyPath: (NSString*)aKey
    {
        //以.切割keyPath,递归调用setValue:forKeyPath:方法逐步递归取值,知道最后不能再递归就赋值
      NSRange       r = [aKey rangeOfString: @"." options: NSLiteralSearch];
    #ifdef WANT_DEPRECATED_KVC_COMPAT
      IMP           o = [self methodForSelector: @selector(takeValue:forKeyPath:)];
    
      setupCompat();
      if (o != takePath && o != takePathKVO)
        {
          (*o)(self, @selector(takeValue:forKeyPath:), anObject, aKey);
          return;
        }
    #endif
    
      if (r.length == 0)
        {
          [self setValue: anObject forKey: aKey];
        }
      else
        {
          NSString  *key = [aKey substringToIndex: r.location];
          NSString  *path = [aKey substringFromIndex: NSMaxRange(r)];
          
          [[self valueForKey: key] setValue: anObject forKeyPath: path];
        }
    }
    
    

    上面截取的代码是KVC赋值与取值的操作,主要解读了下赋值的操作。有人会觉得毕竟两个框架不一样,需要验证下:这里我只描述下如何验证原理,网上很多博客KVC底层原理的分析,都只是验证,我这里是配合类源码分析了下。
    场景:对一个Person类中的name属性进行KVC赋值与取值。赋值,我们在这个Person类中,使用赋值操作原理中提到的方法和成员变量覆盖掉Person类隐藏的,然后逐个验证。

    相关文章

      网友评论

        本文标题:KVC底层原理探究本质-GNUstep

        本文链接:https://www.haomeiwen.com/subject/jxprmctx.html