个性化阅读
专注于IT技术分析

Objective-C内存管理详细解释 – Objective-C开发教程

上一章Objective-C开发教程请查看:Objective-C快速枚举用法

这一章我们谈论OC开发中的一个重要概念:内存管理,实际上,学习每一种语言都有必要了解其内存管理模式,这有助于你写出更好的代码。

Objective-C内存管理中最基本的一个概念就是引用计数(Reference Count),了解这个基本就明白了OC的内存管理机制了(当然还需要了解一下堆栈),OC主要提供两种方式管理内存:手动引用计数(MRR)和自动引用计数(ARC),目前Xcode中默认使用ARC自动引用计数管理内存。

引用计数(Reference Count)

在OC编程中,对象一般是储存在堆中的,对象在堆内存中有一个地址P(指针),这样的对象又称为实例对象,OC对每个实例对象有一个引用计数,所谓引用也就是指针,也就是对指针的计数。

我们使用一个对象,一般都是使用一个指针储存该对象的地址,如NSObject *ptr = P,这个ptr储存指针值,它是在栈上的。指针计数也就是说,类似于在每个对象上都有一个count属性,用来统计指向该对象的指针数(也就是有多少人指向我?)

在OC中,如果一个对象的引用计数retainCount=0,则该对象会被释放。

Objective-C引用计数

如上图,A指针首先指向对象O,因此该对象的引用计数count=1;接着另一个指针B也指向O,则count=2;然后A不再指向O了,count=1,;当B也不再指向O的时候,count=0,此时该对象就会被释放了。

在OC编程中,引用计数的基本变化如下:

  • 当使用alloc、new、copy或mutableCopy,新得到的对象,其引用计数初始化为1。
  • 使用对象的retain方法,对象引用计数加1;使用release方法,对象引用计数减1。

要注意实例对象引用计数的变化,有可能会造成内存泄漏。

既然了解了OC的引用计数,那就不得不说一下OC中的属性特性了。

属性特性

目前OC中提供的主要属性特性有:weak、strong、assign、retain和copy。

理解这几个属性特性,主要从两方面考虑:是否开辟新的内存空间;是否对对象的引用计数有所增加。

  • 首先是copy,它是对原先对象A的一个拷贝得到B,对于A对象的引用计数是没有影响的,因为新copy的指针并不指向A,而是指向B,此时B的引用计数为1。
  • weak是对原先指针的一个简单拷贝,但是指向对象的引用计数不会增加,也就说,系统是否该对象,与weak指针无关。对象释放后,指针置为nil。不能修饰基本数据类型。
  • strong也是对原先指针的一个简单拷贝,指向对象的引用计数加1。
  • assign是对原先指针的拷贝,对象引用计数不会增加,可以修饰基本数据类型。但是对象释放后,指针不会置为nil,所以建议,除非是修饰基本数据类型,否则其它不使用assign。
  • retain对原先指针的拷贝,引用计数加1。

除了copy,其它四个关键字都是一一对应的,weak弱引用 => strong强引用,assign弱引用 => retain强引用。

ARC下修饰对象的默认关键字为strong,记得修饰基本数据类型使用assign就对了,然后就要关注引用计数的变化了。

另外在iOS开发中,有一个常见的问题:delegate为什么用weak?

例如controller中,该controller有一个tableview的强引用(tableview引用计数count=1),设置tableview的delegate为controller自身,那么tableview也有一个对controller的强引用(controller的引用计数count=1)。

释放controller引用计数为1,controller不能被释放,tableview也不能释放。

释放的原则是:最开始的类被释放后,会导致其成员有一些对象的引用计数为0,那么就可以逐步释放下去。假设这个叫做向前释放,那么如果后面有一个类使用强引用指向前面的类,那么会导致前面的类的retainCount > 0,而不能释放,造成内存泄漏。(后面的类可都看做前面的类的成员变量)

解决的办法就是,如果需要在后面的类指向前面的类,那么可以使用weak修饰该指针。也就说,在tableview中的delegate要使用weak修饰。

MRR手动内存管理

最新Xcode默认是使用ARC内存管理的,如果需要使用MRR,可以在PROJECT -> Build Settings中找到Objective-C Automatic Reference Couting,将其设置为NO。

MRR下,使用alloc、new、copy、mutableCopy,初始对象的引用计数为1,使用retainCount方法可以获取对象的引用计数,使用retain可以增加引用计数,使用release减少引用计数。

下面是一个MRR的实例:

#import <Foundation/Foundation.h>

@interface SampleClass:NSObject
- (void)sampleMethod;
@end

@implementation SampleClass
- (void)sampleMethod {
   NSLog(@"Hello, World! \n");
}

- (void)dealloc  {
  NSLog(@"释放对象");
  [super dealloc];
}

@end

int main() {
   // 初始引用计数为1
   SampleClass *sampleClass = [[SampleClass alloc]init];
   [sampleClass sampleMethod];
   
   NSLog(@"初始分配后引用计数: %d", 
   [sampleClass retainCount]);
   [sampleClass retain];
   
   NSLog(@"retian后引用计数: %d", [sampleClass retainCount]);
   [sampleClass release];
   NSLog(@"release后引用计数: %d", [sampleClass retainCount]);
   [sampleClass release];
   NSLog(@"在此之前将调用SampleClass dealloc");
   
   // 将指针设置为nil,防止野指针
   sampleClass = nil;
   return 0;
}

ARC内存管理

ARC内存管理是默认的方式,若无特殊情况,强烈建议使用ARC内存管理方式。

ARC内存管理是基于OC的NSObject对象提供的方法以及运行时,OC自动管理对象的引用计数的变化,而且,在ARC模式下,不能显式调用retain、release和dealloc等。

若要尽快释放对象,可将指针设置为nil,这还要看是否还有其它强引用指向该对象,如果没有则释放,否则需要等其它强引用释放才会释放该对象。

但是可以重写对象的dealloc方法,不过仍然不需要显式调用[super dealloc],重写该方法可以做一些对象成员的释放工作,例如释放C语言中的结构体。

另外@autoreleasepool,最新OC编程是不需要使用的。

下面是ARC的一个简单例子:

#import <Foundation/Foundation.h>

@interface SampleClass:NSObject
- (void)sampleMethod;
@end

@implementation SampleClass
- (void)sampleMethod {
   NSLog(@"Hello, World! \n");
}

- (void)dealloc  {
  NSLog(@"释放对象");
}

@end

int main() {
   SampleClass *sampleClass = [[SampleClass alloc]init];
   [sampleClass sampleMethod];
   sampleClass = nil; // 对象指针设置为nil,该对象的retainCount=0,即被自动释放
   return 0;
}
赞(0)
未经允许不得转载:srcmini » Objective-C内存管理详细解释 – Objective-C开发教程

评论 抢沙发

评论前必须登录!