3~5年开发经历的 iOS工程师 应该知道的内容~本文总结以下内容

  • 视图烘托和离屏烘托
  • 事情传递和呼应链
  • crash处理和功能优化
  • 编译流程和发动流程

导航

iOS 进阶常识总结(一)

  • 目标
  • 类目标
  • 分类
  • runtime
  • 音讯与音讯转发

iOS 进阶常识总结(二)

  • KVO
  • KVC
  • 多线程
  • runloop
  • 计时器

iOS 进阶常识总结(三)

  • 视图烘托和离屏烘托
  • 事情传递和呼应链
  • crash处理和功能优化
  • 编译流程和发动流程

iOS 进阶常识总结(四)

  • 内存办理
  • 野指针处理
  • autoreleasePool
  • weak
  • 单例、通知、block、继承和集合

iOS 进阶常识总结(五)

  • 网络根底
  • AFNetWorking
  • SDWebImage

烘托

屏幕撕裂的原因?

  • 单一缓存形式下,帧缓冲区只要一个缓存空间
  • 图片需求经过CPU -> 内存 -> GPU -> 展现 的烘托进程
  • CPU和GPU的协作进程中呈现了偏差,GPU应该完整的制作图片,但是工作慢了只制作出图片上半部分。
  • 此时CPU又把新数据存储到缓冲区,GPU持续制作的时分下半部分就变成了新数据。
  • 形成了两帧一起呈现了一部分在屏幕上,看起来就撕裂了。

怎样处理屏幕撕裂?

  • 处理上一帧和下一帧的覆盖问题,需求运用不同的缓冲区,经过两个图形缓冲区的替换来处理。
  • 呈现速度差的时分,就把下一帧存储在后备缓冲区,制作完结后再切换帧。
  • 当制作完终究一个像素点就会发出这个笔直同步信号通知展现完结。
  • 所以屏幕撕裂问题需求经过 双缓冲区 + 笔直同步信号 处理。

掉帧是怎样产生的?

  • 屏幕正在展现A帧的时分,CPU和GPU会处理B帧。
  • A帧展现完结该切换展现B帧的时分B帧的数据未预备好。
  • 没办法切换就只能重复展现A帧,感官上便是卡了,这便是掉帧的问题

怎样处理掉帧?

  • 掉帧底子原因是CPU和GPU烘托核算耗时过长
  • 1、下降视图层级
  • 2、提前或削减在烘托期的核算

CPU烘托功能

  • 布局核算:假如视图层级过于杂乱,呈现或修正的时分需求耗费许多时刻核算
  • 视图懒加载:当显现的时分才会加载视图,这对内存运用和程序发动时刻很有优点。但展现前的操作都不会被及时呼应,当视图从xib加载或许涉及图片显现,懒加载都会比正常加载慢。
  • 解压图片PNG或许JPEG紧缩之后的图片会比位图小得多。在制作到屏幕之前,必须把它扩展成完整的尺寸(通常等同于图片宽 x 长 x 4个字节)。为了节省内存,真实制作的时分(榜首次赋值UIImageView或许把它制作到Core Graphics)才会解码图片,大图会占用必定的时刻。
  • Core Graphics制作:完成了drawRect:drawLayer:inContext:CALayerDelegate 的办法,在制作前都会产生一个巨大的功能开支以拓荒绘画上下文。
  • 图层打包:由于GPU不知道图层结构,CPU需求经过OpenGL把烘托树中的每个可见图层转换成纹路三角板。CPU的工作量和图层量成正比,假如层级联系太杂乱就会烘托速度。

GPU烘托功能

  • GPU会依据生成的前后帧缓存数据,依据实际情况进行组成
  • 形成GPU烘托担负:离屏烘托,图层混合,延迟加载

一个UIImageView添加到视图上今后,内部怎样烘托到手机上的?

  • 图片显现分为三个进程: 加载、解码、烘托
  • 通常,咱们程序员的操作只是加载,至于解码和烘托是由UIKit内部进行的。
  • 例如:UIImageView显现在屏幕上的时分需求赋值iamgeUIImage持有的数据是未解码的紧缩数据,赋值的时分图画数据会被解码变成RGB色彩数据,终究烘托到屏幕上。

