2018-06-29 学习iOS性能分析和优化

本文章参考自iOS性能分析和优化,主要学习如何使用性能分析工具Instruments。

一.基本概念

1.内存空间的划分

一个进程占用的内存空间,包含5种不同的数据区:
(1) BSS段:通常存放未初始化的全局变量
(2)数据段:通常存放已初始化的全局变量
(3)代码段:通常存放程序执行代码
(4)堆:通常存放进程运行中被动态分配的内存段,OC对象(所有继承自NSObject的对象)就存放在堆里。
(5)栈:由编译器自动分配释放,存放函数的参数值,局部变量等值。

其中,栈内存是由系统来管理的,因此我们常说的内存管理,是指堆内存的管理,也就是所有OC对象的创建和销毁的管理。随着苹果推出了ARC,编译器会自动生成内存管理的代码,因此我们的内存泄漏通常来源于堆内存。

2.内存泄漏(Memory Leak)

用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元,即该内存空间使用完毕之后未回收。在ios的内存泄漏原因一般有循环引用、错用Strong/copy等。

二.Instruments多功能检测

1.静态分析(Analyze)

不需要运行程序,就可以检查到存在内存泄漏的地方。



常见的三种泄露情形:

  1. 创建了一个对象但是并没有使用,提示:Value Stored to 'number' is never read
  2. 创建了一个(指针可变的)对象,且初始化了,但是初始化的值一直没读取过,提示:Value Stored to 'str' during its initialization is never read
  3. 调用了让某个对象引用计数加一的函数,但没有调用相应的让其引用计数减一的函数,提示:Potential leak of an object stored into 'subImageRef'

通过测试可以发现,程序中有这样的泄露:



原来是因为这个指针初始化之后没有使用,其实在这里如果要赋值的话,直接变成这样:

NSDictionary *cover = self.covers[indexPath.item];

2.内存泄漏检查工具——Leak

参考Xcode结合Leaks检测内存泄露



中途有点报错,关闭SIP就好了


哇,吓我一跳,这么多内存泄漏的吗..赶紧参考了AFNetworking 3.0中调用[AFHTTPSessionManager manager]方法导致内存泄漏的解决办法,发现问题在于,由于ARC的机制,导致每当实例化Session类后都没有地方释放掉实例,需要把Session类的实例都改成单例模式。


(附)科普:单例模式

参考iOS-单例模式简单使用

1.概念

一个类只允许有一个实例,在整个程序中需要多次使用,共享同一份资源的时候,就可以创建单例,一般封装成工具类使用,如UIApplication,NSUserDefaults,NSNotificationCenter,NSFileManager等。

2.单例优缺点

优点:单例模式会使类只有一个实例,所以方便使用,并且节省内存资源的分配。因为使用GCD的方式是线程安全的,所以会避免资源的多重使用。

缺点:单例创建的内存只有在程序结束的时候才会被释放。由于单例不能被继承(因为返回的是同一个实例),所以扩展性很不好。


由此,我们首先建立一个AFSessionSingleton文件,里面包含的是单例模式的声明。

#import "AFSessionSingleton.h"

@implementation AFSessionSingleton

static AFHTTPSessionManager *manager;

+ (AFHTTPSessionManager *) sharedHttpSessionManager
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [AFHTTPSessionManager manager];
        manager.requestSerializer.timeoutInterval = 10.0;
    });
    return manager;
}

@end

在调用的时候变成这一句:

//    AFHTTPSessionManager *session = [AFHTTPSessionManager manager];
AFHTTPSessionManager *session = [AFSessionSingleton sharedHttpSessionManager];

就可以用单例模式避免内存泄漏了!

其他问题:还有一个Leak,但是通过debug发现没什么事,就不管了。


3.自己制造一个Leak出来

参考iOS开发中本人或同事碰到的内存泄漏及解决办法

循环引用
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    NSMutableArray *arr1 = [NSMutableArray array];
    NSMutableArray *arr2 = [NSMutableArray array];
    
    [arr1 addObject: arr2];
    [arr2 addObject: arr1];
}

他说的那种方法还是会导致内存泄漏啊..以后还是尽量避免循环引用吧。

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    NSMutableArray *arr1 = [NSMutableArray array];
    NSMutableArray *arr2 = [NSMutableArray array];
    
    __weak typeof (arr1) weakArr1 = arr1;
    
    [weakArr1 addObject: arr2];
    [arr2 addObject: weakArr1];
}

