在IOS程序中,内存通常被分成如下5个区域
栈区:存储局部变量,在作用域结束后内存会被回收
堆区:存储Objective-C对象,需要开发者手动申请和释放
BSS区:用来存储未初始化的全局变量和静态变量
数据区:用来存储已经初始化的全局变量、静态变量和常量
代码段:加载代码
在上面的五个内存区域中,除了堆区需要开发者手动进行内存管理外,其它区都由系统自动进行回收。
一 MRC下的内存管理
MRC内存管理下的两条准则:
谁持有对象,谁负责对象,不是自己持有的不能释放
当对象不再需要时,需要主动释放。
不是自己创建的对象,如果自己对其使用了强引用,那么也需要对其负责进行释放。在Objective-C中,下边中的方法,都会对对象进行持有:
需要注意的是,前四个方法都是创建新的对象,让其引用计数为1,retain方法的作用是对当前对象进行持有,使其引用计数加1。
当我们使用[self.view addSubview:v]时,self.view会对v强引用一次,v的引用计数会+1.当self.view被释放时,v也会被释放一次,引用计数会-1。
当我们使用NSArray * array = @[obj]时,array会对obj强引用一次,obj的引用计数会+1,当array被释放时,obj也会被释放一次,引用计数会-1。
二 ARC下的内存管理
在ARC下,有几个修饰关键字非常重要,分别是:__strong、__weak、__unsafe_unretained、__autoreleasing.这些关键字在ARC中被称为所有权修饰符
1 关于__strong
实际上我们使用的指针默认都是__strong关键字修饰的,在ARC环境中,下面两行代码的意义完全一致:
__strong NSArray * v = [NSArray array];
NSArray * v2 = [NSArray array];
__strong修饰符通常用来对变量进行强引用,主要有以下三个作用:
使用__strong修饰的变量如果是自己生成的,则会被添加进自动释放池,在作用域结束后,会被release一次。
使用__strong修饰的变量如果不是自己生成的,则会被强引用,即会被持有使其引用计数+1,在离开作用域后会被release一次。
使用__strong修饰的变量指针如果重新赋值或者置为nil,则变量会被release一次。
2 关于__weak
__weak修饰符通常用来对变量进行弱引用,其最大的用途是避免ARC环境下的循环引用。__weak主要有以下两个作用:
被__weak修饰的变量仅提供弱引用,不会使其引用计数增加。变量对象如果是自己生成的,则会被添加到自动释放池,会在离开作用域时被release一次,如果不是自己生成的,则离开作用域后,不会进行release操作。
被__weak修饰的变量指针,变量如果失效,则指针会被自动置为nil,这时一种比较安全的设计方式,大量的减少了野指针造成的异常。
3 关于__unsafe_unretained
从名字可以看出,这个修饰符是不安全的,和上面__weak修饰符相比,这个修饰符的作用也是对对象进行弱引用,不同的是,当变量对象失效时,其指针不会被自动置为nil。
__unsafe_unretained作用如下:
被__unsafe_unretained修饰的变量仅提供弱引用,不会使其引用计数增加。变量对象如果是自己生成的,则会在离开作用域时被release一次,如果不是自己生成的,则在离开作用域后,不会进行release操作。
当变量失效后,被修饰指针不会被安全处理为nil,即旧地址依然保存。
4 关于__autoreleasing
__sutoreleasing修饰符常与自动释放池一起使用。
5 ARC环境下的几条原则
不能使用retain、release、autorelease函数
不能调用dealloc函数,可以覆写dealloc函数,但是十几种不可调用[super dealloc];
不能使用NSAutoreleasePool,但可以使用@autoreleasepool代替。
对象型变量不能作为C语言的结构体。
二 属性修饰符
内存管理相关属性修饰符列举如下表:
三 自动释放池
ios系统在运行应用程序时,会自动创建一些线程,每一个线程都默认拥有自动释放池。在每次执行时间循环结束之前,系统都会将其自动释放池清空。因此,大多数情况下,开发者都不必手动创建自动释放池,但有一种情况例外:
如果在大量的循环中生成自动释放对象,则可能会导致内存消耗瞬间暴涨,当所有循环结束后,内存又急剧下降。这样的场景下使用AutoreleasePool进行优化内存:
- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view.//使用AutoreleasePool[self useAutoreleasePool];
}
#pragma 使用AutoreleasePool
-(void)useAutoreleasePool
{int n = 100000;for (int i = 0; i < n; i++) {UIView * v = [[UIView alloc] init];v.backgroundColor = [UIColor blueColor];UIImage * image = [UIImage imageNamed:@"yyll_gift_tz"];NSLog(@"i = %d", i);}
}
下图为检测的cpu的使用情况:
可以看到Thread1占用的cpu内存爆增,内存的峰值达到76%.这样的场景适合使用AutoreleasePool,下面是优化后的代码:
- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view.//使用AutoreleasePool[self useAutoreleasePool];
}#pragma 使用AutoreleasePool
-(void)useAutoreleasePool
{int n = 100000;for (int i = 0; i < n; i++) {@autoreleasepool {UIView * v = [[UIView alloc] init];v.backgroundColor = [UIColor blueColor];UIImage * image = [UIImage imageNamed:@"yyll_gift_tz"];}NSLog(@"i = %d", i);}
}
优化后检测到的cpu的使用情况如下:
可以看到内存的峰值降低了近10%,最高峰值为68%,且运行的时间从1min 29sec降至52sec。
********* 内容源于 <<IOS性能优化实战>>*********