1、成员变量与属性
举例与解释
- 对比他们的区别我们先从1个简单的例子入手。
- 首先我们先定义几个成员变量和属性
- 然后cd进要转译的文件目录(例子是main.m) ,转成
cpp文件
clang -rewrite-objc main.m -o main.cpp-
在cpp文件中搜索定义的LZPerson类得到:
-
那么为什么用
copy修饰的属性会产生一个objc_setProperty呢?objc_setProperty是一个中间层代码。为了每一个属性在使用setter方法的时候不必为其在底层创建单独的实现方法,当检测到属性对象存在copy属性的时候,会将此对象的setter方法重定向到objc_setProperty(),然后只要在底层实现setProperty方法就可以将属性对象的setter方法实现了。
-
向下查方法参数能发现如下内容,他们代表什么呢?
- 其实这是返回实例变量的编码类型的
类型字符串(以对象类型声明的特殊的成员变量)
{{(struct objc_selector *)"ncName", "@16@0:8",(void *)_I_LZPerson_ncName}, {(struct objc_selector *)"setNcName:", "v24@0:8@16", (void *)_I_LZPerson_setNcName_},根据上边官网TypeEncoding的表一一对应可知:
-
@16@0:8
-
v24@0:8@16
- 其实这是返回实例变量的编码类型的
- 首先我们先定义几个成员变量和属性
1.1、ivar、imp、_cmd、method
ivar: 成员变量imp: 指向实际执行函数体的函数指针_cmd: SEL 类型的一个变量,Objective C的函数的前两个隐藏参数为self 和 _cmdmethod: 指向Objective C中的方法的指针
1.2、分析首地址+内存偏移与objc_setProperty
- 当
getter方法时,copy + atomic修饰通过objc_setProperty进行get,其余修饰可以通过首地址+内存偏移来获取想要获取的那个成员变量 - 当
setter方法时,strong修饰的也可以通过首地址+内存偏移这种方式进行set,但Copy修饰的会使用objc_setProperty进行set - 原因是进行setter时,无论是setName还是setAge等等,本质是对内存进行赋值,只有
_cmd方法名不同,底层进行的操作完全一样,所以对底层的赋值方法进行了抽取,在编译时(LLVM),所有的ivar的sel通过imp重定向到objc_setProperty
1.3、属性被Strong和Copy修饰时的区别
-
strong修饰时,对象的getter/setter方法是通过对象的首地址+内存偏移找到对应对象的内存地址,然后进行值的get或set。//getter方法 static NSString * _I_LZPerson_nName(LZPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LZPerson$_nName)); } //setter方法 static void _I_LZPerson_setNName_(LZPerson * self, SEL _cmd, NSString *nName) { (*(NSString **)((char *)self + OBJC_IVAR_$_LZPerson$_nName)) = nName; } -
copy修饰时,get方法与strong时相同 ,但setter方法则是通过objc_setProperty进行set但是造成这个的原因时什么呢?我们分步分析:
- 判断在什么阶段确定的 setter/getter 方法:因为用MachOView查看可执行性文件可以看到已经生成了方法,所以可以确定是编译阶段就已经完成
- 确定了在编译阶段,那么就需要查看LLVM源码中是如何编译的:
-
全局搜索 objc_setProperty ,发现是在
getSetPropertyFn()方法中通过CreateRuntimeFunction(FTy,"objc_setProperty"),在runtime运行时中创建,接下来找 getSetPropertyFn() 是在哪被调用 -
找到在
GetPropertySetFunction()中间层中,继续找GetPropertySetFunction() -
发现是
PropertyImplStrategy条件判断中调用了 GetPropertySetFunction() ,但它有两种类型GetSetProperty和SetPropertyAndExpressionGet -
Copy属性修饰时,枚举值
strategy值被设置为GetSetProperty
-
- 通过此案例得出结论:
- 只要为对象设置了
copy属性,无论是原子性还是非原子性,都没有影响,setter方法都会被重定向到objc_setProperty - 如果声明的对象不设置如何除原子性之外的属性,那么默认属性是
strong,不会触发objc_setProperty - 中间层的优点:
底层变化上层不受影响,上层变化底层也不会受影响
- 只要为对象设置了
- 对于
getter方法:atomic+copy修饰的属性使用objc_getProperty方式实现,其它属性使用内存偏移实现,还是需要看LLVM源码,类似于 setter 的查找方法
-
objc_setProperty源码
2、类、元类的方法
Runtime API查看类和元类
-
获取方法名
-
获取实例方法
-
获取类方法
-
获取IMP
总结:
实例方法存在类中,因此类调用不到类方法类方法存在元类中,因此元类调用不到实例方法- 找不到IMP实现的也返回方法,这个方法就是
_objc_msgFarward; - 编译器自动生成
元类,目的是存放类方法 - 万物皆对象,在底层并没有区分对象方法和类方法,其实
类和元类在底层都是调用的实例方法,类方法只是OC层的区分(类方法本质是元类的实例方法)
实例方法:
类方法:
IMP: