作品分享
问答交流
发现
任务
客服工单
一. GCD和OperationQueue 二. CADisplayLink、NSTimer利用注意 三. 内存布局 四. Tagged Pointer 五. copy和mutableCopy 六. OC对象的内存管理 七. AutoreleasePool主动开释池 八. 图片的解压缩到渲染过程 九. 应用卡顿的缘故起因以及优化 十. APP的启动
GCD 可用于多核的并行运算; GCD 会主动利用更多的 CPU 内核(比如双核、四核); GCD 会主动管理线程的生命周期(创建线程、调理任务、烧毁线程); 步调员只必要告诉 GCD 想要实行什么任务,不必要编写任何线程管理代码。
NSOperation、NSOperationQueue 是基于 GCD 更高一层的封装,完全面向对象。但是比 GCD 更简单易用、代码可读性也更高。 可添加完成的代码块,在操纵完成后实行。 添加操纵之间的依靠关系,设定操纵实行的优先级,方便的控制实行次序;设置最大并发数。 可以很方便的取消一个操纵的实行。 利用 KVO 观察对操纵实行状态的更改:isExecuteing、isFinished、isCancelled。
同步和异步紧张影响:能不能开启新的线程 同步:在当前线程中实行任务,不具备开启新线程的本领 异步:在新的线程中实行任务,具备开启新线程的本领
并发和串行紧张影响:任务的实行方式 并发:多个任务并发(同时)实行 串行:一个任务实行完毕后,再实行下一个任务
CADisplayLink、NSTimer会对target产生强引用,如果target又对它们产生强引用,那么就会引发循环引用。办理办法是利用署理对象NSProxy。 NSTimer依靠于RunLoop,如果RunLoop的任务过于繁重,大概会导致NSTimer禁绝时。而GCD的定时器会更加准时。
栈区(heap):由体系去管理。地点从高到低分配。先辈后出。会存一些局部变量,函数跳转跳转时现场掩护(寄存器值生存于规复),这些体系都会帮我们主动实现,无需我们干预。以是大量的局部变量,深递归,函数循环调用都大概耗尽栈内存而造成步调瓦解 。 堆区(stack):必要我们本身管理内存,alloc申请内存release开释内存。创建的对象也都放在这里。 地点是从低到高分配。堆是全部步调共享的内存,当N个这样的内存得不到开释,堆区会被挤爆,步调立马瘫痪。这就是内存泄漏。 全局区/静态区(staic):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块地区, 未初始化的全局变量和未初始化的静态变量在相邻的另一块地区。步调竣事后有体系开释。 常量区:常量字符串就是放在这里的,尚有const常量。 代码区:存放App代码,App步调会拷贝到这里。
从64bit开始,iOS引入了Tagged Pointer技能,用于优化NSNumber、NSDate、NSString等小对象的存储 在没有利用Tagged Pointer之前, NSNumber等对象必要动态分配内存、维护引用计数等,NSNumber指针存储的是堆中NSNumber对象的地点值 利用Tagged Pointer之后,NSNumber指针里面存储的数据酿成了:Tag + Data,也就是将数据直接存储在了指针中 当指针不够存储数据时,才会利用动态分配内存的方式来存储数据 objc_msgSend能辨认Tagged Pointer,比如NSNumber的intValue方法,直接从指针提取数据,节省了从前的调用开销 怎样判断一个指针是否为Tagged Pointer? iOS平台,最高有用位是1(第64bit);Mac平台,最低有用位是1
在iOS中,利用引用计数来管理OC对象的内存。 一个新创建的OC对象引用计数默认是1,当引用计数减为0,OC对象就会烧毁,开释其占用的内存空间。 调用retain会让OC对象的引用计数+1,调用release会让OC对象的引用计数-1。 当调用alloc、new、copy、mutableCopy方法返回了一个对象,在不必要这个对象时,要调用release大概autorelease来开释它;想拥有某个对象,就让它的引用计数+1;不想再拥有某个对象,就让它的引用计数-1。
AutoreleasePool(主动开释池) 是OC中的一种内存主动采取机制,在开释池中的调用了autorelease方法的对象都会被压在该池的顶部(以栈的情势管理对象)。当主动开释池被烧毁的时间,在该池中的对象会主动调用release方法来开释资源,烧毁对象。以此来达到主动管理内存的目标。
__AtAutoreleasePool 实际是一个布局体,在内部起首实行objc_autoreleasePoolPush(),然后在调用objc_autoreleasePoolPop(atautoreleasepoolobj)。
AutoreleasePoolPage的布局 每个AutoreleasePoolPage对象占用4096字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放autorelease对象的地点。 全部的AutoreleasePoolPage对象通过双向链表的情势毗连在一起。
调用push方法会将一个POOL_BOUNDARY入栈,而且返回其存放的内存地点 调用pop方法时传入一个POOL_BOUNDARY的内存地点,会从末了一个入栈的对象开始发送release消息,直到遇到这个POOL_BOUNDARY id *next指向了下一个能存放autorelease对象地点的地区
Autorelease何时开释? 1、手动调用AutoreleasePool的开释方法(drain方法) 2、Autorelease对象是在当前的runloop迭代竣事时开释的,而它可以或许开释的缘故起因是体系在每个runloop迭代中都参加了主动开释池Push和Pop
Runloop和Autorelease的关系 App启动后,苹果在主线程 RunLoop 里注册了两个 Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()。 第一个 Observer 监督的事故是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建主动开释池。其 order 是 -2147483647,优先级最高,包管创建开释池发生在其他全部回调之前。 第二个 Observer 监督了两个事故: BeforeWaiting(预备进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 开释旧的池并创建新池;Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来开释主动开释池。这个 Observer 的 order 是 2147483647,优先级最低,包管其开释池子发生在其他全部回调之后。
假设我们利用 +imageWithContentsOfFile: 方法从磁盘中加载一张图片,这个时间的图片并没有解压缩; 然后将天生的 UIImage 赋值给 UIImageView ; 接着一个隐式的 CATransaction 捕获到了 UIImageView 图层树的厘革; 在主线程的下一个 runloop 到来时,Core Animation 提交了这个隐式的 transaction ,这个过程大概会对图片举行 copy 操纵,而受图片是否字节对齐等因素的影响,这个 copy 操纵大概会涉及以下部门或全部步调: 分配内存缓冲区用于管理文件 IO 息争压缩操纵; 将文件数据从磁盘读到内存中; 将压缩的图片数据解码成未压缩的位图情势,这是一个非常耗时的 CPU 操纵; 末了 Core Animation 中CALayer利用未压缩的位图数据渲染 UIImageView 的图层。 CPU盘算好图片的Frame,对图片解压之后.就会交给GPU来做图片渲染 渲染流程 GPU获取获取图片的坐标 将坐标交给极点着色器(极点盘算) 将图片光栅化(获取图片对应屏幕上的像素点) 片元着色器盘算(盘算每个像素点的终极表现的颜色值) 从帧缓存区中渲染到屏幕上
分配内存缓冲区用于管理文件 IO 息争压缩操纵; 将文件数据从磁盘读到内存中; 将压缩的图片数据解码成未压缩的位图情势,这是一个非常耗时的 CPU 操纵; 末了 Core Animation 中CALayer利用未压缩的位图数据渲染 UIImageView 的图层。 CPU盘算好图片的Frame,对图片解压之后.就会交给GPU来做图片渲染
GPU获取获取图片的坐标 将坐标交给极点着色器(极点盘算) 将图片光栅化(获取图片对应屏幕上的像素点) 片元着色器盘算(盘算每个像素点的终极表现的颜色值) 从帧缓存区中渲染到屏幕上
总结:图片渲染到屏幕的过程: 读取文件->盘算Frame->图片解码->解码后纹理图片位图数据通过数据总线交给GPU->GPU获取图片Frame->极点变动盘算->光栅化->根据纹理坐标获取每个像素点的颜色值(如果出现透明值必要将每个像素点的颜色*透明度值)->渲染到帧缓存区->渲染到屏幕
CPU: 盘算视图frame,文本盘算和排版,图片解码,必要绘制纹理图片通过数据总线交给GPU。 GPU: 纹理混淆,极点变动与盘算,像素点的添补盘算,渲染到帧缓冲区。 平常所说的“卡顿”紧张是由于在主线程实行了比力耗时的操纵, 可以添加Observer到主线程RunLoop中,通过监听RunLoop状态切换的耗时,以达到监控卡顿的目标。
在 VSync 信号到来后,体系图形服务会通过 CADisplayLink 等机制关照 App,App 主线程开始在 CPU 中盘算表现内容,比如视图的创建、布局盘算、图片解码、文本绘制等。随后 CPU 会将盘算好的内容提交到 GPU 去,由 GPU 举行变动、合成、渲染。随后 GPU 会把渲染效果提交到帧缓冲区去,期待下一次 VSync 信号到来时表现到屏幕上。由于垂直同步的机制,如果在一个 VSync 时间内,CPU 大概 GPU 没有完成内容提交,则那一帧就会被丢弃,期待下一次时机再表现,而这时表现屏会保留之前的内容稳定。这就是界面卡顿的缘故起因。 从上面的图中可以看到,CPU 和 GPU 岂论哪个拦阻了表现流程,都会造成掉帧征象。以是开辟时,也必要分别对 CPU 和 GPU 压力举行评估和优化。
CPU 只管用轻量级的对象,比如用不到事故处置处罚的地方,可以思量利用CALayer取代UIView 不要频仍地调用UIView的干系属性,比如frame、bounds、transform等属性,只管淘汰不须要的修改 只管提前盘算好布局,在有必要时一次性调解对应的属性,不要多次修改属性 Autolayout会比直接设置frame斲丧更多的CPU资源 图片的size最好刚好跟UIImageView的size保持同等 控制一下线程的最大并发数目 只管把耗时的操纵放到子线程:文本处置处罚(尺寸盘算、绘制)、图片处置处罚(解码、绘制)等
GPU 只管制止短时间内大量图片的表现,尽大概将多张图片合成一张举行表现 GPU能处置处罚的最大纹理尺寸是4096x4096,一旦高出这个尺寸,就会占用CPU资源举行处置处罚,以是纹理只管不要高出这个尺寸 只管淘汰视图数目和条理 淘汰透明的视图(alpha<1),不透明的就设置opaque为YES 只管制止出现 离屏渲染
APP的冷启动可以概括为3大阶段:dyld、runtime、main
dyld(dynamic link editor),Apple的动态链接器,可以用来装载Mach-O文件(可实行文件、动态库等)。 启动APP时,dyld所做的事变有: 装载APP的可实行文件,同时会递归加载全部依靠的动态库. 当dyld把可实行文件、动态库都装载完毕后,会关照Runtime举行下一步的处置处罚.
启动APP时,runtime所做的事变有: 调用map_images举行可实行文件内容的解析和处置处罚 在load_images中调用call_load_methods,调用全部Class和Category的+load方法 举行各种objc布局的初始化(注册Objc类 、初始化类对象等等) 调用C++静态初始化器和attribute((constructor))修饰的函数
接下来就是UIApplicationMain函数,AppDelegate的application:didFinishLaunchingWithOptions:方法
Swift从入门到精通 每周一道算法题 恋上数据布局与算法(一) 恋上数据布局与算法(二)
如果必要跟我交流的话: ※ 简书:https://www.jianshu.com/u/e15d1f644bea
举报
Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )
GMT+8, 2024-11-22 04:21, Processed in 0.191532 second(s), 36 queries.© 2003-2025 cbk Team.