copy/strong/weak/__weak/__strong/assign的使用总结

ARC在编译期间,根据Objective-C对象的存活周期,在适当的位置添加retain和release代码。从概念上讲,ARC与MRC内存管理遵循同样的内存管理规则,区别MRC, ARC工作是编译器完成的, 但是ARC也无法防止循环强引用。

ARC还引入了新的修饰符来修饰变量和声明属性。

声明变量的修饰符:__strong, __weak, __unsafe_unretained, __autoreleasing;

声明属性的修饰符:strong, weak, unsafe_unretained。

对于线程的安全,有nonatomic,这样效率就更高了,但是不是线程的。如果要线程安全,可以使用atomic,这样在访问是就会有线程锁。

记住内存管理法则(最重要的一点):谁使对象的引用计数+1,不再引用时,谁就负责将该对象的引用计数-1(谁引用谁释放)。

下面我们来声明一个Person类来学习:

@interface Person : NSObject

// 注意:苹果有命名规范的,命名属性时,不能以copy开头。

// 如果下面的属性声明为copyString,会编译不通过。

@property (nonatomic, copy) NSString *copiedString;

// 默认会是什么呢?

@property (nonatomic) NSString *name;

// 默认是strong类型

@property (nonatomic) NSArray *array;

@end

如果属性没有指定类型,默认是strong。如果证明呢?验证方法:分别将array属性的类型分别设置为weak, assign,strong,不设置,这四种情况的结果分别是:第一种打印为空,第二种直接直接崩溃,第三种和最后一种是可以正常使用。如下面的验证代码:

Person *lili = [[Person alloc] init];

lili.name = @"LiLi";

lili.copiedString = @"LiLi\' father is LLL";

lili.array = @[@"谢谢", @"感谢"];

NSArray *otherArray = lili.array;

lili = nil;

NSLog(@"%@", otherArray);

再继续添加下面的代码。默认声明变量的类型为__strong类型,因此上面的NSArray *otherArray = lili.array;与__strong NSArray *otherArray = lili.array;是一样的。如果我们要使用弱引用,特别是在解决循环强引用时就特别重要了。我们可以使用__weak声明变量为弱引用,这样就不会增加引用计数值。

__strong NSArray *strongArray = otherArray;

otherArray = nil;

// 打印出来正常的结果。

NSLog(@"strongArray = %@", strongArray);

__weak NSArray * weakArray = strongArray;

strongArray = nil;

// 打印出来:null

NSLog(@"weakArray: %@", weakArray);

xib/storybard连接的对象为什么可以使用weak

@property (nonatomic, weak) IBOutlet UIButton *button;

像上面这行代码一样,在连接时自动生成为weak。因为这个button已经放到view上了,因此只要这个View不被释放,这个button的引用计数都不会为0,因此这里可以使用weak引用。

如果我们不使用xib/storyboard,而是使用纯代码创建呢?

@property (nonatomic, weak) UIButton *button;

使用weak时,由于button在创建时,没有任何强引用,因此就有可能提前释放。Xcode编译器会告诉我们,这里不能使用weak。因此我们需要记住,只要我们在创建以后需要使用它,我们必须保证至少有一个强引用,否则引用计数为0,就会被释放掉。对于上面的代码,就是由于在创建时使用了weak引用,因此button的引用计数仍然为0,也就是会被释放,编译器在编译时会检测出来的。

这样写,在创建时通过self.button = ...就是出现错误,因为这是弱引用。所以我们需要声明为强引用,也就是这样:

@property (nonatomic, strong) UIButton *button;

block声明使用copy

在使用block时,尽量使用typedef来起一个别名,这样更容易阅读。使block作为属性时,使用copy。

typedef void (^HYBTestBlock)(NSString *name);

@property (nonatomic, copy) HYBTestBlock testBlock;

字符串

对于字符串,通常都是使用copy的方式。虽然使用strong似乎也没有没有问题,但是事实上在开发中都会使用copy。为什么这么做?因为对于字符串,我们希望是一次内容的拷贝,外部修改也不会影响我们的原来的值,而且NSString类遵守了NSCopying, NSMutableCopying, NSSecureCoding协议。

下面时使用copy的方式,验证如下:

NSString *hahaString = @"哈哈";

NSString *heheString = [hahaString copy];

// 哈哈, 哈哈

NSLog(@"%@, %@", hahaString, heheString);

heheString = @"呵呵";

// 哈哈, 呵呵

NSLog(@"%@, %@", hahaString, heheString);

我们修改了heheString,并不会影响到原来的hahaString。copy一个对象变成新的对象(新内存地址) 引用计数为1 原来对象计数不变。

属性声明修饰符

属性声明修饰符有:strong, weak, unsafe_unretained, readWrite,默认strong, readWrite的。

strong:strong和retain相似,只要有一个strong指针指向对象,该对象就不会被销毁

weak:声明为weak的指针,weak指针指向的对象一旦被释放,weak的指针都将被赋值为nil;

unsafe_unretained:用unsafe_unretained声明的指针,指针指向的对象一旦被释放,这些指针将成为野指针。