说说烘托流程

  • 1、承认极点方位:CPU承认制作图形的方位,从iOS坐标系换算成屏幕坐标系。
  • 2、图元安装:承认极点间的连线联系。
  • 3、光栅化:便是把展现用到的像素点摘出来。
  • 4、着色器处理:GPU核算像素点展现的色彩,并存入缓冲区。
  • 5、屏幕展现

什么是离屏烘托

  • 普通烘托流程:APP – 帧缓冲区 – 展现
  • 离屏烘托流程:APP – 离屏烘托缓冲区 – 帧缓冲区 – 展现
  • 离屏烘托,是无法一次性处理烘托,需求分部处理并存储中心成果引起的。
  • 所以判别是否呈现离屏烘托的底子条件便是判别烘托是否需求分部处理~
    • 需求分步处理,会产生离屏烘托
    • 一次性烘托,不产生离屏烘托

离屏烘托的影响

  • 需求分几步就需求拓荒出几个离屏烘托缓冲区存储中心成果,形成空间浪费。
  • 终究合并多个离屏烘托缓冲区才干展现成果,会影响功能。

什么操作会触发离屏烘托?

  • 光栅化 layer.shouldRasterize = YES
  • 遮罩layer.mask
  • 圆角layer.maskToBounds = YesLayer.cornerRadis
  • 阴影layer.shadowXXX

视图

AutoLayout的原理,功能怎样

  • Auto Layout 只关注视图之间的联系,经过布局引擎和已有的束缚核算出各个视图的frame
  • 每逢束缚改动时会从头核算各个视图的frame
  • 取得frame的进程,便是依据各个视图已有的束缚条件解方程式的进程。
  • 功能会随着视图数量的添加呈指数级添加
  • 到达必定数量的视图时,布局所需求的时刻就会大于16.67ms,超过屏幕的改写频率时会呈现卡顿。

你认为主动布局怎样完成的

  • 原理是线性公式,运用了体系提供的NSLayoutConstraint
  • Masonry依据它封装

ViewController生命周期

  • initWithCoder:经过nib文件初始化时触发。
  • awakeFromNib:nib文件被加载的时分,会产生一个awakeFromNib的音讯到nib文件中的每个目标。
  • loadView:开端加载视图控制器自带的view
  • viewDidLoad:视图控制器的view被加载完结。
  • viewWillAppear:视图控制器的view即将显现在window上。
  • updateViewConstrains:视图控制器的view开端更新AutoLayout束缚。
  • viewWillLayoutSubviews:视图控制器的view即将更新内容视图的方位。
  • viewDidLayoutSubviews:视图控制器的view现已更新视图的方位。
  • viewDidAppear:视图控制器的view现已展现到window上。
  • viewWillDisappear:视图控制器的view即将从window上消失。
  • viewDidDisappear:视图控制器的view现已从window上消失。

LayoutSubviews调用时机

  • init 初始化不会调用 LayoutSubviews 办法
  • addsubView 时分会调用
  • 改动一个 viewframe 的时分调用
  • 翻滚 UIScrollView 导致 UIView 从头布局的时分会调用
  • 手动调用 setNeedsLayout 或许 layoutIfNeeded

setNeedsLayoutlayoutIfNeeded的差异

  • setNeedsLayout 符号为需求从头布局

    • 异步调用layoutIfNeeded改写布局,不当即改写,鄙人一轮runloop结束前改写。
    • 关于这一轮runloop之内的一切布局和UI更新只会改写一次,layoutSubviews必定会被调用。
  • layoutIfNeeded

    • 假如有需求改写的符号,当即调用layoutSubviews进行布局
    • 假如没有符号,不会调用layoutSubviews
    • 假如想在当时runloop中当即改写,调用次序应该是
    [self setNeedsLayout];
    [self layoutIfNeeded];
    

drawRect调用时机

  • drawRectloadViewViewDidLoad之后调用

UIViewCALayer是什么联系?

  • View能够呼应并处理用户事情,CALayer 不能够。
  • 每个 UIView 内部都有一个 CALayer 提供尺寸款式(模型树),进行制作和显现。
  • 两者都有树状层级结构,layer 内部有 subLayersview 内部有 subViews
  • CALayer是支持隐式动画的,View 作为Layer的署理,经过 actionForLayer:forKey:Layer提交相应的动画
  • layer 内部维护着三分 layer tree
    • 动画树presentLayer Tree,修正动画的特点改的是动画树的特点值
    • 模型树modeLayer Tree,终究展现在界面上的其实是提供视图的模型树
    • 烘托树render Tree

