美文网首页
类原理探索 - protocol

类原理探索 - protocol

作者: Nulll | 来源:发表于2021-06-24 18:39 被阅读0次
向日葵

前言

通过前面的讨论,我们知道了类的大部分成员变量了,我们也一一探讨 isasuperClass、 和 bits;在 bits 里面知道 class_rw_t 存储着类相关的信息。同时也分析了 methodsproperties。 那么 protocols 又是什么呢?

在开始之前我们准备如下代码

@protocol TestProtocol <NSObject>

@property (nonatomic, copy) NSString *p_name;
- (void)p_saySomthing;
+ (void)p_classFunction;

@end

@protocol TestProtocol2 <NSObject>

@property (nonatomic, copy) NSString *p2_name;

@end

@interface CDPerson : NSObject<TestProtocol, TestProtocol2>

@property (nonatomic, copy) NSString *name;         
@property (nonatomic, strong) NSString *nickName;
- (void)sayHello;
+ (void)saySomething;

@end


@implementation CDPerson

@synthesize p_name = _p_name;
@synthesize p2_name = _p2_name;
- (void)sayHello {
    NSLog(@"%@ sayHello", _name);
} 
+ (void)saySomething {
    NSLog(@"PERSON say");
} 
- (void)p_saySomthing {
    NSLog(@"协议方法实现...");
} 
+ (void)p_classFunction {
    NSLog(@"p_classFunction");
}
@end

LLDB 调试 结合源码分析

按照我们之前的步骤得出了 class_rw_t

(lldb) p/x [CDPerson class]
(Class) $0 = 0x0000000100008968 CDPerson
(lldb) p (class_data_bits_t *)0x0000000100008988
(class_data_bits_t *) $1 = 0x0000000100008988
(lldb) p $1->data()
(class_rw_t *) $3 = 0x0000000101932460
(lldb) p *$3
(class_rw_t) $4 = {
  flags = 2148007936
  witness = 1
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = {
      Value = 4295001024
    }
  }
  firstSubclass = nil
  nextSiblingClass = NSUUID
}

然后按照步骤我们一步一步的到了我们看是要的 protocol_list_t

(lldb) p $4.protocols()
(const protocol_array_t) $5 = {
  list_array_tt<unsigned long, protocol_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x0000000100008408
      }
      arrayAndFlag = 4295001096
    }
  }
}
(lldb) p $5.list.ptr
(protocol_list_t *const) $6 = 0x0000000100008408
(lldb) p *$6
(protocol_list_t) $7 = (count = 1, list = protocol_ref_t [] @ 0x00007fbf71cfb578)

到这里当我想要验证 protocol_list_t 里面是否是我想要的结果,所以我想看看这个 protocol_list_t 里面到底是何方神圣。然后我们找到了 protocol_list_t 这个结构体的定义。

struct protocol_list_t {
    // count is pointer-sized by accident.
    uintptr_t count;
//通过前面的经验,这个就是我们存储的的相关数据
    protocol_ref_t list[0]; // variable-size

    size_t byteSize() const {
        return sizeof(*this) + count*sizeof(list[0]);
    }
.....

}

然后我们继续对这个 protocol_list_tlist 下手。可以看到我们确实可以打印出来 protocol_ref_t

(protocol_list_t) $7 = (count = 2, list = protocol_ref_t [] @ 0x00007fbf73da5318)
(lldb) p $7.list[0]
(protocol_ref_t) $8 = 4295002816
(lldb) p $7.list[1]
(protocol_ref_t) $9 = 4295002912
(lldb) p $7.list[2]
(protocol_ref_t) $10 = 0

但是当我们去寻找 protocol_ref_t 的时候却发现 typedef uintptr_t protocol_ref_t; // protocol_t *, but unremapped从注释我们知道 protocol_ref_t 是个 protocol_t 类型的结构题指针强转的。于是乎,我们去搜索 protocol_t 这个玩意。

struct protocol_t : objc_object {
    const char *mangledName;
    struct protocol_list_t *protocols;
    method_list_t *instanceMethods;
    method_list_t *classMethods;
    method_list_t *optionalInstanceMethods;
    method_list_t *optionalClassMethods;
    property_list_t *instanceProperties;
    uint32_t size;   // sizeof(protocol_t)
    uint32_t flags;
    // Fields below this point are not always present on disk.
    const char **_extendedMethodTypes;
    const char *_demangledName;
    property_list_t *_classProperties;

    const char *demangledName();

    const char *nameForLogging() {
        return demangledName();
    }
......
};

然后我们强为 protocol_t 为看结果如何

