对copy/strong/weak/__weak/__strong/assign的使用场景总结总结
ARC在编译期间,根据Objective-C对象的存活周期,在适当的位置添加retain和release代码。从概念上讲,ARC与手动引用计数内存管理遵循同样的内存管理规则,但是ARC也无法防止循环强引用。
ARC还引入了新的修饰符来修饰变量和声明属性。
- 声明变量的修饰符:
__strong
,__weak
,__unsafe_unretained
,__autoreleasing
; - 声明属性的修饰符:
strong
,weak
,unsafe_unretained
。 - 对象和Core Foundation-style对象直接的转换修饰符号:
__bridge
,__bridge_retained
或CFBridgingRetain
,__bridge_transfer
或CFBridgingRelease
。 - 对于线程的安全,有
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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
如果属性没有指定类型,默认是什么呢?其实是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);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
再继续添加下面的代码。默认声明变量的类型为__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;// 打印出来:nullNSLog(@"weakArray: %@", weakArray);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
xib/storybard连接的对象为什么可以使用weak
@property (nonatomic, weak) IBOutlet UIButton *button;
- 1
像上面这行代码一样,在连接时自动生成为weak
。因为这个button已经放到view上了,因此只要这个View不被释放,这个button的引用计数都不会为0,因此这里可以使用weak
引用。
如果我们不使用xib/storyboard
,而是使用纯代码创建呢?
@property (nonatomic, weak) UIButton *button;
- 1
补充:有朋友反映这里说得不够详细,感谢这位朋友的反馈。
使用
weak
时,由于button
在创建时,没有任何强引用,因此就有可能提前释放。Xcode编译器会告诉我们,这里不能使用weak
。因此我们需要记住,只要我们在创建以后需要使用它,我们必须保证至少有一个强引用,否则引用计数为0,就会被释放掉。对于上面的代码,就是由于在创建时使用了weak
引用,因此button
的引用计数仍然为0,也就是会被释放,编译器在编译时会检测出来的。
这样写,在创建时通过self.button = ...
就是出现错误,因为这是弱引用。所以我们需要声明为强引用,也就是这样:
@property (nonatomic, strong) UIButton *button;
- 1
block声明使用copy
在使用block时,尽量使用typedef
来起一个别名,这样更容易阅读。使block作为属性时,使用copy
。
typedef void (^HYBTestBlock)(NSString *name);@property (nonatomic, copy) HYBTestBlock testBlock;
- 1
- 2
- 3
字符串
对于字符串,通常都是使用copy
的方式。虽然使用strong
似乎也没有没有问题,但是事实上在开发中都会使用copy
。为什么这么做?因为对于字符串,我们希望是一次内容的拷贝,外部修改也不会影响我们的原来的值,而且NSString类遵守了NSCopying
, NSMutableCopying
, NSSecureCoding
协议。
下面时使用copy
的方式,验证如下:
NSString *hahaString = @"哈哈";NSString *heheString = [hahaString copy];// 哈哈, 哈哈NSLog(@"%@, %@", hahaString, heheString);heheString = @"呵呵";// 哈哈, 呵呵NSLog(@"%@, %@", hahaString, heheString);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
我们修改了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);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
深拷贝与浅拷贝
关于浅拷贝,简单来说,就像是人与人的影子一样。而深拷贝就像是梦幻西游中的龙宫有很多个长得一样的龙宫,但是他们都是不同的精灵,因此他们各自都是独立的。
我相信还有不少朋友有这样一种误解:浅拷贝就是用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] = @"修改了谢谢";
// 打印: 谢谢 修改了谢谢
// 说明数组里面是字符串时,直接使用copy是相当于深拷贝的。
NSLog(@"%@ %@", otherArray[0], lili.array[0]);
- 1
- 2
- 3
- 4
- 5
- 6
这里就是浅拷贝,但是由于数组中的元素都是字符串,因此不会影响原来的值。
数组中是对象时:
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);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
深拷贝,其实就是对数组中的所有对象都创建一个新的对象:
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的名字被修改了";// 打印结果:liliNSLog(@"%@", ((Person *)(personArray[0])).name);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
Getter/Setter
在ARC下,getter/setter的写法与MRC的不同了。我面试过一些朋友,笔试这关就写得很糟(不包括算法)。通常在笔试时都会让重写一个属性的Getter/Setter方法。
@property (nonatomic, strong) NSMutableArray *array;- (void)setArray:(NSMutableArray *)array {if (_array != array) {_array = nil;_array = array;}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
如果是要重写getter就去呢?就得增加一个变量了,如果同时重写getter/setter方法,就不会自动生成_array变量,因此我们可以声明一个变量为_array
:
- (void)setArray:(NSMutableArray *)array {if (_array != array) {_array = nil;_array = array;}
}- (NSMutableArray *)array {return _array;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
总结
关于属性的这些选项的学习,做一下总结:
- 所有的属性,都尽可能使用
nonatomic
,以提高效率,除非真的有必要考虑线程安全。 - NSString:通常都使用
copy
,以得到新的内存分配,而不只是原来的引用。 - strong:对于继承于NSObject类型的对象,若要声明为强使用,使用
strong
,若要使用弱引用,使用__weak
来引用,用于解决循环强引用的问题。 - weak:对于xib上的控件引用,可以使用weak,也可以使用strong。
- __weak:对于变量的声明,如果要使用弱引用,可以使用__weak,如:__weak typeof(Model) weakModel = model;就可以直接使用weakModel了。
- __strong:对于变量的声明,如果要使用强引用,可以使用__strong,默认就是__strong,因此不写与写__strong声明都是一样的。
- unsafe_unretained:这个是比较少用的,几乎没有使用到。在所引用的对象被释放后,该指针就成了野指针,不好控制。
- __unsafe_unretained:也是很少使用。同上。
- __autoreleasing:如果要在循环过程中就释放,可以手动使用__autoreleasing来声明将之放到自动释放池。
参考资料
官方文档关于自动引用计数内存管理介绍:https://developer.apple.com/library/mac/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html
对copy/strong/weak/__weak/__strong/assign的使用场景总结总结相关推荐
- Objective-c属性修饰关键字(assign retain copy strong weak)
一.线程相关: [atomic/nonatomic] (1)atomic[默认属性]:OC使用的一种线程保护技术,是防止在写未完成的时候被另外一个线程读取,造成数据错误.而这种机制是耗费系统资源的,所 ...
- assign,copy,strong,weak,nonatomic的具体理解
例子: NSString *houseOfMM = [[NSString alloc] initWithString:'MM的三室两厅']; 上面一段代码会执行以下两个动作: 1 在堆上分配一段内 ...
- assign,copy,strong,weak,nonatomic的理解
举个例子: NSString *houseOfMM = [[NSString alloc] initWithString:'MM的三室两厅']; 上面一段代码会执行以下两个动作: 1 在堆上分配一 ...
- 关于@property()的那些属性及ARC简介【nonatomic,atomic,assign,retain,strong,weak,copy。】
@property()常用的属性有:nonatomic,atomic,assign,retain,strong,weak,copy. 其中atomic和nonatomic用来决定编译器生成的gette ...
- copy, retain, assign , readonly , readwrite,strong,weak,nonatomic整理
copy:建立一个索引计数为1的对象,然后释放旧对象 对NSString 对NSString 它指出,在赋值时使用传入值的一份拷贝.拷贝工作由copy方法执行,此属性只对那些实行了NSCopying协 ...
- Objective-C之MRC、ARC模式下,属性修饰关键字strong、retain、assign、weak的区别和联系...
平时用strong.retain.assign.weak这几个关键字的时候,也没有过多的在意,下边就MRC.ARC模式下,每个关键字做一下总结.为了简明期间,采用表格的形式进行展示. MRC ARC ...
- @property 各个属性作用【使用时最需注意strong/weak类型】【补充部分内存知识】...
一. 属性readwrite,readonly,assign,retain,copy,nonatomic 等各是什么作用,在那种情况下用? 1> readwrite:同时生成get方法和set ...
- iOS 浅谈:深.浅拷贝与copy.strong
深.浅拷贝 copy mutableCopy NSString NSString *string = @"汉斯哈哈哈"; // 没有产生新对象 NSString *copyStri ...
- iOS学习笔记-retain/assign/strong/weak/copy/mutablecopy/autorelease区别
readwrite:是可读可写特性,需要生成getter和setter方法: readonly是之都特性,只会生成getter方法,不会生成setter方法,不希望属性在类外改变时候使用: alloc ...
最新文章
- 王振的开发板_Android
- 你一笑,AI就变聪明了?微软最新研究提出“微笑训练法”
- postgresql----文本搜索类型和检索函数
- iOS - appledoc 文档生成
- sqlsourcesafe mysql_mysql权限问题,看不到其它的库!
- Android当中layer-list使用来实现多个图层堆叠到一块儿
- 前端学习(3094):vue+element今日头条管理-反馈
- 名字英文name域名_企业建站必读:六大方法锁定最佳企业域名
- Python实战从入门到精通第二讲——类
- Quartz任务调度器
- 如何计算信号的信噪比
- excel查找指定表计算机,两个excel表格找文本相同数据库-excel如何查找并自动填写对应数据...
- 去哪儿PMO Head赵云:项目管理背后有哪些生存之道?
- dubbo之telnet使用
- DFM文件以及写该文章的原因等等
- 如何快速打通CRM系统和ERP系统,实现业务流程自动化流转
- postgresql查询锁表以及解除锁表
- 计算机硬件系统结构主要分为什么五大组成,硬件系统的五大组成部分
- 《怪诞行为学》读书笔记
- 案例-站狼云品智美站助力必信空调中国制造领先品牌