UIView显现原理

  • UIView 能够显现是由于内部有一个layer作为根图层,根图层上能够放其他子图层。
  • UIView 中一切能够看到的内容都包含在layer
  • UIView需求显现到屏幕上会调用drawRect:办法进行绘图,而且会将一切内容制作在自己的layer
  • 绘图结束后,体系将图层展现到屏幕上,完结了UIView的显现。

UIView显现进程

  • view.layer创立一个图层类型的上下文(Layer Graphics Contex
  • 触发署理办法drawLayer:inContext:,传入刚才预备好的上下文
  • drawLayer:inContext:内部会让view调用drawRect:办法绘图
  • 开发者在drawRect:办法中完成绘图代码,内容终究制作到view.layer
  • 体系将view.layer展现到屏幕,完结了view的显现

UITableView的重用机制?

  • UITableView 经过重用单元格来节省内存
  • 为每个单元格指定一个重用标识符,即指定了单元格的种类
  • 当屏幕上的单元格滑出屏幕时,体系会把这个单元格添加到重用队列中,等候被重用。
  • 当有新单元格从屏幕外滑入屏幕内时,从重用队列查找可重用的单元格,假如有就拿来用,假如没有就创立一个运用。

UITableView卡顿的的原因有哪些?

  • 隐式制作 CGContext
  • 制作 Core Graphics
  • 文本 CATextLayerUILabel
  • 截图 -renderInContext:
  • 可弹性图片
  • 混合图层
  • 目标回收
  • 离屏烘托
    • 光栅化 shouldRasterize
    • 阴影作用 shadowPath
    • 圆角裁切 cornerRadius
    • 遮罩

UITableVIew优化

  • 重用机制(缓存池)
  • 少用有透明度的View
  • 尽量防止运用xib
  • 尽量防止过多的层级结构
  • iOS8今后出的预估高度
  • 削减离屏烘托操作(圆角、阴影)
  • 缓存cell的高度(提前核算好cell的高度,缓存进当时的模型里边)
  • 异步制作
  • 滑动的时分,按需加载
  • 尽量少add、remove 子控件,最好经过hidden控制显现

imageNameimageWithContentsOfFile差异?

  • imageWithContentsOfFile:加载本地目录图片,不能加载image.xcassets里边的图片资源。不缓存占用内存小,相同的图片会被重复加载到内存中。不需求重复读取的时分运用。
  • imageNamed:能够读取image.xcassets的图片资源,加载到内存缓存起来,占用内存较大,相同的图片不会被重复加载。直到收到内存正告的时分才会开释不运用的UIImage。需求重复读取同一个图片的时分用。

IBOutlet连出来的视图特点为什么能够被设置成weak?

  • 由于Xcode内部把链接的控件放进了一个_topLevelObjectsToKeepAliveFromStoryboard的私有数组中,这个数组强引用这一切topLevel的目标,所以用weak也无伤大雅。

UIScrollerView完成原理

  • 翻滚其实是在修正原点坐标。当手指接触后,scrollview阻拦接触事情创立一个计时器。
  • 假如计时器到点后没有产生手指移动事情,scrollview 发送 tracking events 到被点击的 subview
  • 假如计时器到点前产生了移动事情, scrollview 撤销 tracking 自己翻滚。

怎样完成视图的变形?

  • 修正viewtransform

事情呼应链和事情传递

什么是呼应链

  • 由链接在一起的呼应者(UIResponse及其子类)组成的链式联系。
  • 最先的呼应者称为榜首呼应者
  • 最底层的呼应者是UIApplication

写出一个呼应链

subView -> view -> superView -> viewController -> window -> application

什么是事情传递

  • 触发事情后,事情从榜首呼应者到application的传递进程

事情传递的进程

  • 当程序中产生接触事情之后,体系会将事情添加到UIApplication办理的一个队列当中
  • application将使命队列的首个使命向下分发
  • application -> window -> viewController -> view
  • view需求满意条件才干够处理使命,透明度>0.01、接触在view的区域内、userInteractionEnabled=YEShidden=NO
  • 满意条件的view遍历本身的subViews,判别是否满意上述条件
  • 假如一切subView都无法满意条件,那么最佳呼应者便是自己。
  • 假如没有任何一个view能处理事情,事情会被抛弃。

找出接触的View

// 回来的View是本次点击的最佳呼应者
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
​
// 判别点是否落在某区域
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event

重写hitTest:withEvent

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
  // 1.判别当时控件能否接纳事情
  if (self.userInteractionEnabled == NO || 
    self.hidden == YES || 
    self.alpha <= 0.01) {
     return nil;
   }
  // 2. 判别点在不在当时控件
  if ([self pointInside:point withEvent:event] == NO) {
     return nil;
  }
  // 3.从后往前遍历自己的子控件
  NSInteger count = self.subviews.count;
  for (NSInteger i = count - 1; i >= 0; i--) {
    UIView *childView = self.subviews[I];
    // 把当时控件上的坐标系转换成子控件上的坐标系
    CGPoint childP = [self convertPoint:point toView:childView];
    UIView *fitView = [childView hitTest:childP withEvent:event];
    if (fitView) { // 寻找到最合适的view
      return fitView;
    }
  }
  // 循环结束 没有比自己更合适的view
  return self;
}