(lldb) p (protocol_t *)$8
(protocol_t *) $12 = 0x0000000100008ac0
(lldb) p *$12
(protocol_t) $13 = {
  objc_object = {
    isa = {
      bits = 4298469576
      cls = Protocol
       = {
        nonpointer = 0
        has_assoc = 0
        has_cxx_dtor = 0
        shiftcls = 537308697
        magic = 0
        weakly_referenced = 0
        unused = 0
        has_sidetable_rc = 0
        extra_rc = 0
      }
    }
  }
  mangledName = 0x0000000100003e6c "TestProtocol"
  protocols = 0x0000000100008860
  instanceMethods = 0x0000000100008878
  classMethods = 0x00000001000088c8
  optionalInstanceMethods = 0x0000000000000000
  optionalClassMethods = 0x0000000000000000
  instanceProperties = 0x00000001000088e8
  size = 96
  flags = 0
  _extendedMethodTypes = 0x0000000100008900
  _demangledName = 0x0000000000000000
  _classProperties = 0x0000000000000000
}
(lldb) p (protocol_t *)$9
(protocol_t *) $14 = 0x0000000100008b20
(lldb) p *$14
(protocol_t) $15 = {
  objc_object = {
    isa = {
      bits = 4298469576
      cls = Protocol
       = {
        nonpointer = 0
        has_assoc = 0
        has_cxx_dtor = 0
        shiftcls = 537308697
        magic = 0
        weakly_referenced = 0
        unused = 0
        has_sidetable_rc = 0
        extra_rc = 0
      }
    }
  }
  mangledName = 0x0000000100003e55 "TestProtocol2"
  protocols = 0x0000000100008510
  instanceMethods = 0x0000000100008528
  classMethods = 0x0000000000000000
  optionalInstanceMethods = 0x0000000000000000
  optionalClassMethods = 0x0000000000000000
  instanceProperties = 0x0000000100008560
  size = 96
  flags = 0
  _extendedMethodTypes = 0x0000000100008578
  _demangledName = 0x0000000000000000
  _classProperties = 0x0000000000000000
}

从结果可以看到相关的名字是可以对上的。我们的 CDPerson 遵守来两个协议,这里面也确实有两个 。接下来我们看看里面到底存储的是什么;从定义上可以知道protocol_t 是继承自己 objc_object ,里面也有一个 isa,从这里看到里面的isa 是一个纯的isa和前面的类的指针不一样,因为类的isa里面不仅有类信息,还有一些相关的东西而 protocol 就是只有isa。

接下来我们看看 property_list_t *instanceProperties 这个里面,发现里面确实存储的是我们的属性。

(lldb) p $13.instanceProperties
(property_list_t *) $23 = 0x00000001000088e8
(lldb) p *$23
(property_list_t) $24 = {
  entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 1)
}
(lldb) p $24.get(0)
(property_t) $25 = (name = "p_name", attributes = "T@\"NSString\",C,N")

接下来我们查看 instanceMethods ,结果也是和预期的一样,里面存储的是我们预期的三个方法。

(lldb) p $13.instanceMethods
(method_list_t *) $26 = 0x0000000100008878
(lldb) p *$26
(method_list_t) $27 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 24, count = 3)
}
(lldb) p $27.get(0).big
(method_t::big) $28 = {
  name = "p_saySomthing"
  types = 0x0000000100003e8c "v16@0:8"
  imp = 0x0000000000000000
}
  Fix-it applied, fixed expression was: 
    $27.get(0).big()
(lldb) p $27.get(1).big
(method_t::big) $29 = {
  name = "p_name"
  types = 0x0000000100003e79 "@16@0:8"
  imp = 0x0000000000000000
}
  Fix-it applied, fixed expression was: 
    $27.get(1).big()
(lldb) p $27.get(2).big
(method_t::big) $30 = {
  name = "setP_name:"
  types = 0x0000000100003e81 "v24@0:8@16"
  imp = 0x0000000000000000
}
  Fix-it applied, fixed expression was: 
    $27.get(2).big()

查看 classMethods 这个里面,通过打印可以发现,协议的类方法直接存在了这个不结构里面,并不是像类一样存在元类里面。

(lldb) p $13.classMethods
(method_list_t *) $31 = 0x00000001000088c8
(lldb) p *$31
(method_list_t) $32 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 24, count = 1)
}
(lldb) p $32.get(0).big
(method_t::big) $33 = {
  name = "p_classFunction"
  types = 0x0000000100003e8c "v16@0:8"
  imp = 0x0000000000000000
}
  Fix-it applied, fixed expression was: 
    $32.get(0).big()

查看 struct protocol_list_t *protocols,这个就是和类的结构一样的.

结论:通过上面的调试我们发现了, 协议在底层是一个继承 objc_object 的结构体 struct protocol_t : objc_object,这也体现了万物皆对象的原理,同时这个isa 也和类的isa不太一样,是个纯的isa。里面存储了协议的名字 mangledName ,自身遵循的协议 struct protocol_list_t *protocols;以及方法(包括实例方法 instanceMethods 和类方法 classMethods)和属性 instanceProperties等。

相关文章

网友评论

      本文标题:类原理探索 - protocol

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