iOS学习笔记之ARC内存管理
iOS学习笔记之ARC内存管理
写在前面
ARC(Automatic Reference Counting),自动引用计数,是iOS中采用的一种内存管理方式。
指针变量与对象所有权
指针变量暗含了对其所指向对象的所有权
当某个方法(或函数)有一个指向某个对象的局部变量时,可以称该方法(或函数)拥有该变量所指向的对象,如:
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSString *test = [[NSString alloc] init];
}
return 0;
}
上述代码中,main函数中有一个指向NSString的test对象,因此main函数拥有那个用相应的NSString对象。
当某个对象有一个指向其他对象的实例变量时,可以称该对象用用该实例变量所指向的对象。如:
@interface Item : NSObject
{
NSString *_itemName;
}
@end
上述代码中,我们声明一个Item类,在其头文件中创建一个NSString类型的实例变量_itemName,因此Item类型的对象拥有一个NSString对象。
对象所有权用于帮助我们决定是否应该释放某个对象并回收该对象占有的内存
如果某个对象没有拥有者,就应该将其释放掉。没有拥有者的对象是孤立的,程序无法向其发送消息。保留这样的对象只会浪费宝贵的内存空间,导致内存泄漏问题。
如果某个对象有一个或多个拥有者,就必须保留不能释放。如果释放了某个对象,但是其他对象或方法仍然有指向该对象的指针(准确地说,是指向该对象被释放前的地址),那么向该指针指向的对象发送消息就会使应用崩溃。释放正在使用的对象的错误称为过早释放。指向不存在的对象的指针称为空指针或者空引用。
对象失去拥有者的情况当程序修改某个指向特定对象的变量并将其指向另一个对象时。
当程序将某个指向特定对象的变量设置为nil时。
当程序释放对象的某个拥有者时。
当从collection类中(例如数组)删除对象时。
所有权链条
因为对象可以拥有其他对象,后者也可以再拥有别的对象,所以释放一个对象可能会产生连锁反应,导致多个对象失去拥有者,进而释放对象并归还内存。
强引用与弱引用
强引用:如果指针变量指向了某个对象,那么相应的对象就多一个拥有者,并且不会被程序释放,这种指针特性称为强引用。
弱引用:指针变量不影响其指向对象的拥有者个数。这种不会改变对象拥有者个数的指针特性称为弱引用。
弱引用非常适合解决强引用循环的内存管理问题。当两个或两个以上的对象相互之间有强引用特性的指针关联时,就会产生强引用循环。强引用循环会导致内存泄漏。当两个对象互相拥有时,将无法通过ARC机制来释放。即使应用中的其他对象都释放了针对这两个对象的所有权,这两个对象及其拥有的对象也无法释放。
属性特性
iOS中属性后可以跟一组特性,用于描述相应存取方法的行为。这些特性写在@property指令后的小括号中。如:
@property (nonatomic, readwrite, copy) NSString *itemName;
其中,第一个特性用于描述属性的多线程特性,第二个特性用于描述属性的读/写特性,第三个特性用于描述属性的内存管理特性。
属性的内存管理特性有四种可选类型:strong、weak、copy和unsafe_unretained。
unsafe_unretained
对于不指向任何对象的属性(如int value),不需要做内存管理,这时应该选用unsafe_unretained,它表示存取方法会直接为实例变量赋值。unsafe_unretained中的”unsafe(不安全)”是相对于弱引用而言的。与弱引用不同,unsafe_unretained类型的指针指向的对象被销毁时,指针不会自动设置为nil,而是成为空指针,因此不安全。但是当处理非对象属性时,是不会出现空指针问题的。unsafe_unretained是非对象属性的默认内存管理特性,不用明确写出。
对于指向oc对象的属性,四种类型都有可能,默认是strong类型,但通常需要明确写出(Apple可能会修改默认值)。
strong
在ARC环境下,某个对象只要有一个指向它的strong指针,该对象就不能被销毁。一旦该对象没有strong类型的指针指向它了,该对象就应该被销毁。某个对象的引用计数等于指向该对象的strong指针的个数。
weak
在ARC环境下,weak指针不会增加其指向对象的引用计数。当其指向的对象销毁后,weak指针自动设置为nil,避免了出现野指针(指向不可以内存的指针)
copy
copy略微复杂一些。分为浅拷贝和深拷贝。
浅拷贝
也称为指针拷贝,副本和源对象是同一个对象,源对象的引用计数加1,其本质是增加了一个指向源对象的指针。
深拷贝
副本对象和源对象是不同的两个对象,源对象引用计数器不变,副本对象计数器为1。其本质是产生了新的对象。
此外,复制还有copy与mutableCopy之分
copy
能接收copy消息的类必须遵守NSCopying协议。对系统的非容器类对象,copy是浅拷贝
对系统的容器类,如果是不可变容器,copy是浅拷贝
mutableCopy
能接收mutableCopy消息的类必须遵守NSMutableCopying协议。对所有类,mutableCopy都是深拷贝。
首先看下系统非容器类的案例代码:
NSString *str0 = [NSString stringWithFormat:@"test"];
NSString *str1 = [str0 copy];
NSString *str2 = [str0 mutableCopy];
NSLog(@"The address of str0 is: %p", str0);
NSLog(@"The address of str1 is: %p", str1);
NSLog(@"The address of str2 is: %p", str2);
运行结果如下:

