iOS-Strong、Weak、Assgin、Retain、Copy

iOS的五个修饰符,strong、weak、assgin、retain、copy的用法。

用法

Name Limit
strong 只能用于修饰对象
retain 只能用于修饰对象
copy 只能用于修饰对象
weak 只能用于修饰对象
assign 能用于修饰对象和基本数据类型

栈赋值

栈赋值的场景为,目标指针指向栈上的对象, 以NSStackBlock为例:

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
@property (nonatomic, strong) void(^strongVar)();
@property (nonatomic, retain) void(^retainVar)();
@property (nonatomic, copy) void(^copyVar)();
@property (nonatomic, weak) void(^weakVar)();
@property (nonatomic, assign) void(^assignVar)();

-(void)test{
__block int i;
_strongVar = ^{
i++;
};

_retainVar = ^{
i++;
};

_copyVar = ^{
i++;
};

_weakVar = ^{
i++;
};

_assignVar = ^{
i++;
};

[self print];
}

- (void)print{
NSLog(@"%@", _strongVar);
NSLog(@"%@", _retainVar);
NSLog(@"%@", _copyVar);
NSLog(@"%@", _weakVar);
NSLog(@"%@", _assignVar);
}

打印结果为:

1
2
3
4
5
<__NSMallocBlock__: 0x600000051fa0>
<__NSMallocBlock__: 0x600000051e20>
<__NSMallocBlock__: 0x600000051c70>
<__NSStackBlock__: 0x7fff507d9a88>
<__NSStackBlock__: 0x7fff507d9a60>

说明,对于栈赋值,strong、retian、copy,会在堆上复制一个,并指向该堆对象,而weak、assgin只会直接指向栈对象。一旦,栈对象被释放,再调用会引起Crash。

举例:

1
2
3
4
5
6
7
8
9
10
11
12
-(void)test{
...
[self performSelector:@selector(print) withObject:nil afterDelay:1];
}

- (void)print{
NSLog(@"%@", _strongVar);
NSLog(@"%@", _retainVar);
NSLog(@"%@", _copyVar);
NSLog(@"%@", _weakVar); //crash
NSLog(@"%@", _assignVar); //crash
}

总结

Name RetainCount Usage
strong 引用=1 在堆上复制一个,并指向它
retain 引用=1 在堆上复制一个,并指向它
weak 不影响引用 直接指向栈对象,栈对象被释放时,不会自动变成nil
assign 不影响引用 直接指向栈对象,栈对象被释放时,不会自动变成nil
copy 引用=1 在堆上复制一个,并指向它

堆赋值

堆赋值的场景为,目标指针指向堆上的对象, 以 NSMallocBlock为例:

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
-(void)test{
__block int i;
_strongVar = [^{
i++;
} copy];

_retainVar = [^{
i++;
} copy];

_copyVar = [^{
i++;
} copy];

_weakVar = [^{
i++;
} copy];

_assignVar = [^{
i++;
} copy];

[self print];
}

- (void)print{
NSLog(@"%@", _strongVar);
NSLog(@"%@", _retainVar);
NSLog(@"%@", _copyVar);
NSLog(@"%@", _weakVar);
NSLog(@"%@", _assignVar); // crash
}

打印结果为:

1
2
3
4
<__NSMallocBlock__: 0x60800005fe30>
<__NSMallocBlock__: 0x60800005fd40>
<__NSMallocBlock__: 0x60800005fbc0>
(null)

说明,对于堆赋值,strong、retian、copy、weak、assign,都是直接指向该堆对象,会导致引用数+1,而weak,assgin不会改变引用数。其中,weak指向的对象如果被释放了,weak指针会自动置为nil,而assgin不会,一旦,堆对象被释放,再调用会引起Crash。

总结

Name RetainCount Usage
strong 引用+1 直接指向堆对象
retain 引用+1 直接指向堆对象
weak 不影响引用 直接指向堆对象,堆对象被释放时,会自动变成nil
assign 不影响引用 直接指向堆对象,堆对象被释放时,不会自动变成nil
copy 引用=1(可变类型);引用+1(不可变类型) 对于可变类型,在堆上复制一个,并指向它;对于不可变类型,直接指向它

常量赋值

常量赋值的场景为,目标指针指向常量空间上的对象, 以NSGlobalBlock为例:

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
@property (nonatomic, strong) void(^strongVar)();
@property (nonatomic, retain) void(^retainVar)();
@property (nonatomic, copy) void(^copyVar)();
@property (nonatomic, weak) void(^weakVar)();
@property (nonatomic, assign) void(^assignVar)();

-(void)test{
_strongVar = ^{
};

_retainVar = ^{
};

_copyVar = ^{
};

_weakVar = ^{
};

_assignVar = ^{
};

[self print];
}

- (void)print{
NSLog(@"%@", _strongVar);
NSLog(@"%@", _retainVar);
NSLog(@"%@", _copyVar);
NSLog(@"%@", _weakVar);
NSLog(@"%@", _assignVar);
}

打印结果为:

1
2
3
4
5
<__NSGlobalBlock__: 0x10c3050c0>
<__NSGlobalBlock__: 0x10c305100>
<__NSGlobalBlock__: 0x10c305140>
<__NSGlobalBlock__: 0x10c305180>
<__NSGlobalBlock__: 0x10c3051c0>

说明,对于常量赋值,strong、retian、copy、weak、assign,都是直接指向该常量地址,由于常量的生命周期与进程一致,所以引用数没有意义。

总结

Name RetainCount Usage
strong 引用无意义 直接指向该常量对象
retain 引用无意义 直接指向该常量对象
weak 引用无意义 直接指向该常量对象
assign 引用无意义 直接指向该常量对象
copy 引用无意义 直接指向该常量对象