属性、成员、实例变量简析

本文探索属性、成员、实例变量。

一、简介

1、属性变量

1
@property (nonatomic, copy) NSString *propName;

@property:做了什么事?文章后面探索。

为 propName 生成了 set/get 方法的声明和实现,同时为其生成了私有的 ivar 成员变量  _propName;

2、成员变量

1
2
3
4
@interface MyPerson : NSObject {

NSString *instName;
}

没有 set/get,只有私有的 _instName 变量。

3、实例变量

可以进行实例化的对象 –> 实例变量是一种特殊的成员变量

1
2
3
4
5
6
@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 底层编译:

1
2
3
4
// 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 操作

1
2
3
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 处 –> 最后返回新值

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
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 文件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#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;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!