可见用copy的str1与str0的地址相同,而用mutableCopy生成的str2却与前面两者地址不同。
下面是一个NSArray类型的案例
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
//声明一个不可变数组,并初始化
NSArray *array0 = [NSArray arrayWithObjects:@"one", @"two", nil];
//用copy复制一个NSArray副本,浅拷贝
NSArray *array1 = [array0 copy];
//用mutableCopy复制一个NSArray副本,深拷贝
NSArray *array2 = [array0 mutableCopy];
//用copy复制一个NSMutableArray副本,浅拷贝
NSMutableArray *array3 = [array0 copy];
//[array3 addObject:@"three"];//可通过编译,但运行异常
//用mutableCopy复制一个NSMutableArray副本,深拷贝
NSMutableArray *array4 = [array0 mutableCopy];
[array4 addObject:@"three"];//编译和运行都可通过
NSLog(@"The address of array0 is: %p", array0);
NSLog(@"The address of array1 is: %p", array1);
NSLog(@"The address of array2 is: %p", array2);
NSLog(@"The address of array3 is: %p", array3);
NSLog(@"The address of array4 is: %p", array4);
}
return 0;
}
运行结果如下

可见,使用mutableCopy的array2、array4的地址与array0地址不同,其他用copy得到的地址都相同。
如果想要自定义的对象拥有复制特性,必须实现NSCopying,NSMutableCopying协议,实现该协议的copyWithZone方法和mutableCopyWithZone方法。深拷贝和浅拷贝的区别就在于copyWithZone方法的实现,
各种内存特性使用情况
strong:默认属性,除了NSString/block外的OC对象都使用
weak:各种UI控件(不绝对,某些控件也要使用strong)
copy:copy创建的是不可变副本(imutable),而mutableCopy创建的是可变副本(mutable)
总结
对于属性特性中的strong、weak等关键字,也有对应的局部变量版本。在局部变量前添加__strong就声明了一个强引用特性的局部变量。
另外,有了ARC之后,并不意味着程序员就完全不用手动管理内存了。ARC只能应用在OC对象上,如果涉及到底层的malloc()或者free(),ARC就无用武之地了,必须要程序员手动管理。此外,对于strong类型的指针,在不再使用之后,程序员也要手动设置为nil。
iOS学习笔记之ARC内存管理的更多相关文章
- iOS学习17之OC内存管理
1.内存管理的方式 1> iOS应用程序出现Crash(闪退),90%的原因是因为内存问题. 2> 内存问题 野指针异常:访问没有所有权的内存,如果想要安全的访问,必须确保空间还在 内存泄 ...
- iOS学习之Object-C语言内存管理
一.内存管理的方式 1.iOS应用程序出现Crash(闪退),90%的原因是因为内存问题. 2.内存问题: 1)野指针异常:访问没有所有权的内存,如果想要安全的访问,必须 ...
- 【cocos2d-x 3.x 学习笔记】对象内存管理
内存管理 内存管理一直是一个不易处理的问题.开发人员必须考虑分配回收的方式和时机,针对堆和栈做不同的优化处理,等等.内存管理的核心是动态分配的对象必须保证在使用完成后有效地释放内存,即管理对象的生命周 ...
- 【原】iOS学习18之OC内存管理高级
1.属性的内存管理 1> 属性的语义特性 2> assign下的属性内部实现 @property (nonatomic, assign) NSString *name; @synthesi ...
- iOS学习之Object-C语言内存管理高级
一.属性的内存管理
- JVM学习笔记一:内存管理
参考资料 本文参考:<深入理解Java虚拟机>作者 周志明 知识产权归作者所有 走近java java组成部分:java语言.各平台虚拟机.Class文件结构.java api 类库.第三 ...
- 操作系统学习笔记(三) windows内存管理
//系统物理页面是由 (Page Frame Number Database )简称PFN数据库来进行管理,实际上是一个数组,每个物理页面都对应一个PFN项. 进程的地址空间是通过VAD(Virtua ...
- iOS学习之C语言内存管理
一.存储区划分 按照地址从高到低的顺序:栈区,堆区,静态区,常量区,代码区 1.栈区:局部变量的存储区域 局部变量基本都在函数.循环.分支中定义 栈区的内存空 ...
- iOS阶段学习第21天笔记(ARC内存管理-Copy-代理)
iOS学习(OC语言)知识点整理 一.OC 中的ARC内存管理 1)ARC中释放对象的内存原则:看这个对象有没有强引用指向它 2)strong:强引用,默认情况下的引用都是强引用 3) weak:弱引 ...
随机推荐
- idea自动生成serialVersionUID
Setting->Plugins 找到一个叫 GenerateSerialVersionUID 的插件 下载安装好,alt+insert就可以看到 默认情况下Intellij IDEA是关闭了 ...
- 实用Linux命令,不求最全但求实用-------iptables命令实战
开始配置 我们来配置一个filter表的防火墙. (1)查看本机关于IPTABLES的设置情况 [root@tp ~]# iptables -L -n Chain INPUT (policy ACCE ...
- android 使用静态变量传递数据
使用静态变量传递数据之通用方式. 测试应用:当前页面点击button传递数据到一个新的页面显示在textview中. 首先在,mainActivity.xml文件中加入一个button按钮 <B ...
- C# 文件递归
C# 文件递归 Directory.GetDirectories: 获取指定目录下的文件夹,不包括子目录: Directory.GetFiles:获取指定文件夹下的文件,不包括子目录: 1.获取所 ...
- POJ 1904 HDU 4685
这两道题差不多,POJ这道我很久以前就做过,但是比赛的时候居然没想起来.. POJ 这道题的题意是,N个王子每个人都有喜欢的公主,当他们选定一个公主结婚时,必须是的剩下的人也能找到他喜欢的公主结婚. ...
- 验证码图片生成工具类——Captcha.java
验证码图片生成工具,使用JAVA生成的图片验证码,调用getRandcode方法获取图片验证码,以流的方式传输到前端页面. 源码如下:(点击下载 Captcha.java) import java. ...
- ThinkPad L421 如何禁用触摸板
控制面板 - 硬件和声音 - 鼠标 . 选中如下图所示的 UltraNav 选项卡. 将 启用 TouchPad 前的 √ 去掉即可. 如果没有 UltraNav 这一选项卡,可至联想官网下载相关驱动 ...
- SQL SERVER 常用字符类型的区别
长度为 n 个字节的固定长度且非 Unicode 的字符数据.n 必须是一个介于 1 和 8,000 之间的数值.存储大小为 n 个字节.char 在 SQL-92 中的同义词为 character. ...
- shell脚本实现 视频格式转换 ffmpeg 实现视频转换
#!/bin/bash original=$1 echo $original # check whether file is exist # if $original de chang du wei ...
- Python自带包建立简单web服务器
在DOS里cd到准备做服务器根目录的路径下,输入命令: python -m Web服务器模块 [端口号,默认8000] 例如: python -m SimpleHTTPServer 8080 然后就可 ...