【iOS基础篇】---@property 后面的修饰符们

虽然写了不少年的iOS代码,可是不少东西没有深刻理解,或者当时理解了,后来不用又慢慢又忘了。因此抽空整理一份资料,以备本身之后查找。也但愿看到的小伙伴批评指正。
这篇文章主要写@property后面的修饰符。html

一、assign、unsafe_unretained和weak

assign:既能够修饰对象,也能够修饰基本类型。unsafe_unretained与assign同义。assign修饰对象会产生野指针的问题,修饰的对象释放后,指针不会自动置成nil,此时再向对象发消息程序会崩溃。安全

weak:只能修饰对象,若是修饰基本数据类型会报错:Property with 'weak' attribute must be of object type. weak修饰的对象释放后(引用计数变为0),指针会被自动置为nil,以后再向该对象发消息也不会崩溃(向nil发送任何消息都不会崩溃)。weak适用于delegate和block等引用类型,不会致使野指针问题,也不会循环引用,很是安全。app

总结:assign、weak和unsafe_unretained,修饰的属性,再调用set方法时是不会增长引用计数的。能够理解为set方法只是简单的赋值。也就是栈上的变量的赋值。验证代码以下:atom

@property(nonatomic,assign) NSObject *property;
//在代码实现中
NSObject *object = [[NSObject alloc] init];
NSLog(@"\n 引用计数 = %lu \n ", (unsigned long)[object retainCount]);
self.property = object;
NSLog(@"\n 引用计数 = %lu \n ", (unsigned long)[object retainCount]);

输出结果
2019-08-05 02:02:08.475192+0800 test4[89384:3597709]
引用计数 = 1
2019-08-05 02:02:08.475320+0800 test4[89384:3597709]
引用计数 = 1 线程

因此assign、weak和unsafe_unretained修饰的对象,默认生成的set方法,应该以下方法一所示:
-(void)setProperty:(NSObject *)property{指针

_property = property;
//weak可能还有其余一些操做

}code

而retain和strong修饰的对象,大概生成的set方法以下方法二所示:
-(void)setProperty:(NSObject *)property{htm

[property retain];
[_property release];
_property = property;

}对象

注意:若是本身实现了set方法,则这些关于存取方法的修饰符就再也不起做用了。好比,你虽然用assign修饰了对象,可是本身实现了set方法二,则assign会被忽略。ci

二、retain和strong

苹果的文档里有这样一句话:
// The following declaration is a synonym for: @property(retain) MyClass *myObject;
@property(strong) MyClass *myObject;

因此在ARC下他们是等价的。

一般在ARC环境里,不会用assign修饰对象,由于有了weak,再用assign就是自找麻烦。总之在ARC环境里用strong代替retain强引用对象,用weak弱引用对象,用assign修饰基本数据类型。

三、atomic和nonatomic

atomic和nonatomic的区别在于:系统自动生成的 getter/setter 方法不同。(若是你本身写 getter/setter,那 atomic/nonatomic/retain/assign/copy 这些关键字只起提示做用,写不写都同样)

对于atomic的属性,系统生成的 getter/setter 会保证 get、set 操做的完整性,不受其余线程影响。好比,线程 A 的 getter 方法运行到一半,线程 B 调用了 setter:那么线程 A 的 getter 仍是能获得一个无缺无损的对象。

而nonatomic就没有这个保证了。因此,nonatomic的速度要比atomic快。
nonatomic系统合成的方法大概以下:

@property(nonatomic, retain) UITextField *userName;
//系统生成的代码以下:

- (UITextField *) userName {
    return userName;
}

- (void) setUserName:(UITextField *)userName_ {
    [userName_ retain];
    [userName release];
    userName = userName_;
}

atomic系统合成的方法大概以下:

//@property(retain) UITextField *userName;
//系统生成的代码以下:

- (UITextField *) userName {
    UITextField *retval = nil;
    @synchronized(self) {
        retval = [[userName retain] autorelease];
    }
    return retval;
}

- (void) setUserName:(UITextField *)userName_ {
    @synchronized(self) {
      [userName release];
      userName = [userName_ retain];
    }
}

假设有一个 atomic 的属性 "userName",若是线程 A 调[self setUserName:@"A"],线程 B 调[self setUserName:@"B"],线程 C 调[self userName],那么全部这些不一样线程上的操做都将依次顺序执行——也就是说,若是一个线程正在执行 getter/setter,其余线程就得等待。所以,属性 setUserName 是读/写安全的。
可是,若是有另外一个线程 D 同时在调[userName release],那可能就会crash,由于 release 不受 getter/setter 操做的限制。也就是说,这个属性只能说是读/写安全的,但并非线程安全的,由于别的线程还能进行读写以外的其余操做。线程安全须要开发者本身来保证。
若是 userName 属性是 nonatomic 的,那么上面例子里的全部线程 A、B、C、D 均可以同时执行,可能致使没法预料的结果。若是是 atomic 的,那么 A、B、C 会串行,而 D 仍是并行的。

四、readwrite和readonly

readwrite:默认的属性修饰符。会合成get和set方法。

readonly:只会生成get方法。因此若是obj用readonly修饰,则self.obj = xxx;编译时会报错提示:“Assignment to readonly property”。

五、copy属性修饰符

copy:只能修饰对象类型,不能修饰基础数据类型(用了会报编译时错误)。用copy修饰的对象,必须实现
NSCopying协议也就是实现方法-(id)copyWithZone:(nullable NSZone *)zone。合成的set方法中会调用这个方法。这里也能看出copy,和retain不同的地方。

至于copy是深拷贝仍是浅拷贝彻底是看copyWithZone的实现方式。也就是说copy和深拷贝和浅拷贝没有关系!
另外对象的mutableCopy方法,也是要对象知足NSMutableCopying协议,实现- (id)mutableCopyWithZone:(nullable NSZone *)zone方法。若是不实现次方法会运行时报错!

总结

修饰符其实就是告诉编译器怎么来合成存取方法的。有些修饰符须要本身另外实现一些方法,好比copy,这里就能定制一些本身的东西,好比实现真正的多层深拷贝等等。

另外,还有一些新的修饰符nullable、nonnull、null_resettable、null_unspecified、getter=method、setter=method等,咱们下一篇文章里再总结.

参考:
https://www.jianshu.com/p/728...

相关文章
相关标签/搜索