Crash

常见Crash的原因有哪些?

  • 1、找不到办法的完成 unrecognized selector sent to instance
  • 2、KVC形成的crash
  • 3、KVO形成的crash
  • 4、EXC_BAD_ACCESS
  • 5、集合类相关溃散,越界等
  • 6、多线程中的溃散,运用了被开释的目标
  • 7、后台回来错误的数据结构

BAD_ACCESS在什么情况下呈现?

  • 拜访现已开释目标的成员变量
  • 给现已开释目标发音讯
  • 死循环

不运用第三方,排查闪退问题?

  • 1、运用NSSetUncaughtExceptionHandler计算闪退的信息
  • 2、将计算到的信息发给后台
  • 3、在后台搜集信息,进行排查
    static void my_uncaught_exception_handler (NSException *exception) {
       //获取NSException 信息
       NSLog(@"***********************************************");
       NSLog(@"%@",exception);
       NSLog(@"%@",exception.callStackReturnAddresses);
       NSLog(@"%@",exception.callStackSymbols);
       NSLog(@"***********************************************");
    }
      
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
       NSSetUncaughtExceptionHandler(&my_uncaught_exception_handler);
       return YES;
    }
    

功能优化

优化发动时刻

  • 发动时刻是用户点击App图标,到榜首个界面展现的时刻。
  • 发动时刻在小于400ms是最佳的,由于从点击图标到显现Launch Screen,到Launch Screen消失这段时刻是400ms。
  • 发动时刻不能够大于20s,不然会被体系杀掉。

main函数作为分水岭,发动时刻其实包含了两部分:

mian函数之前的发动优化

  • 削减或合并动态库(这是目前为止最耗时的了, 基本上占了95%以上的时刻)
  • 承认动态库是optional or required。假如该Framework在支持的一切iOS体系版本都存在,那么就设为required,不然就设为optional,由于optional会有些额定的查看

mian函数之后的发动优化

  • 1、合并和删减不必要的类或许分类
  • 2、将不必需在+load办法中做的事情,延时放到+initialize
  • 3、非必要的 SDK 和装备事情能够放在榜首个界面处理
  • 4、削减创立线程,线程创立和运转需求多次的上下文切换所带来的开支,均匀耗费大约在 29 毫秒。这是很大的时刻开支,应该防止乱用。
  • 5、编译器插桩获取办法符号,生成order file设置到xcode。削减页中断带来的耗时。

网络优化

  • IP直连,将咱们原本的恳求链接www.baidu.com 修正为 180.149.132.47
  • 运营商在拿到恳求后发现是IP地址会直接放行,而不会去走DNS解析
  • 不走他的DNS解析也就不会存在DNS被劫持的问题
  • 完成办法1:接运用HTTPDNS等sdk
  • 完成办法2:服务端下发发域名-IP对应列表,客户端缓存,经过缓存IP来进行事务拜访。

