OC 底层探索 05、属性、成员、实例变量简析
本文探索属性、成员、实例变量。
一、简介
1、属性变量
@property (nonatomic, copy) NSString *propName;
@property:做了什么事?文章后面探索。
为 propName 生成了 set/get 方法的声明和实现,同时为其生成了私有的 ivar 成员变量 _propName;
2、成员变量
@interface MyPerson : NSObject { NSString *instName; }
没有 set/get,只有私有的 _instName 变量。
3、实例变量
可以进行实例化的对象 --> 实例变量是一种特殊的成员变量
@interface MyPerson : NSObject { NSObject *obj; MyPerson *person1; UIButton *btn; }
二、详解
通过 .cpp 文件和源码进行分析
之前文章 对象本质与isa 已介绍过通过 clang 编译后cpp文件,main.m文件编译后内容如下图(main.m内容见本文底)。
1、变量和方法
1、成员变量:
从 .cpp 文件可以看到,struct MYPerson_IMPL 中,可以看到我们声明的成员变量 privName 和 property 声明的2个变量编译成 _xxxx 的形式的成员变量。
2、sel imp
111650 行 “MYPerson * self, SEL _cmd”,每个方法都是默认有这两个属性的 --> 引申 SEL IMP
SEL:方法编号 --> 我们定义的方法名
IMP:函数实现指针 --> 指向方法实现的指针
2、ivar_getTypeEncoding
代码中“@ v :”这些符号是什么含义呢?方法签名
示例:v16@0:8 -->
v -> 返回值类型:void 即无返回值
16 -> 共申请开辟的内存:16 字节
@ -> 第一个参数类型:id
0 -> 第一个参数位置从第几位开始:从 0 位值开始,占 8 字节
:-> 方法
8 -> 方法 位置从第几位开始:从第 8 位开始,到第 15 位,也占了8字节。
苹果文档:Type Encodings
3、property 的 copy 和 strong
从 .cpp 文件的 111655 ~ 111661 行,是两个 NSString 类型的属性变量的编译后代码,区别是 cNickName 用copy 修饰,sNickName 用 strong 修饰。我们发现2者的 set 方法是不同的。
set 操作
copy:--> objc_setProperty() --> reallySetProperty() --> 新值 retain 和 旧值 release
strong:
源码 --> objc_storeStrong() --> 简单的新旧值 retain release:
.cpp 底层编译:
// strong 的 set 方法 static void _I_MYPerson_setSNickName_(MYPerson * self, SEL _cmd, NSString *sNickName) { (*(NSString **)((char *)self + OBJC_IVAR_$_MYPerson$_sNickName)) = sNickName; }
C++ 中是没有 retain release 的,如上代码,(char *)self 位置进行地址平移,OBJC_IVAR_MYPerson_sNickName 的位置,到 sNickName 的位置(这个位置我们是不知道的不必细究),然后在此位置进行赋值操作。其实就是做了内存平移然后赋值。
和上面源码一致,location 即 sNickName 的地址,拿到地址上的值,判断新值和旧值是否相同,相同则直接 return。
get 操作
static NSString * _I_MYPerson_cNickName(MYPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_MYPerson$_cNickName)); }
get 操作比较简单,就是通过指针平移找到位置取出值。
weak 扩展
weak:objc_storeWeak() --> storeWeak() --> SideTable 散列表(weak_table) --> 新旧值处理:
首先 针对 有旧值时, 一直对散列表进行 循环,取值、判断 ,直至找到旧值的位置 --> 对新值进行判断处理 --> 清除旧值 --> 注册新值 --> 新值赋在 location 处 --> 最后返回新值
源码:
static id storeWeak(id *location, objc_object *newObj) { ASSERT(haveOld || haveNew); if (!haveNew) ASSERT(newObj == nil); Class previouslyInitializedClass = nil; id oldObj; SideTable *oldTable; SideTable *newTable; // Acquire locks for old and new values. // Order by lock address to prevent lock ordering problems. // Retry if the old value changes underneath us. retry: if (haveOld) { oldObj = *location; oldTable = &SideTables()[oldObj]; } else { oldTable = nil; } if (haveNew) { newTable = &SideTables()[newObj]; } else { newTable = nil; } SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable); // 有旧值时,不断循环查找 -- 直到 找到 location 上的旧值 if (haveOld && *location != oldObj) { SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable); goto retry; } // Prevent a deadlock between the weak reference machinery // and the +initialize machinery by ensuring that no // weakly-referenced object has an un-+initialized isa. // 有新值 if (haveNew && newObj) { Class cls = newObj->getIsa(); // 新值为全新的 if (cls != previouslyInitializedClass && !((objc_class *)cls)->isInitialized()) { SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable); class_initialize(cls, (id)newObj); // If this class is finished with +initialize then we're good. // If this class is still running +initialize on this thread // (i.e. +initialize called storeWeak on an instance of itself) // then we may proceed but it will appear initializing and // not yet initialized to the check above. // Instead set previouslyInitializedClass to recognize it on retry. // 新值付给 previouslyInitializedClass - 继续 retry previouslyInitializedClass = cls; goto retry; } } // Clean up old value, if any. // 清除 旧值 if (haveOld) { weak_unregister_no_lock(&oldTable->weak_table, oldObj, location); } // Assign new value, if any. // 注册新值 if (haveNew) { newObj = (objc_object *) weak_register_no_lock(&newTable->weak_table, (id)newObj, location, crashIfDeallocating); // weak_register_no_lock returns nil if weak store should be rejected // Set is-weakly-referenced bit in refcount table. if (newObj && !newObj->isTaggedPointer()) { newObj->setWeaklyReferenced_nolock(); } // Do not set *location anywhere else. That would introduce a race. // location 位置 附赋上新值 *location = (id)newObj; } else { // No new value. The storage is not changed. } SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable); // 返回新值 return (id)newObj; }
main.m 文件内容
#import <Foundation/Foundation.h> @interface MYPerson : NSObject { NSString *privName; } @property (nonatomic, copy) NSString *cNickName; @property (nonatomic, strong) NSString *sNickName; - (void)funcTest; @end @implementation MYPerson - (void)funcTest { } @end int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... NSLog(@"Hello, World!"); } return 0; }