iOS 性能优化之内存管理

对于App的性能衡量标准可以有很多, 我们常见的几大指标包括了:

  • 内存
  • 电量消耗
  • 初始化时间
  • 执行速度
  • 响应速度
  • 网络环境
  • 数据安全
  • App的稳定性
  • 其他

这许多性能指标可以说是常见App都需要覆盖的, 除此之外还会有一些根据业务需要而引申出的很多特性, 比如本地数据库的读写, 多媒体数据的处理等等等等, 不一而足.

在这些性能指标中, 内存管理是我们十分常见也是十分重要的一环, 关于内存管理, 这里记录下自己的学习经验.

有关内存的分配:

系统分配的每个线程都会为其专门分配一定的内存空间, 我们称之为栈空间, 而每个方法都拥有自己的栈帧, 并会消耗掉整体的栈空间, 所以当我们递归调用方法的时候, 方法会持续消耗栈空间, 导致内存飙升, 直到方法结束后逐步下降.
而每个进程中, 所有的栈都共享同一个堆空间, 而堆空间的大小是由系统自动分配的, 应用无法控制. 使用NSString 图片 或者创建使用json/xml数据都会占用大量的堆空间. 而当一个对象是在堆中被创建的, 那么他所引用的属性也有可能会被从栈中复制到堆中. 当他的某个属性值在方法内部使用使, 则有可能会被从堆中复制到栈中, 比如基本数据类型的引用, 而对象类型属性的引用因为是指针操作, 则不会做复制行为.

使用自动释放池autoreleasepool:

自动释放池相当于手动创建一个内存管理域, 一般arc的内存管理是在一个loop内执行的, 当一个事件循环结束后, 系统会进行内存的释放. 具体可以在一个方法的作用域结束时. 现在我们在一个方法内进行循环, 并创建变量. 虽然我们知道, 当方法结束时, 因为每次循环被创建的变量没有引用者, 这些变量会被释放, 但是在循环进行时, 这些变量会一直堆积在内存中, 导致内存峰值上升, 使用自动释放池包裹变量的每次创建, 那么他们就会在自动释放池作用域结束时被释放, 因为不会导致内存堆积, 从而降低内存峰值

变量限定符:

对象默认的变量限定符是: __strong, 我们经常在block中使用被__weak变量限定符修饰的对象

__weak typeof(self) weak_self = self;

__autoreleasing用于引用使用id*传递消息参数, 我们常见的使用NSError **error作为参数的时候, 就会用到__autoreleasing变量限定符

- (void)someMethod:(NSError * __autoreleasing *)error;
使用僵尸对象检测:

僵尸对象检测是用来捕捉内存错误的, 即野指针问题.
当对象本身被释放了, 而对象指针没有释放, 就会造成野指针错误. 开启僵尸对象检测, 对象在释放时不会被真的释放, 而是会被标记为僵尸. 当僵尸被调用时, 你就可以确定对象在代码中被调用的位置了.

找到对象的持有者:

内存泄漏的一大原因就是循环引用, 由于多重引用而导致对象在内存中的释放失败, 此时如果反复创建对象则会造成大量的内存负荷. 这时候找到对象的持有者是正向解决问题的关键.
由于ARC禁止使用retain等函数, 我们没办法重写或者调用他们, 所以我们在调试的时候可以禁用ARC



然后重写需要检查持有者的对象的retain方法

#if !__has_feature(objc_arc)
- (id)retain {
    
    NSLog(@"%s, %@", __PRETTY_FUNCTION__, [NSThread callStackSymbols]);
    return [super retain];
}

#endif

这样可以打印出对象retain时调用的堆栈顺序, 找到他的持有者.


记录App运行中的内存使用情况:

我们可以在App运行时, 通过定时器来时刻查询当前内存的使用状况, 当发现内存占用超过一定值时, 就记录下当前的堆栈调用, 内存使用情况, 以便后续上报给后台监测, 一下是获取当前占用的内存和可以使用的内存的方法.

vm_size_t getUserMemory() {
    
    task_basic_info_data_t info;
    mach_msg_type_number_t size = sizeof(info);
    kern_return_t kerr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t) &info, &size);
    if (kerr == KERN_SUCCESS) {
        return info.resident_size;
    } else {
        return 0;
    }
}

vm_size_t getFreeMemory() {
    
    mach_port_t host = mach_host_self();
    mach_msg_type_number_t size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t pagesize;
    vm_statistics_data_t vmstat;
    
    host_page_size(host, &pagesize);
    kern_return_t kerr = host_statistics(host, HOST_VM_INFO, (host_info_t) &vmstat, &size);
    if (kerr == KERN_SUCCESS) {
        
        return vmstat.free_count * pagesize;
    } else {
        
        return 0;
    }
}

你可能感兴趣的:(iOS 性能优化之内存管理)