包体积优化

  • 1、删去陈腐代码、删去陈腐xib/sb ,删去无用的资源文件(检测未运用图片的工具LSUnusedResources
  • 2、图片、音视频资源紧缩后运用。
  • 3、动图能够运用webP格式,加载速度比较慢,但体积小
  • 4、能用动态库的尽量用动态库,一般情况静态库的体积会比动态库大
  • 5、主题类的资源文件提供按需下载功能,不直接打包在运用包里边
  • 6、App Slicing,运用程序切片,只打包下载机型所需的资源文件,不需求开发者处理
  • 7、Bitcode,中心代码, 苹果会对可执行文件进行优化,不需求开发者处理
  • 8、ODR,On Demand Resources,按需加载资源,需求开发者处理

电量优化

  • 1.定位,尽量不要实时更新,能够恰当下降精度
  • 2.网络恳求,能用缓存的时分尽量运用缓存,下降恳求的频率,削减恳求的次数,优化传输结构
  • 3.CPU处理,需求复用的数据能缓存的数据尽量缓存,优化算法和数据结构
  • 4.GPU处理,削减离屏烘托

短视频/直播优化

  • 多播映器,像tableview那样维护缓存池。多播映器,才干做预加载。
  • 边下边播,要完成下一个视频或许几个视频能快速的播映起来,首先应该保证正在播映的短视频能顺畅播映,边下边播使命优先级应该高于预加载使命。没有边下边播时才干执行预加载,边下边播使命进行时应当中止预加载。
  • 预加载,预加载的下载应该和边下边播区分开,手势滑动到差不多呈现的时分,开端播映预加载的那1m数据。滑动下一个的时分,就撤销preload,播映那1m下载的数据,再持续load。一起调起下一个preload预备下载。

短视频缓存办理

  • 缓存首要创立了三个目录办理,分别为temp、media、trash目录
  • 缓存分为暂时缓存和终究缓存,当短视频资源未下载完时是放在temp、当视频资源缓存完时移动到media,这样分别存放便能方便读取和办理两种状态的缓存,
  • 一切需求删去的缓存文件都移入trash,随后再删去以此来保证较高的删去效率。一切文件命名运用视频url的md5值保证唯一性。
  • 缓存应该具有主动办理功能,默认装备下ShortMediaCache答应暂时缓存最多保存1天,最大100Mb,而终究缓存则答应最多保存2天最大200Mb,假如事务需求能够自定义ShortMediaCacheConfig装备完成。

一般是怎样用Instruments的?

  • Instruments里边工具许多,常用:
  • Time Profiler: 功能剖析
  • Zombies:查看是否拜访了僵尸目标,但是这个工具只能从上往下查看,不智能。
  • Allocations:用来查看内存。
  • Leaks:查看内存,看是否有内存泄露。

编译 & 发动

编译链接流程

  • 0、输入文件:找到源文件
  • 1、预处理:包含替换宏和导入头文件
  • 2、编译阶段:词法剖析、语法剖析、语义剖析,终究生成IR
    • 2-1、预处理后会进行词法剖析,词法剖析会把代码切片成一个个token
    • 2-2、语法剖析会验证语法是否正确,在词法剖析的根底上把单词组组成各种语法短语,然后把节点组成抽象的语法树
    • 2-3、代码生成器依据语法树自顶向下生成LLVM IROC会在这儿进行runtime的桥接:property的组成、ARC处理等
  • 3、编译器后端:经过一个个Pass去优化,每个Pass完结一部分功能,终究生成汇编代码
    • 3-1、苹果对代码做了进一步优化,而且经过.ll文件生成.bc文件。
    • 3-2、能够经过.bc.ll文件生成汇编代码
  • 4、生成,.o格式的目标文件
  • 5、链接动态库和静态库
  • 6、经过不同架构,生成对应的可执行文件MachO

APP发动进程

  • 1、加载可执行文件(读取Mach-O
  • 2、加载动态库(Dylib
  • 3、Rebase & Bind
    • 3-1、Rebase的作用是修正ASLR的偏移,把当时MachO的指针指向正确的内存
    • 3-2、Bind的作用是从头修正外部办法指针的指向,fishhook原理
  • 4、objc_init,加载类和分类
  • 5、Initializers,调用load办法,初始化C & C++的目标等
  • 6、main()函数
  • 7、执行AppDelegate的署理办法(如:didFinishLaunchingWithOptions)。
  • 8、初始化Windows,初始化ViewController

dyld做了什么

  • 1、dyld读取Mach-OHeaderLoad Commands
  • 2、找可执行文件的依靠动态库,将依靠的动态库加载到内存中。这是一个递归的进程,依靠的动态库可能还会依靠别的动态库,所以dyld会递归每个动态库,直至一切的依靠库都被加载结束。