@proterty属性分类

Strong

强引用,对象的引用计数器值+1

@property (nonatomic, strong) TestObj *s1;
@property (nonatomic, strong) TestObj *s2;

self.s1 = [[TestObj allow] init];
self.s2 = self.s1;
self.s1 = nil;
NSLog(@"s2 = %@",self.s2);

结果:s2 = <TestObj:0x7f97a9c73740>;
内存:前:*_s1: 0x7f97a9c73740; *_s2: 0x7f97a9c73740
     后:*_s1: 0x0;            *_s2: 0x7f97a9c73740
     
可见,s1指向的地址中的内容已经不存在了,但是因为引用计数+1了所以该块内存不会被释放,可以继续访问

Assign

弱引用,对象的引用计数器值不变,用于基础类型(基础类型copy,基础类型没有引用计数的概念)

@property (nonatomic, strong) TestObj *s1;
@property (nonatomic, assign) TestObj *s2;

self.s1 = [[TestObj allow] init];
self.s2 = self.s1;
self.s1 = nil;
NSLog(@"s2 = %@",self.s2);

结果:crash  但是打印出了s2 = 0x7fb668419500 (这里有时候会crash,有时候不会,当这块地址被回收了,就会crash。因为s1和s2指向同一块地址,当s1被释放了,地址就极可能会被回收。)
内存:前:*_s1: 0x7fb668419500;   *_s2: 0x7fb668419500
     后:*_s1: 0x0;              *_s2: 0x7fb668419500
可见,s1指向的地址已经被回收,所以s2找不到地址。

拓展

assign 简单赋值,不更改索引计数(Reference Counting)。
assign 说明设置器直接进行赋值,这也是默认值。
在使用垃圾收集的应用程序中,如果你要一个属性使用assign,且这个类符合NSCopying协 议,你就要明确指出这个标记,而不是简单地使用默认值,否则的话,你将得到一个编译警告。这再次向编译器说明你确实需要赋值,即使它是可拷贝的。

如果想在dellac中调用delegate的某些函数时候,如果是weak会找不到对象,因为被置空了。所以用assign 
  • 对基础数据类型 (NSInteger,CGFloat)和C数据类型(int, float, double, char)等,另外还有id。如:
  • @property (nonatomic, assign) int number;
  • @property (nonatomic, assign) id className;//id必须用assign
  • 反正记住:前面不需要加 “*” 的就用assign吧

Weak

弱引用,如果持有对象被释放,该对象也自动释放

@property (nonatomic, strong) TestObj *s1;
@property (nonatomic, weak)   TestObj *s2;

self.s1 = [[TestObj allow] init];
self.s2 = self.s1;
self.s1 = nil;
NSLog(@"s2 = %@",self.s2);

结果:s2 = nil;
内存:前:*_s1: 0x7f9689554460;  *_s2: 0x7f9689554460
     后:*_s1: 0x0;             *_s2: 0x0
     
可见,s1的内存地址被回收,s2的指针也变成nil,不会再指向该地址

Copy

拷贝一份、建立一个索引计数为1的对象,然后释放旧对象

//对象要实现NSCopy协议
@property (nonatomic, strong) TestObj *s1;
@property (nonatomic, copy)   TestObj *s2;

self.s1 = [[TestObj allow] init];
self.s2 = self.s1;
self.s1 = nil;
NSLog(@"s2 = %@",self.s2);

结果:s2 = <TestObj:0x7fdaf1f54300>;
内存:前:_s1: 0x7fdaf1f543e0;  *_s2: 0x7fdaf1f54300
     后:*_s1: 0x0;            *_s2: 0x7fdaf1f54300
     
可见,s1、s2指针指向的地址是不同的 因为copy了一份,内容相同,不是原来的地址了,所以s1= nil,不影响s2

Retain

释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的索引计数为1

@property (nonatomic, strong) TestObj *s1;
@property (nonatomic, retain) TestObj *s2;

