细说@property(三)

@property修饰符

定义一个属性时,nonatomic、copy、strong、assign等被称作是关键字,或者是修饰符。

修饰符种类

  • 原子性。原子性有nonatomic、atomic两个值,如果不写nonatomic,那么默认是atomic的。如果属性是atomic的,那么在访问其getter和setter方法之前,会有一些判断,大概是判断是否可以访问等,这里系统使用的是自旋锁。由于使用atomic并不能绝对保证线程安全,且会耗费一些性能,因此通常情况下都使用nonatomic。
  • 读写权限。读写权限有两个取值,readwrite和readonly。声明属性时,如果不指定读写权限,那么默认是readwrite的。如果某个属性不想让其他人来写,那么可以设置成readonly。
  • 内存管理。内存管理的取值有assign、strong、weak、copy、unsafe_unretained。
  • set、get方法名。如果不想使用自动合成所生成的setter、getter方法,声明属性时甚至可以指定方法名。

默认修饰符
声明属性时,如果不显示指定修饰符或者不指定修饰符时,那么默认修饰符分两种

  • 基本数据类型:atomic, readwrite, assign
  • Objective-C对象类型:atomic, readwrite, strong

atomic是否是线程安全的
声明属性时,通常使用nonatomic修饰符,原因就是因为atomic并不能保证绝对的线程安全。举例来说,假设有一个线程A在不断的读取属性name的值,同时有一个线程B修改了属性name的值,那么即使属性name是atomic,线程A读到的仍旧是修改后的值,可见不是线程安全的。如果想要实现线程安全,需要手动的实现锁。示例:

stu.name = @"aaa";

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    for(int i = 0 ; i < 1000; ++i){
        NSLog(@"stu.name = %@",stu.name);
    }
});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    stu.name = @"bbb";
});

输出:

2019-01-16 14:42:26.837215+0800 TestClock[15405:175815] stu.name = aaa
2019-01-16 14:42:26.837837+0800 TestClock[15405:175815] stu.name = bbb

如此证实了即使使用了atomic,也不能保证线程安全。

weak和assign区别
weak和strong是对应的,一个是强引用,一个是弱引用。weak和assign的区别主要是体现在两者修饰OC对象时的差异。上面也介绍过,assign通常用来修饰基本数据类型,如int、float、BOOL等,weak用来修饰OC对象,如UIButton、UIView等。

基本数据类型用weak来修饰:假设声明一个int类型的属性,但是用weak来修饰,那么Xcode会直接提示错误,错误信息如下

Property with 'weak' attribute must be of object type

也就是说,weak只能用来修饰对象,不能用来修饰基本数据类型,否则会发生编译错误。

对象使用assign来修饰:假设声明一个UIButton类型的属性,但是用assign来修饰,你会发现编译没有问题,运行也没有问题。我们声明两个UIButton类型属性,一个用assign修饰,一个用weak修饰,创建button,打印比较发现一致没问题。但是释放button,将两个属性置为nil,再打印这两个属性:

UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(100,100,100,100)];
[btn setTitle:@"Test" forState:UIControlStateNormal];
btn.backgroundColor = [UIColor lightGrayColor];
self.assignBtn = btn;
self.weakButton = btn;
btn = nil;
NSLog(@"self.weakBtn = %@",self.weakButton);
NSLog(@"self.assignBtn = %@",self.assignBtn);

运行,执行到self.assignBtn的时候崩溃了,崩溃信息是:

 EXC_BAD_ACCESS (code=EXC_I386_GPFLT)

那么weak和assign修饰对象时的差别体现出来了:weak修饰的对象,当对象释放之后,即引用计数为0时,对象会置为nil;assign修饰的对象,当对象释放之后,即引用计数为0时,对象会变为野指针,不知道指向哪,再向该对象发消息,非常容易崩溃。因此,当属性类型是对象时,不要使用assign,会带来一些风险。

堆和栈
上面说到,属性用assign修饰,当被释放后,容易变为野指针,容易带来崩溃问题,那么,为何基本数据类型可以用assign来修饰呢?这就涉及到堆和栈的问题。

相对来说,堆的空间大,通常是不连续的结构,使用链表结构。使用堆中的空间,需要开发者自己去释放。OC中的对象,如 UIButton 、UILabel ,[[UIButton alloc] init] 出来的,都是分配在堆空间上。

栈的空间小,约1M左右,是一段连续的结构。栈中的空间,开发者不需要管,系统会帮忙处理。iOS开发 中 int、float等变量分配内存时是在栈上。如果栈空间使用完,会发生栈溢出的错误。

由于堆、栈结构的差异,栈和堆分配空间时的寻址方式也是不一样的。因为栈是连续的控件,所以栈在分配空间时,会直接在未使用的空间中分配一段出来,供程序使用;如果剩下的空间不够大,直接栈溢出;堆是不连续的,堆寻找合适空间时,是顺着链表结点来寻找,找到第一块足够大的空间时,分配空间,返回。根据两者的数据结构,可以推断,堆空间上是存在碎片的。

那么assign修饰基本数据类型没有野指针的问题?因为这些基本数据类型是分配在栈上,栈上空间的分配和回收都是系统来处理的,因此开发者无需关注,也就不会产生野指针的问题。

栈线程安全
进程和线程的关系:
线程是进程的一个实体,是CPU调度和分派的基本单位。一个进程可以拥有多个线程。线程本身是不配拥有系统资源的,只拥有很少的,运行中必不可少的资源(如程序计数器、寄存器、栈)。但是线程可以与同属于一个进程的其他线程,共享进程所拥有的资源。一个进程中所有的线程共享该进程的地址空间,但是每个线程有自己独立的栈,iOS系统中,每个线程栈的大小是1M。而堆则不同。堆是进程所独有的,通常一个进程有一个堆,这个堆为本进程中的所有线程所共享。

通过上面的介绍,我们可以清楚的了解到:栈是线程安全的。
堆是多个线程所共有的空间,操作系统在对进程进行初始化的时候,会对堆进行分配;
栈是每个线程所独有的,保存线程的运行状态和局部变量。栈在线程开始的时化,每个线程的栈是互相独立的,因此栈是线程安全的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容