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、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];
        //[array2 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。

时间: 2024-10-11 21:43:50

iOS学习笔记之ARC内存管理的相关文章

IOS学习笔记--Objective-C之内存管理

2014-07-26 17:10 by KenshinCui, 17317 阅读, 6 评论, 收藏, 编辑 概述 我们知 道在程序运行过程中要创建大量的对象,和其他高级语言类似,在ObjC中对象时存储在堆中的,系统并不会自动释放堆中的内存(注意基本类型是由系统自己管 理的,放在栈上).如果一个对象创建并使用后没有得到及时释放那么就会占用大量内存.其他高级语言如C#.Java都是通过垃圾回收来(GC)解决这个问 题的,但在OjbC中并没有类似的垃圾回收机制,因此它的内存管理就需要由开发人员手动维

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

内存管理 内存管理一直是一个不易处理的问题,开发者必须考虑分配回收的方式和时机,针对堆和栈做不同的优化处理,等等.内存管理的核心是动态分配的对象必须保证在使用完毕后有效地释放内存,即管理对象的生命周期.由于C++是一个较为底层的语言,其设计上不包含任何智能管理内存的机制.一个对象在使用完毕后必须被回收,然而在复杂的程序中,对象所有权在不同程序片段间传递或共享,使得确定回收的时机十分困难,因此内存管理成为了程序员十分头疼的问题. 另一方面,过于零散的对象分配回收可能导致堆中的内存碎片化,降低内存的

iOS开发中的ARC内存管理de技术要点

本文旨在通过简明扼要的方式总结出iOS开发中ARC(Automatic Reference Counting,自动引用计数)内存管理技术的要点,所以不会涉及全部细节.这篇文章不是一篇标准的ARC使用教程,并假定读者已经对ARC有了一定了解和使用经验.详细的关于ARC的信息请参见苹果的官方文档与网上的其他教程:) 本文的主要内容: ARC的本质 ARC的开启与关闭 ARC的修饰符 ARC与Block ARC与Toll-Free Bridging ARC的本质 ARC是编译器(时)特性,而不是运行时

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

系统物理页面是由 (Page Frame Number Database )简称PFN数据库来进行管理,实际上是一个数组,每个物理页面都对应一个PFN项. 进程的地址空间是通过VAD(Virtual Address Destriptor)管理.每个进程都有一个AVL树来保存这些VAD节点,来记录使用的地址以及属性等. 进程的内存地址属性分为保留和提交,保留即是使用时候才实际分配内存,而提交时需要交割对现空间的,需要分配物理页面的,然后将两者关联起来. 我们从NtAllocateVirtualMe

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

IOS学习(OC语言)知识点整理 一.OC 中的ARC内存管理 1)ARC中释放对象的内存原则:看这个对象有没有强引用指向它 2)strong:强引用,默认情况下的引用都是强引用 3) weak:弱引用__weak 4)ARC环境下:与内存相关的代码都不能使用了,如果要在ARC环境下使用MRC内存管理代码 如: [super    delloc]  选中项目找到 Build Phases 菜单下的  Compile Sources 项 选中要转换的.m文件, 双击写入此行代码:-fno-objc

黑 马 程 序 员_视频学习总结<Objective-C>----04 内存管理、protocol、block、ARC

---------------------- ASP.Net+Unity开发..Net培训.期待与您交流! ---------------------- 一.内存管理 1.为什么要用内存管理: 移动设备的内存极其有限,每个app所能占用的内存是有限制的.当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不需要再使用的内存空间.比如回收一些不需要使用的对象.变量等 2.管理范围: 任何继承了NSObject的对象,对其他基本数据类型(int.char.float.double.stru

iOS学习笔记(1)— UIView 渲染和内容管理

iOS学习笔记(1)— UIView 渲染和内容管理 iOS中应用程序基本上都是基于MVC模式开发的.UIView就是模型-视图-控制器中的视图,在iOS终端上看到的.摸到的都是UIView. UIView在屏幕上定义了一个矩形区域和管理区域内容的接口.在运行时,一个视图对象控制该区域的渲染:UIView继承自UIResponder,UIResponder是用来响应事件的类,UIView也具有响应事件的能力.所以说UIView具有三个基本的功能,绘制内容并管理内容的布局,响应用户交互,动画.正是

黑马程序员-OC学习笔记之ARC

---------------------- IOS开发.Android培训.期待与您交流! ----------------------一.什么是ARC        ARC是自动引用计数,苹果官方在Xcode4.2及以后版本中加入的一个内存管理机制,完全取代了程序员人工的内存管理,使得程序员只须关心程序本身.使用ARC,编译器会自动在代码中加入内存管理代码,是一个编译器特性,而不是运行时特性,当然,它也和JAVA的垃圾回收不同,它的本质和手动的内存管理是一样的,只是这些代码编译器会自动添加.

iOS学习笔记(十六)——数据库操作(使用FMDB)

iOS中原生的SQLite API在使用上相当不友好,在使用时,非常不便.于是,就出现了一系列将SQLite API进行封装的库,例如FMDB.PlausibleDatabase.sqlitepersistentobjects等,FMDB (https://github.com/ccgus/fmdb) 是一款简洁.易用的封装库,这一篇文章简单介绍下FMDB的使用. 在FMDB下载文件后,工程中必须导入如下文件,并使用 libsqlite3.dylib 依赖包. FMDB同时兼容ARC和非ARC工