4.Allocations——内存分配

用于检测程序运行过程中的内存分配情况。

5.Time Profiler

分析代码的执行时间,以便找到导致程序变慢的原因。

当点击Time Profiler应用程序开始运行后.就能获取到整个应用程序运行消耗时间分布和百分比.为了保证数据分析在统一使用场景真实行有如下点需要注意:

在开始进行应用程序性能分析前,请一定要使用真机,因为模拟器运行在Mac上,然而Mac上的CPU往往比iOS设备要快。相反,Mac上的GPU和iOS设备的完全不一样,模拟器不得已要在软件层面(CPU)模拟设备的GPU,这意味着GPU相关的操作在模拟器上运行的更慢,尤其是使用CAEAGLLayer来写一些OpenGL的代码时候。这就导致模拟器性能数据和用户真机使用性能数据相去甚远。

可以看到,加载视频是最花时间的,然后就是加载瀑布流图片。

三.一些总结

1.以下情况会增加APP的内存占用

  1. 创建对象,定义变量。
  2. 调用一个函数或方法。

2.以下情况会增加CPU的消耗

  1. 创建对象、调整对象属性、销毁对象。
  2. 布局计算和AutoLayout。
  3. 文本的计算和渲染。
  4. 图片的解码和绘制。

3.如何避免内存泄漏

  1. 做好cell等可复用对象的重用
  2. 可以只创建一次的对象,不要创建多次(如页面的某个功能弹窗)
  3. 用较少的对象和方法调用去实现功能
  4. 将耗时的操作放在子线程

4.一些关于ViewController的内存泄漏

参考iOS学习——内存泄漏检查及原因分析

在目前主要以ARC进行内存管理的开发模式,导致内存泄漏的根本原因是代码中存在循环引用,从而导致一些内存无法释放,这就会导致dealloc()方法无法被调用。主要原因大概有一下几种类型。

1) ViewController中存在NSTimer
NSTimer scheduledTimerWithTimeInterval:1.0 
                                 target:self 
                               selector:@selector(updateTime:) 
                               userInfo:nil 
                                repeats:YES];

如果你的ViewController中有NSTimer,那么你就要注意了,因为当你调用target:self时,就会增加ViewControllerreturn count,如果不将这个Timer invalidate,将不会调用dealloc

2) ViewController中的代理delegate

一个比较隐秘的因素,要找一下这个类有关的代理,看看有没有强引用属性,如果这个View Controller需要外部传入某个Delegate进来,来通过Delegate+protocol的方式传参数给其他对象,那么这个delegate一定不要强引用,尽量assign或者weak,否则整个View Controller会持续持有这个delegate,直到自身被释放。

3) ViewController中的Block

这个问题会比较容易犯,Block体内使用实例变量也会造成循环引用,使得拥有这个实例的对象不能释放。因为该Block本来就是当前View Controller的一部分,现在Block又强引用self,导致循环引用无法释放。

例如:有一个类叫OneViewController,有个属性是NSString *name,如果在block体内使用了self.name或者_name,这个类就无法释放了。

解决方法就是在Block之前声明当前的self为弱引用。

__weak ViewController *weasSelf = self;
4) ViewController的子视图对self的持有

我们有时候需要在自视图或者某个cell中点击跳转等操作,需要在自视图或者cell中持有当前的ViewController对象,这样跳转之后的back键才能直接返回该页面,同时不销毁当前的ViewController。此时,就需要注意在子视图或者cell中对当前页面的持有对象不能是强引用,尽量用assign或者weak,否则会造成循环引用,导致内存无法释放。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容

  • 1.ios高性能编程 (1).内层 最小的内层平均值和峰值(2).耗电量 高效的算法和数据结构(3).初始化时...
    欧辰_OSR阅读 29,027评论 8 265
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,036评论 1 32
  • 内存管理 简述OC中内存管理机制。与retain配对使用的方法是dealloc还是release,为什么?需要与a...
    丶逐渐阅读 1,867评论 1 16
  • 前情回顾:渣男日记(十一) “你们知道吗?世界上百分之八十的财富,被百分之二十的人占有,同样的,百分之八十的女人啊...
    听任蔓草湮路阅读 798评论 4 2
  • 最近考虑我司品牌定位的一些事情,看到友人朋友圈的书单,阅读了此书。全书通过通俗的语言和生动的案例,从品牌角度分析了...
    LukeYU阅读 229评论 0 1