![](https://img.haomeiwen.com/i1694726/af525d66164420a7.jpg)
前言
通过前面的讨论,我们知道了类的大部分成员变量了,我们也一一探讨 isa
、 superClass
、 和 bits
;在 bits
里面知道 class_rw_t
存储着类相关的信息。同时也分析了 methods
和 properties
。 那么 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_t
的 list
下手。可以看到我们确实可以打印出来 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
等。
网友评论