网络知识 娱乐 IOS性能优化之内存管理与内存爆增(一)

IOS性能优化之内存管理与内存爆增(一)

        在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。 

        

********* 内容源于 <>*********