可执行程序构成
- Objective-C可执行程序是由(可执行)代码、初始化和未初始化的程序数据、链接信息、重定位信息、局部数据和动态数据构成的。
- 可执行程序、程序数据以及链接在重定位信息会以静态方式分配内存,并在程序的生命周期一直存在。
- 局部数据在语句块中声明并且仅在该语句块中有效,当该语句块执行后局部数据不在继续存在,存在栈内存中,系统自动管理。
- "动态数据"->Objective-C将创建的对象存储在动态分配的内存即“堆”内存中,我们需要进行唯一的内存管理。
- 程序数据包包括以静态方式声明的变量和程序变量(即在程序编译时在代码中设置的常数)。
内存管理不当引起的问题
- 内存泄漏
如果程序没有释放不再使用的对象,就会导致内存泄漏,造成内存浪费,容易耗尽系统内存。 - 悬挂指针
如果程序释放了仍在使用的对象,就会导致该问题,为野指针或僵尸指针,向悬挂指针发送消息会导致程序奔溃。
Objective-C内存管理方式
Objective-C内存管理是通过引用计数实现的,引用计数是一种通过对象的唯一引用,确定对象是否正在被使用。如果对象的引用计数为零时,对象就被视为不再使用,系统将释放这个对象的内存。
苹果公司提供两种方式管理内存:
- 手动管理MRR/MRC
- 自动管理ARC->苹果推荐使用ARC,已经很成熟的计数
1.手动管理
手动管理是建立在“对象所有权”概念上的内存管理机制,只要对象的所有者还存在,对象就不会被Objective-C运行时环境释放。
- 对象引用&对象所有权
Objective-C对象是通过指向Objective-C对象内存地址的变量,以间接方式访问,这种变量也称为指针。
如 KNZPerson *person = [[KNZPerson alloc]init];- person为一个指针变量,保存着对象的内存地址
- [[KNZPerson alloc]init]为创建一个对象并初始化,返回对象的地址</br>
再创建一个指针变量person2,将person赋值给person2,那么person2拥有指向该对象的内存地址。
- KNZPerson *person2 = person;
如果person2没有获得该对象的所有权(通过赋值语句无法获得所有权)的话,当person被释放(person不再指向该对象,该对象被释放),那么person2保存的对象地址指向的内存空间不再存在,person2就是悬挂指针,因此我们需要以手动的方式管理对象的生命周期(保留和释放),在编写代码时***(面向类Class设计时添加内存管理代码)***必须遵守一些内存管理规则。
- 手动内存管理规则
- 为创建的所有对象设置所有权
- 创建:alloc、new、copy或者mutableCopy,计数器+1
注意:Foundation里面的类用alloc创建时也需要进行内存管理,但是使用一些便利方法创建的不需要:
例如:NSString *str = [[NSString alloc]initWithFormat:@"A1"];
需要[str release];
NSString *str2 = [NSString stringWithFarmat:@"A2"];
不需要进行内存管理
- 应使用retain方法获取对象的所有权
- retain:计数器+1
- 当不再使用某个对象时,必须放弃其所有权
- release:计数器-1
- 当计数器=0对象自动释放,释放时自动调用dealloc方法
- 不能放弃不归你所有的对象所有权
- 为创建的所有对象设置所有权
例如:
KNZPerson *person = [[KNZPerson alloc]init];
alloc创建一个对象并且赋予person,计数器+1,当前值为1;
KNZPerson *person2 = [person retain];
person指向的对象发送消息retain,计数器+1,当前值为2;
当person不再指向该对象的话[person release];
计数器-1,当前值为1;
还有指针变量person2指向该对象,该对象不会被释放;
[person2 release];person2也不再指向该对象,计数器-1,当前值为0,
系统认为该对象不再被使用,系统自动调用dealloc方法释放掉该对象。
MRR管理内存情况下如何设计一个类
- 设计一个类拥有其他对象时,初始化时被创建的其他对象引用计数器+1(记得在对象释放调用dealloc方法中,释放掉这些其他对象)
- 创建这个类的实例对象时,这个对象的引用计数器自动+1,就可以拥有这个对象了,不用管对象内部所拥有的其他对象。
- 当这个对象的引用计数器=0时,系统自动调用对象的dealloc方法释放此对象,当对象拥有其他对象时,需要在dealloc方法里面释放掉所拥有的对象,最后调用[super dealloc]方法,彻底释放对象。
@autorelease{自动释放代码块}
在自动释放池里面创建一个实例对象并对这个对象发送autorelease消息,无需再对对象进行内存管理,对象会在释放池最后释放掉。
如 KNZPerson *person = [[[KNZPerson alloc]init]autorelease];用于创建iOS和macOS应用的苹果UI框架、尤其是AppKit和UIKit,能够自动提供自动释放代码块。
-
需要在自动释放池中手动编写自动释放代码块:
- 你编写的程序不是以苹果UI框架为基础的,如命令行工具
- 你实现的逻辑中含有创建许多临时对象的循环。
- 你编写的应用派生了一个或多个辅助线程。
自动管理ARC
自动引用计数是一种功能强大的内存管理工具。与MRR相同,也是通过对象引用计数器来管理对象保留及释放,编译程序时由编译器分析源代码,在编译代码必要位置自动插入retain和release消息。
苹果公司推荐使用ARC进行内存管理。
ARC使用规则和约定
- 不能手动编写发送retain、retainCount、release、autorelease、dealloc消息
- 不能直接进行id和(void *)类型的互换
- 需要使用自动释放池代码块执行由ARC管理的自动释放操作
- 不能使用C结构体重的对象指针。
- 不能使用内存去NSZone
- 为了与非ARC代码协作,不能创建以“copy”开头的方法和自动声明属性
- 默认情况下,ARC并非异常安全。
ARC的生命周期限定符
用来声明常规变量和属性的生命周期
- 常规变量生命周期
- _strong,默认配置
- _weak,表面对象随时被释放,当对象被释放,指针变量会被设置为nil
- _unsafe_unretained,与_weak类似,但指针变量不会设置为nil而是悬挂
- _autorelease,与autorelease方法无关,用于通过引用传递对象。
- 属性生命周期
- strong,默认配置,等同于retain
- weak,类似于assign特性,如果引用对象被释放了,其实例变量会被设置为nil。
循环引用解决方法weak
设计类时,类A拥有类B的实例变量,类A强stong指向类B的实例变量;当类B也拥有类A的实例变量,这个时候用类B也用strong指向类A的实例变量话,那么就无法释放A和B的实例对象了,这个时候需要有一方weak弱指向对方,当对象A释放后,对象B弱weak指向对象A,对象B指针被设置为nil,对象B也会被释放的。
一般iOS开发界面UI控件都用weak,因为添加到window的控件都会被Window强引用着,没有必要再使用一个指针强引用着UI控件。
delegate属性一般weak特性,因为带有代理设计的tableview或者textField之类的UIView控件都会让控制器成为它的代理,控制器已经有强指针指着UIView根视图,根视图再指向带有代理设计的tableview或者textField之类的UIView控件,这个时候使用delegate再strong指向控制器的话就会造成循环引用。