self.s1 = [[TestObj allow] init];
self.s2 = self.s1;
self.s1 = nil;
NSLog(@"s2 = %@",self.s2);

结果:s2 = <TestObj:0x7fdaf1f54300>;
内存:前:*_s1: 0x7ffb5875c1b0;  *_s2: 0x7ffb5875c1b0
     后:*_s1: 0x0;             *_s2: 0x7ffb5875c1b0
     
可见,s1、s2指针指向的地址是相同的,当s1= nil,不影响s2。因为引用+1了,所以该内存地址不会被回收
  • 对其他NSObject和其子类

  • xcode 4.2不支持ARC,所以会频繁使用retain来修饰,用完释放掉,而xcode4.3支持ARC,可以使用retian,不需要手动释放内存,系统会自动为你完成,如果你在xcode4.3上面开发,retian和strong都是一样的,没区别

拓展

retain是指针拷贝,copy是内容拷贝。在拷贝之前,都会释放旧的对象

unsafe_unretained

和assign类似,但是它适用于对象类型,当目标被摧毁时,属性值不会自动清空(unsafe)。这是和weak的区别

@property (nonatomic, strong) TestObj *s1;
@property (nonatomic, unsafe_unretained)  TestObj *s2;

self.s1 = [[TestObj allow] init];
self.s2 = self.s1;
self.s1 = nil;
NSLog(@"s2 = %@",self.s2);

结果:crash;
内存:前:*_s1: 0x7fd5dbfa42f0;  *_s2: 0x7fd5dbfa42f0
     后:*_s1: 0x0;             *_s2: 0x7fd5dbfa42f0
     
可见,s1、s2指针指向的地址是相同的,s1=nil,让该块地址被回收,当s2指向这个地址时,就会找不到

拓展

weak指针的前身,现在已被weak取代

与weak的最大区别是,unsafe_unretained所指向的对象在释放掉后,unsafe_unretained不会"归零"(weak指针会自动设置为nil),可能指向"僵尸"对象

NSString 为何要用copy、而不是strong

首先我们来看看使用strong会出现什么情况:

.h 
@property (nonatomic,strong)NSString *name;

.m

NSMutableString * myName = [NSMutableString stringWithFormat:@"拓跋"];
self.name = myName;

NSLog(@"使用strong第一次得到的名字:%@",self.name);
[myName appendString:@"野"];
NSLog(@"使用strong第一次得到的名字:%@",self.name);
打印结果:
2017-05-06 08:09:06.730 Quartz2DTest[11074:8265411] 使用strong第一次得到的名字:拓跋
2017-05-06 08:09:06.730 Quartz2DTest[11074:8265411] 使用strong第一次得到的名字:拓跋野

结论

通过上面的例子我们可以看出,我们没有直接修改self.name的情况下self.name却被修改了,我们的初衷只是想修改myName,self.name却被修改了、而这就是我们使用strong所不想看到的、它会破坏程序的封装性(使用strong后、self.namemyName指向的是同一片内存、所以修改一个值后两个值都变了)

那么使用copy又会得到什么结果呢?下面是使用copy的例子:

.h
@property (nonatomic, copy) NSString *name;
.m

NSMutableString * myName = [NSMutableString stringWithFormat:@"拓跋"];
self.name = myName;

NSLog(@"使用strong第一次得到的名字:%@",self.name);
[myName appendString:@"野"];
NSLog(@"使用strong第一次得到的名字:%@",self.name);
打印结果:
2017-05-06 08:18:15.730 Quartz2DTest[11134:8279219] 使用strong第一次得到的名字:拓跋
2017-05-06 08:18:15.730 Quartz2DTest[11134:8279219] 使用strong第一次得到的名字:拓跋

结论

myName通过copy得到了一个新的对象赋值给self.name、这样我们再修改myName就跟self.name没什么关系了、只有对self.name直接进行赋值才能改变它的值、这样就保证了程序的封装性。

