这一篇是解决上一篇遗留问题中的第2个问题的,如果没有读过的请先阅读上一篇的内容
变量:有成员变量
和属性变量
2者是包含关系,这个上篇已经解释过了;
方法:分为实例方法
和类方法
;
准备工作
还是上一篇中的样例代码
1 | #import <Foundation/Foundation.h> |
runtime方法记忆的小技巧
有时候调用runtime
的方法的时候特别难以记忆,如果对应到_class_ro_t
类结构,现在是不是很好记忆了
看上面这些提示会发现和类对象
已经元类对象
的结构正好对应上了
1 | struct _class_ro_t { |
你只要对着这些数据结构敲对应的属性名称,就会找到相应的方法
成员变量获取
由上一篇知道成员变量的信息是保存在类对象
中的,当然是需要通过类对象
去拿的;
很容易得到类对象关于成员变量的数据结构
1 | static struct /*_ivar_list_t*/ { |
可以看到变量的信息都存放在装着_ivar_t
的ivar_list
数组里面,知道了使用runtime
的小技巧很容易知道获取成员变量列表会找Ivar
1 | int main(int argc, const char * argv[]) { |
由struct _ivar_t ivar_list[5];
就知道这个ivar_list
装的都是_ivar_t
类型的变量
_ivar_t
如下
1 | struct _ivar_t { |
所以我们把代码写完
1 | int main(int argc, const char * argv[]) { |
输出如下
1 | 属性名称:_age, 偏移量:8,变量类型编码:i |
这种方式可以拿到所有的成员变量列表以及类型编码,这2者应该没什么难理解的,也许这个偏移量咋一看不知道怎么得出这些数字的,
这里还是要回到一开始存的时候
1 | static struct /*_ivar_list_t*/ { |
以_age
为例
1 | {(unsigned long int *)&OBJC_IVAR_$_Animal$_age, "_age", "i", 2, 4}, |
OBJC_IVAR_$_Animal$_age
其实就是
1 | extern "C" unsigned long int OBJC_IVAR_$_Animal$_age __attribute__ ((used, section ("__DATA,__objc_ivar"))) = __OFFSETOFIVAR__(struct Animal, _age); |
而__OFFSETOFIVAR__
就是
1 | #define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER) |
表达的意思就是变量距离一开始的位置是多少(我们也可以通过这种方式拿到各个变量的偏移量,见文章最后的小demo),看过我的第一篇文章的应该知道继承NSObject
的对象都有一个4个字节的isa
,所以有一下结论:
_age
是从第8
字节开始的;- 由于
_age
占用4个字节,所以_a
的开始位置为8
+4
=12
; _b
为 isa的8
+ age 的4
+ a的1
但是由于内存对齐的调整,后面的3位放不下字节长度为4
的_b
,所以为isa的8
+ age 的4
+ a的1
+ 空出来的3
个字节 =16
;- 剩下以此类推。。。
也就是下面这张图
读者也可以根据我第一篇的文章看看具体的对象实际分配了多少内存。
属性变量获取
属性变量也是放在类对象
中的所以class_copyPropertyList
第一个参数传的是类对象
还是贴出对应的代码吧
1 | static struct /*_prop_list_t*/ { |
_prop_t
结构
1 | struct _prop_t { |
和成员变量一样
1 | int main(int argc, const char * argv[]) { |
输出
1 | 属性名称:testProperty,变量类型编码:T@"NSString",C,N,V_testProperty |
实例方法
实例方法也是放在类对象
中的所以class_...
第一个参数传的也是类对象
实例方法代码
1 | static struct /*_method_list_t*/ { |
_objc_method
结构
1 | struct _objc_method { |
1 |
|
输出
1 | 方法名称:testFunction: ,方法地址:0x100000ac0,方法类型编码:i20@0:8i16 |
会发现多了个.cxx_destruct
,这个应该是框架加上去的,先不管这些,从输出结果拿到了所有的实例变方法
类方法
类方法是放在元类对象
中的所以class_...
第一个参数传的也是元类对象
只需要把获取实例方法
的一行代码修改下就可以了
将class_copyMethodList
的第一个参数,由类对象换成元类对象就可以了
1 |
|
1 |
|
输出
1 | 方法名称:testClassFunction ,方法地址:0x100000af0,方法类型编码:i16@0:8 |
###OFFSETOFIVAR
1 | struct TestStruct { |
输出
1 | 0 |