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、copyunsafe_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是浅拷贝

对系统的容器类,如果是可变容器,则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内存管理的更多相关文章

  1. iOS学习17之OC内存管理

    1.内存管理的方式 1> iOS应用程序出现Crash(闪退),90%的原因是因为内存问题. 2> 内存问题 野指针异常:访问没有所有权的内存,如果想要安全的访问,必须确保空间还在 内存泄 ...

  2. iOS学习之Object-C语言内存管理

    一.内存管理的方式      1.iOS应用程序出现Crash(闪退),90%的原因是因为内存问题.      2.内存问题:      1)野指针异常:访问没有所有权的内存,如果想要安全的访问,必须 ...

  3. 【cocos2d-x 3.x 学习笔记】对象内存管理

    内存管理 内存管理一直是一个不易处理的问题.开发人员必须考虑分配回收的方式和时机,针对堆和栈做不同的优化处理,等等.内存管理的核心是动态分配的对象必须保证在使用完成后有效地释放内存,即管理对象的生命周 ...

  4. 【原】iOS学习18之OC内存管理高级

    1.属性的内存管理 1> 属性的语义特性 2> assign下的属性内部实现 @property (nonatomic, assign) NSString *name; @synthesi ...

  5. iOS学习之Object-C语言内存管理高级

    一.属性的内存管理

  6. JVM学习笔记一:内存管理

    参考资料 本文参考:<深入理解Java虚拟机>作者 周志明 知识产权归作者所有 走近java java组成部分:java语言.各平台虚拟机.Class文件结构.java api 类库.第三 ...

  7. 操作系统学习笔记(三) windows内存管理

    //系统物理页面是由 (Page Frame Number Database )简称PFN数据库来进行管理,实际上是一个数组,每个物理页面都对应一个PFN项. 进程的地址空间是通过VAD(Virtua ...

  8. iOS学习之C语言内存管理

         一.存储区划分      按照地址从高到低的顺序:栈区,堆区,静态区,常量区,代码区    1.栈区:局部变量的存储区域     局部变量基本都在函数.循环.分支中定义     栈区的内存空 ...

  9. iOS阶段学习第21天笔记(ARC内存管理-Copy-代理)

    iOS学习(OC语言)知识点整理 一.OC 中的ARC内存管理 1)ARC中释放对象的内存原则:看这个对象有没有强引用指向它 2)strong:强引用,默认情况下的引用都是强引用 3) weak:弱引 ...

随机推荐

  1. ThreadLocal的几种误区

    最近由于需要用到ThreadLocal,在网上搜索了一些相关资料,发现对ThreadLocal经常会有下面几种误解 一.ThreadLocal是java线程的一个实现       ThreadLoca ...

  2. 转 RMI、RPC、SOAP通信技术介绍及比对

    http://www.open-open.com/home/space.php?uid=37924&do=blog&id=8974 1.RMI 使用java的程序员,对于RMI(Rem ...

  3. JSP的执行过程及其异常处理机制

    1.JSP的执行过程     虽然JSP感觉上很像一般的HTML网页,但事实上它是以Servlet的形式被运行的.因为JSP文件在第一次运行的时候会先解释成Servlet源文件,然后编译成Servle ...

  4. OpenGL基础知识

    基本概念 透视(Perspective)变换(Transformation)投影矩阵(Projection Matrix):用于将3D坐标转换为2D屏幕坐标光栅化(Rasterization): 实际 ...

  5. SUN dataset图像数据集下载

    SUN dataset数据集,有两个不错的网址: http://vision.princeton.edu/projects/2010/SUN/ (普林斯顿大学) http://groups.csail ...

  6. USACO Section 3.3: A Game

    第一次碰到博弈论题目,是很棘手,博弈论题目要考虑全局最优的解法,我第一次用了局部最优的,而且vector也没pop_front()操作.后来看了网上的用dp的方法解的. 博弈论的题目基本都得用dp法子 ...

  7. [HDOJ5510]Bazinga(并查集)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5510 普通集合会tle,换高贵的并查集. #include <algorithm> #in ...

  8. JAVA设计模式之【简单工厂模式】

    1.创建抽象类User public abstract class User // 抽象类 { public void sameOperation() { System.out.println(&qu ...

  9. c语言宏定义

    一. #define是C语言中提供的宏定义命令,其主要目的是为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率,但学生在学习时往往不能理解该命令的本质,总是在此处产生一些困惑,在编程时 ...

  10. Qt之透明提示框

    简述 经常使用企鹅的小伙伴一定对登录失败的提示框很熟悉,主要涉及窗口透明并添加图标.提示信息.关闭按钮的显示等. 我们可以利用QWidget创建一个提示框,然后通过样式设置我们想要的效果. 简述 效果 ...