atomic和nonatomic

  • atomicnonatomic用来决定编译器生成的gettersetter是否为原子操作。

atomic

  • 设置成员变量的@property属性时,默认为atomic,提供读写安全。
  • 在多线程环境下,原子操作是必要的,否则有可能引起错误的结果。加了atomic,setter函数会变成下面这样:
  • 会保证 CPU 能在别的线程来访问这个属性之前,先执行完当前流程
  • 速度不快,因为要保证操作整体完成
{lock}
if (property != newValue) {
[property release];
property = [newValue retain];
}
{unlock}
nonatomic的内存管理语义是非原子性的,非原子性的操作本来就是线程不安全的,而atomic的操作是原子性的,但是并不意味着它是线程安全的,它会增加正确的几率,能够更好的避免线程的错误,但是它仍然是线程不安全的。

当使用nonatomic的时候,属性的setter,getter操作是非原子性的,所以当多个线程同时对某一属性读和写操作时,属性的最终结果是不能预测的。

当使用atomic时,虽然对属性的读和写是原子性的,但是仍然可能出现线程错误:当线程A进行写操作,这时其他线程的读或者写操作会因为该操作而等待。当A线程的写操作结束后,B线程进行写操作,然后当A线程需要读操作时,却获得了在B线程中的值,这就破坏了线程安全,如果有线程C在A线程读操作前release了该属性,那么还会导致程序崩溃。所以仅仅使用atomic并不会使得线程安全,我们还要为线程添加lock来确保线程的安全。

也就是要注意:atomic所说的线程安全只是保证了getter和setter存取方法的线程安全,并不能保证整个对象是线程安全的。如下列所示:
比如:@property(atomic,strong)NSMutableArray *arr;  
如果一个线程循环的读数据,一个线程循环写数据,那么肯定会产生内存问题,因为这和setter、getter没有关系。如使用[self.arr objectAtIndex:index]就不是线程安全的。好的解决方案就是加锁。
  
   据说,atomic要比nonatomic慢大约20倍。一般如果条件允许,我们可以让服务器来进行加锁操作。

nonatomic

  • 禁止多线程,变量保护,提高性能。
  • atomic是Objc使用的一种线程保护技术,基本上来讲,是防止在写未完成的时候被另外一个线程读取,造成数据错误。而这种机制是耗费系统资源的,所以在iPhone这种小型设备上,如果没有使用多线程间的通讯编程,那么nonatomic是一个非常好的选择。
  • 指出访问器不是原子操作,而默认地,访问器是原子操作。这也就是说,在多线程环境下,解析的访问器提供一个对属性的安全访问,从获取器得到的返回值或者通过设置器设置的值可以一次完成,即便是别的线程也正在对其进行访问。如果你不指定 nonatomic ,在自己管理内存的环境中,解析的访问器保留并自动释放返回的值,如果指定了 nonatomic ,那么访问器只是简单地返回这个值。

在定义 property 的时候,atomic 和 nonatomic 有何区别?

@property(nonatomic, retain) UITextField *userName;
@property(atomic, retain) UITextField *userName;
@property(retain) UITextField *userName;

这仨有什么不同?

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

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,096评论 1 32
  • 史上最全的iOS面试题及答案 iOS面试小贴士———————————————回答好下面的足够了----------...
    Style_伟阅读 2,352评论 0 35
  • 属性修饰符简述 ios5之前是MRC,内存需要程序员进行管理,ios5之后是ARC,除非特殊情况,比如C框架或者循...
    咖啡绿茶1991阅读 733评论 0 2
  • 前段时间,一篇内容为“煎饼大妈月入3万”的文章铺天盖地的在各大公号上出现,在这片文章火爆的不行的时候,我却在评论里...
    青囱阅读 707评论 2 2
  • 忆伊 ---荒原狼 放荡不羁的心 一无所有 眺望远方的眸 无所忌惮 当风轻轻吹起 我会选择沉默 即使望着...
    荒原狼1993阅读 174评论 0 1