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. Linux系统下如何禁止ping命令或允许ping命令的方法

    1.禁止pingecho 1 >/proc/sys/net/ipv4/icmp_echo_ignore_all 2.允许ping echo 0 >/proc/sys/net/ipv4/ic ...

  2. 在CentOS下面编译WizNote Qt Project

    编译环境 CentOS 64位 Desktop 版本:6.5 编译前的准备: CentOS的用户默认没有root权限,如果当前用户没有权限root,则可以在终端里面执行下面的命令: su root s ...

  3. Java:网络编程之UDP的使用

    java.net  类 DatagramSocket 此类表示用来发送和接收数据报包的套接字,数据报套接字是包投递服务的发送或接收点. java.net  类 DatagramPacket 此类表示数 ...

  4. JVM内存区域划分

    前言 Java程序的运行是通过Java虚拟机来实现的.通过类加载器将class字节码文件加载进JVM,然后根据预定的规则执行.Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同 ...

  5. DWR3.0 dwr 返回值(数组,集合,Map)

    首先导入项目所需要的包,如下:dwr.jar,commons-logging-1.0.4.jar,版本可以调整 1.web.xml<?xml version="1.0" en ...

  6. jquery ajax 开发手记

    1.json解析的格式要求更严格了,必须全部加引号,否则无法识别 {"result":"false"} 2.ashx如果要使用Session需要继承接口IReq ...

  7. laravel Event执行顺序

    laravel一大特色就是event事件系统.一般首先要listen一个事件,随后fire那个事件,这时执行路径将会调用event handler,返回后继续执行.例如: Event::listen( ...

  8. HDU 1869 六度分离【floyd】

    题意:给出n个人,m个关系,问是否满足任意两个人之间的距离通过6个人就可以连接 用floyd就可以了,注意距离是大于7 #include<iostream> #include<cst ...

  9. Ubuntu 14.04搭建简单git服务器

    /****************************************************************************** * Ubuntu 14.04搭建简单gi ...

  10. 修改Android系统字号(一)

    /*********************************************************************** * 修改Android系统字号(一) * 说明: * ...