@property (nonatomic, copy) NSString *name;

// 一旦所指向的对象被释放,就会成为野指针

@property (nonatomic, unsafe_unretained) NSString *unsafeName;

lili.name = @"Lili";

lili.unsafeName = lili.name;

lili.name = nil;

// unsafeName就变成了野指针。这里不会崩溃,因为为nil.

NSLog(@"%@", lili.unsafeName);

深拷贝与浅拷贝

关于浅拷贝,简单来说,就像是人与人的影子一样。而深拷贝就像是梦幻西游中的龙宫有很多个长得一样的龙宫,但是他们都是不同的精灵,因此他们各自都是独立的。

我相信还有不少朋友有这样一种误解:浅拷贝就是用copy,深拷贝就是用mutableCopy。如果有这样的误解,一定要更正过来。copy只是不可变拷贝,而mutableCopy是可变拷贝。比如,NSArray *arr = [modelsArray copy],那么arr是不可变的。而NSMutableArray *ma = [modelsArray mutableCopy],那么ma是可变的。

lili.array = [@[@"谢谢", @"感谢"] mutableCopy];

NSMutableArray *otherArray = [lili.array copy];

lili.array[0] = @"修改了谢谢";

NSLog(@"%@ %@", otherArray[0], lili.array[0]);

// 打印: 谢谢 修改了谢谢

// 说明数组里面是字符串时,直接使用copy是相当于深拷贝的。

NSLog(@"%@ %@", otherArray[0], lili.array[0]);

这里就是浅拷贝,但是由于数组中的元素都是字符串,因此不会影响原来的值。

数组中是对象时:

NSMutableArray *personArray = [[NSMutableArray alloc] init];

Person *person1 = [[Person alloc] init];

person1.name = @"lili";

[personArray addObject:person1];

Person *person2 = [[Person alloc] init];

person2.name = @"lisa";

[personArray addObject:person2];

// 浅拷贝

NSArray *newArray = [personArray copy];

Person *p = newArray[0];

p.name = @"lili的名字被修改了";

// 打印结果:lili的名字被修改了

// 说明这边修改了,原来的数组对象的值也被修改了。虽然newArray和personArray不是同一个数组,不是同一块内存,

// 但是实际上两个数组的元素都是指向同一块内存。

NSLog(@"%@", ((Person *)(personArray[0])).name);

深拷贝,其实就是对数组中的所有对象都创建一个新的对象:

NSMutableArray *personArray = [[NSMutableArray alloc] init];

Person *person1 = [[Person alloc] init];

person1.name = @"lili";

[personArray addObject:person1];

Person *person2 = [[Person alloc] init];

person2.name = @"lisa";

[personArray addObject:person2];

// 深拷贝

NSMutableArray *newArray = [[NSMutableArray alloc] init];

for (Person *p in personArray) {

Person *newPerson = [[Person alloc] init];

newPerson.name = p.name;

[newArray addObject:newPerson];

}

Person *p = newArray[0];

p.name = @"lili的名字被修改了";

// 打印结果:lili

NSLog(@"%@", ((Person *)(personArray[0])).name);

Getter/Setter

在ARC下,getter/setter的写法与MRC的不同了。我面试过一些朋友,笔试这关就写得很糟(不包括算法)。通常在笔试时都会让重写一个属性的Getter/Setter方法。

@property (nonatomic, strong) NSMutableArray *array;

- (void)setArray:(NSMutableArray *)array {

if (_array != array) {

_array = nil;

_array = array;

}

}

如果是要重写getter就去呢?就得增加一个变量了,如果同时重写getter/setter方法,就不会自动生成_array变量,因此我们可以声明一个变量为_array:

- (void)setArray:(NSMutableArray *)array {

if (_array != array) {

_array = nil;

_array = array;

}

}

- (NSMutableArray *)array {

return _array;

}

总结

关于属性的这些选项的学习,做一下总结:

所有的属性,都尽可能使用nonatomic,以提高效率,除非真的有必要考虑线程安全。

NSString:通常都使用copy,以得到新的内存分配,而不只是原来的引用。

strong:对于继承于NSObject类型的对象,若要声明为强使用,使用strong,若要使用弱引用,使用__weak来引用,用于解决循环强引用的问题。

(对于数组, 字典, 集合, 字符串来说, 可变用strong修饰, 不可变用copy修饰)

weak:对于xib上的控件引用,可以使用weak,也可以使用strong。

__weak:对于变量的声明,如果要使用弱引用,可以使用__weak,如:__weak typeof(Model) weakModel = model;就可以直接使用weakModel了。

__strong:对于变量的声明,如果要使用强引用,可以使用__strong,默认就是__strong,因此不写与写__strong声明都是一样的。

unsafe_unretained:这个是比较少用的,几乎没有使用到。在所引用的对象被释放后,该指针就成了野指针,不好控制。

__unsafe_unretained:也是很少使用。同上。

__autoreleasing:如果要在循环过程中就释放,可以手动使用__autoreleasing来声明将之放到自动释放池。


参考资料

http://www.jianshu.com/p/c16467bbedc1

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

推荐阅读更多精彩内容