属性修饰符

区别

1.retain:

类似于ARC下的 strong,表示强引用和持有的关系:将属性成员变量的指针指向源对象,同时持有源对象(此对象的引用计数会加1)。

1
2
3
4
5
6
7
8
9
//retain
-(void)setName:(NSString *)newName
{
if (_name != newName) {
[newName retain];
[_name release];
_name = newName;
}
}

2.assign:

类似于ARC下的 weak,表示弱引用和不持有的关系:将属性成员变量的指针指向源对象,但不持有源对象,不更改其引用计数,一般用于基本数据类型(NSIntegerCGFloat)和C类型、int,float,double,bool等,这些数值主要存在于栈上)。

1
2
3
4
5
//assign
-(void)setName:(NSString *)newName
{
_name = newName;
}

weak 与 assign 的区别:

1、修饰的对象

assign 可修饰对象类型和基本类型,一般用于修饰基本类型。weak 只可以修饰对象类型,修饰基本数据类型时编译器会报错。

2、修饰对象类型时是否产生野指针

weak 不会产生野指针问题:weak 修饰的对象释放后指针会自动被置nil,之后再向该对象发消息也不会崩溃。assign 修饰对象类型时可能会产生野指针问题:assign 修饰的象被释放后指针不会自动被置空,此时向对象发消息会崩溃。另外,如果assign只是修饰基本数据类型,则是安全的。

3、小结:
值类型会被放入栈中,遵循先进后出原则,由系统负责管理栈内存。而引用类型会被放入堆中,需要我们手动管理内存或通过ARC管理。weak 适用于 delegate 和 block 等引用类型,不会导致野指针问题,是安全的,可用于解决循环引用问题。assign 适用于基本数据类型,不适用于引用类型。

3.copy:

表示拷贝和持有:会在内存中拷贝一份源对象,并将属性对应的成员变量的指针指向此拷贝出的新对象,新对象的引用计数 = 1。

1
2
3
4
5
6
7
-(void)setName:(NSString *)newName
{
if (_name != newName) {
[_name release];
_name = [newName copy];
}
}

与 strong 的区别在于:strong 修饰的变量与原对象指向同一片内存空间,对变量的修改会影响到源对象。而copy 修饰的对象与源对象指向两片不同的内存空间,变量与原对象两者互不影响。

4.readwrite:

编译器自动为属性创建 setter/getter 方法。

5.readonly:

编译器只为属性创建 getter 方法。

属性被声明为只读时,可以同时重写属性的gettersetter 使其变为 readwrite。

6.nonatomic:

nonatomic 修饰的对象不保证settergetter的完整性,多线程中访问它时可能会返回未初始化的对象。它比atomic快,但它不是线程安全的。

1
2
3
4
5
6
7
8
9
10
11
// @property(nonatomic, copy) NSString *name;
- (NSString *)name
{
return _name;
}

- (void)setName:(NSString *)newName
{
[_name release];
_name = [newName copy];
}

7.atomic:

atomic 修饰的对象会保证settergetter的完整性,任何线程对其访问都可以得到一个完整的初始化后的对象。例如有多个线程同时调用setter,不会出现某个线程执行完setter全部语句之前,另一个线程开始执行setter情况,相当于函数头尾加了锁一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// @property(atomic, copy) NSString *name;
- (NSString *)name
{
@synchronized(self) {
return _name;
}
}

- (void)setName:(NSString *)newName
{
@synchronized(self) {
[_name release];
_name = [newName copy];
}
}

因为要保证操作完成,所以速度慢。它比nonatomic安全,但也并不是绝对的线程安全,例如多个线程同时调用setget就会导致获得的对象值不一样甚至发生崩溃。绝对的线程安全就要用到线程的同步机制,比如使用NSLock@synchronized等加锁。

8.默认值

基本数据类型的默认关键字是:atomicreadwriteassign

属性的默认关键字是:atomicreadwritestrong

Getter与Setter

@dynamic

1
@dynamic var;

@dynamic:告诉编译器,属性的gettersetter由我们自己实现,不需要编译器自动生成。如果我们将属性标记为@dynamic var;而又没有提供对应的gettersetter时,编译期是不会报错,但在运行期如果访问属性或者给属性赋值时则会报错并崩溃,因为运行时找不到对应的gettersetter去完成这两个操作。

@synthesize

1
@syntheszie var;

@synthesize:告诉编译器,在编译期用我指定的成员变量实现属性的gettersetter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@interface AppDelegate()
@property (nonatomic, strong) NSArray *anArr;
@end

@implementation AppDelegate
//@dynamic anArr;
@synthesize anArr;

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.anArr = @[@"Hello"];
NSString *str = self.anArr[0];
return YES;
}

注意上面访问属性时使用的都是self.anArr,这时如果使用_anArr会报错,因为@synthesize anArr;只创建了成员变量anArr并绑定到属性anArr上。所以需要我们使用@synthesize anArr = _anArr来让编译器帮我们创建成员变量_anArr并将其绑定到属性上。

默认值

iOS5之后,如果@synthesize@dynamic都没写,那么属性默认是@syntheszie var = _var;


相关参考:

#©故胤道长


属性修饰符
https://davidlii.cn/2018/06/12/property.html
作者
Davidli
发布于
2018年6月12日
许可协议