点击上方蓝字,记得关注我们!
总有面邀在手,一面试就没下文?
莫急莫急,资深大牛告诉你,问题原来在这里!
▼▼▼知己知彼,赶快对号入座,先看下自己的技术段位吧~
高级IOS
- 解决研发过程中的关键问题和技术难题
- 调优设备流量、性能、电量
- 较强的软件设计能力
- 对iOS内部原理有深刻理解
- [/code]
- [/list]资深IOS
- [list][*]精通高性能编程以及性能调优
- [*]灵活运用数据结构、算法解决复杂程序设计问题
- [*]提供性能优化、日志搜索、统计分析等方案
- [*]架构、模块设计
- [/list]
- [code]
复制代码 简历
- 排版清晰
- 重要和突出的表达
- 线上bug数变化、量化指标 质量上的指标
- 开发成本节约了多少 量化指标
- 基本信息、工作经历、项目经验、擅长技能
- 项目经验(主导、参与),背景、方案、效果(START 法则)
技术问题要点
面答问题目录
- 1. UI视图
- 2. UITableView
- 3. 视图显示原理
- 4. OC语言面试问题
- 5. RunTime
- 6. 类对象,对象(实例,isa指向类对象),
- 7. 内存管理
- 8. Block
- 9. 多线程
- 10. RunLoop
- 11. 网络
- 12. 设计模式
- 13. 架构&框架
- 14. 算法
- 15. 第三方库
1.UI 视图- UITableView
- 事件传递&视图响应
- 图像显示原理
- 卡顿&掉帧
- 绘制原理&异步绘制
- 离屏渲染
2.UITableview- 重用机制
- 数据源同步
- UI事件传递&响应
- UIView提供内容,事件响应,响应链
- CALayar的contents负责绘制
- 单一指责原则
3.视图显示原理
3-1、卡顿、掉帧的原因
- CPU
- 对象创建、调整、销毁
- 预排版(布局计算、文本计算)
- 预渲染(文本等异步绘制,图片解码等)
- GPU
3-2、UIView的绘制原理
- [UIView setNeedsDisplay] -> [view.layer setNeedsDisplay] -> [CALayer display]
- [CALayer display]是否响应 [layer.delegate displayLayer]方法
- 是,进入异步绘制。否,进入系统绘制流程
- CALayer creats backing store(CGContextRef)
- 是否有layer has a delegate
- 是,[layer.delegate drawLayer:inContext:]
- 否,[CALayer drawInContext:]
- 最后 CALayer uploads backing store to GPU
3-3、异步绘制 [layer.delegate displayLayer]- - 代理负责生成对应的bitmap- 设置该bitmap作为layer.contents属性的值- 流程如下
复制代码 1234567- [AsyncDrawingView setNeedsDisplay]- [CALayer display]- [AsyncDrawingView displayLayer:] - CGBitmapContextCreat() - CoreGraphic API - CGBitmapContextCreatImage()- [CALayer setContents][h1][/h1][h1]3-4、离屏渲染(OFF-SCREEN RENDERING)[/h1]在当前缓冲区创建新的缓冲区渲染缓冲区渲染,圆角(和maskToBounds一起使用),孟层遮罩,阴影,光栅化- 离屏渲染会增加GPU的工作量, CPU+GPU提交一帧的时间超过了16.7ms,造成了不满60帧的卡顿
- 创建新的渲染缓冲区
- 上下文切换
4.OC语言面试问题[h1]4-1、分类[/h1][h2]分类都做了什么事情?[/h2]- 声明私有方法
- 分解体积庞大的类文件
- framework的私有方法公开
[h2]特点[/h2] - [h2]运行时决议(runtime时添加)[/h2]
- [h2]可以为系统类添加分类[/h2][h2]分类中可以添加哪些内容[/h2]
- 可以为系统类添加分类
- 实例方法
- 类方法
- 协议
- 属性(只是声明了set和get方法,而不是私有属性)
[h2][/h2][h2]结构体(objc-runtime-680版本源代码)[/h2]
- const char * name
- cls
- instanceMethods
- classMethods
- protocols
- instanceProperties
[h2]加载调用栈[/h2]
- _objc_init -> map_2_imags -> map_images_nolock -> _read_images -> remethodizeClass
[h2]源码分析[/h2] - 效果上,分类添加的方法可以「覆盖」原类方法
- 同名分类方法谁能生效取决于编译顺序
- 名字相同的分类会引起编译报错
[h1]4-2、扩展相关面试问题[/h1]- 声明私有属性
- 声明私有方法
- 声明私有成员变量
[h2]扩展特性[/h2] - 编译时决议
- 不能为系统类添加扩展
- 只有声明
[h1]4-3、代理相关[/h1]- 软件设计模式
- @protocol形式提现
- 代理传递一对一
- weak规避循环引用
[h1]4-4、通知相关(NSNotification)[/h1] - 是使用观察者模式来实现的用于跨层传递消息的机制(网络层,ui层传递等等)
- 一对多
[h1]4-5、KVO[/h1]- KVO是Objective-C对观察者设计模式的有一种实现
- Apple使用了isa混写(isa-swizzling)来实现kvo
- NSKVONotifying_A (子类)重写setter方法
123456- (void)setValue:(id)obj { [self willChangeValueForKey:@"keyPath"]; //调用父类实现,也即原类的实现 [supe setValue:obj]; [self didChangeValueForKey:@"keyPath"];} - 1、通过kvc设置value能否生效?
- 2、通过成员变量直接赋值value能否生效?
[h1]4-6、KVC (Key-value coding)键值编码[/h1]- valueForKey流程图
- Accessor Method
- setValueForKey流程图
5.RunTime- 数据结构
- 类对象与元类对象
- 消息传递
- 方法缓存
- 消息转发
- Methond-Swizzling
- 动态添加方法
- 动态方法解析
[h1]数据结构[/h1]objc_object
- isa_t
- 关于isa操作相关
- 弱引用相关
- 关联对象相关
- 内存管理objc_class
- Class = objc_class继承自objc_object
- Class puserClass
- cache_t cache
- class_data_bits_t bits;isa指针
- 指针型isa,的内存地址代表Class的地址
- 非指针型的呢值,代表Class的地址isa指向
- 关于对象,指向类对象,-class
- 类对象,元类对象, Class——isa-MetaClasscache_t
- 用于快速查找方法执行函数,消息传递的速度
- 是可增量扩展的哈希表结构
- 是局部性原理的最佳应用(调用频率越高,越容易命中)
- 数据结构
- key
- IMPclass_data_bits_t
- bucket_t 这样的数组
- 主要是对class_rw_t的封装
- class_ro_t
- protocols(二维数组)
- properties(二维数组)
- methods(二维数组)
- clcass_rw_t 代表了类相关的读写信息、对class_ro_t的封装
- class_ro_t
- name(类名等…)
- ivars(二维数组)
- properties(二维数组)
- protocols(二维数组)
- methodlist(二维数组)
- method_t
- struct method_t= name。。。SEL name; 名称
- chonst char* types; 函数体
- IMP imp; 实现
6.类对象,对象(实例,isa指向类对象)
实例对象可以通过isa指针找到类对象,类对象的isa指针可以找到元类对象,元类对象isa指向根原类对象,根元类对象isa指向根类对象
[h1]6-1、消息传递[/h1]- void objc_mesSend(void /* id self, SEL op, …*/)
- void objc_mesSendSuper(void /* struct objc_super * super, SEL op, …*/)
[h2]a.缓存查找 哈希查找[/h2]通过SEL->hash->key找多对应的bucket_t的IMP
- 对于已排序好的列表,采用二分法查找对应执行函数
- 对于没有排序好的列表,采用一般遍历
- 缓存查找是否命中, 方法列表是否命中, 逐级父类方法是否命中, 消息转发
[h2]b.x消息转发[/h2][h3]实例方法的转发[/h3] - 类方法 resolveInstanceMethode: 返回值bool。yes已处理,消息转发结束
- forwarding TargetForSelector: 返回值是id,因该有那个对象来处理,返回nil进入第三次转发
- methodSignatureForSelector: 返回方法签名,返回nil,消息无法处理
- forwardInvocation:
[h2]c.Method-Swizzling[/h2][h2]d.动态添加方法[/h2]- 通过消息转发机制,动态添加方法。在resolveInstanceMethod
[h2]e.动态方法解析[/h2]- @dynamic 动态运行时语言将函数决议推迟到运行时
- 编译时语言在编译器进行函数决议
[h2]f.[obj foo]和objc_msgSend()函数有什么关系?[/h2]- [obj foo]在编译之后,就变成了和objc_msgSend()
[h2]g.runtime如何通过Selector找到对应的IMP地址?[/h2]- 先从缓存方法列表—-待补充
[h2]h.能否向编译后的类中增加实例变量?[/h2] - 不能!编译后结构体class_ro_t是只读属性
7.内存管理- TaggedPointer
- NONPOINTER_ISA
- 散列表(引用技术表和弱引用表)
[h1]7-1、NONPOINTER_ISA[/h1][h1]7-2、散列表 Side Tables()结构[/h1]- 自旋锁
- 引用计数表
- 弱引用表
- Spinlock_t 自旋锁
- RefcountMap Hash表
- weak_table_t 弱引用表 Hash表
[h1]7-3、MRC & ARC[/h1]- ARC是LLVM和Runtime协作的结果
- ARC禁止调用 retain/等
- ARC中新增weak,strong等属性关键字
[h1]7-4、引用计数管理[/h1][h2]retain实现[/h2]123SideTable& table = SideTables()[This];(哈西查找)size_t& refcntStorage = table.refcnts[This]; size_t就是无符号long型refcnStorage += SIDE_TABLE_RC_ONE;[h2]dealloc实现[/h2]- _objc_rootDeallco()
- rootDealloc()
- 是否可以释放
- nonpointer_isa
- weakly_referenced
- has_assoc
- has_cxx_dtor
- has_sidetable_rc
- 3全部为no,调用object_dospose().否则可直接c函数free()释放
[h2]弱引用管理[/h2][h1]7-5、自动释放池[/h1]- 是以栈为节点通过双向链表的形式结合而成
- 是和线程一一对应的
[h2]双向链表[/h2]{id * next;AutoReleasePoolPage * const parent;AutoReleasePoolPage * child;pthread_t const thread}多层嵌套就是多次插入哨兵对象
[h1]7-6、循环引用[/h1][h2]如何破除循环引用[/h2]- __block
- MRC下,__block修饰不会增加引用计数,避免了循环引用
- ARC下,__block修饰会被强引用,无法避免循环引用,需手动解环
- NSTimer的循环引用问题
- nstimer 会对target强引用,Runloop会强制引用NSTimer;
- 添加中间对象,NSTimer和对象之间
- invalidInvocation
8.BlockBlock是将函数以及执行上下文封装起来的对象.使用【clang -rewite-objc file.m】查看编译之后的文件内容 [h1]8-1、截获变量[/h1]- 对于基本数据类型的局部变量截获其值
- 对于对象类型的局部变量连同所有权修饰符一起截获
- 以指针形式截获局部静态变量
- 不截获全局变量、静态全局变量使用【clang -rewite-objc -fobjc-arc file.m】查看编译之后的文件内容
[h1]8-2、__block修饰符[/h1] - 一般情况下,对被截获的变量进行赋值操纵时,需要添加__block修饰符
[h1]8-3、Block的内存管理[/h1]无论任何位置,都可以顺利的用__block修饰变量访问到
[h1]8-4、循环引用问题[/h1] - __block在MRC下不会引用计数+1,在arc下会引用计数+1
9.多线程- GCD
- NSOperation
- NSTread (常驻线程)
- 多线程与锁
[h1]9-1、GCD[/h1] - 同步、异步和串行/并发
- dispath_barrier_async 针对多读单写问题,栅栏函数
- dispath_group
[h2]a.同步串行[/h2] - 死锁原因,是队列引起的 循环等待
[h2]b.同步并发[/h2][h2]c.异步串行[/h2][h2]d.异步并发 performSelector: delay[/h2] [h1]9-2、dispath_barrier_async()[/h1][h1]9-3、dispath_group_async()[/h1]使用GCD实现:ABC执行完,再执行Ddispatch_group_notify(group,….){} [h1]9-4、NSOperation[/h1][h2]任务执行状态控制[/h2]- isReady
- isExecuting
- isFinished
- isCancelled只重写了main方法,底层控制变更任务执行完成状态,以及任务退出如果重写了star方法,自行控制任务状态
- gnustep=base-1.24.9
系统通过KVO的形势移出一个isFinished为YES的NSOperation [h1]9-5、NSThread[/h1]启动流程 star() -> 创建pthread -> main() -> [target performSelector:SEL] -> exit()
[h1]9-6、多线程和锁[/h1]- @synchronized
- atomic
- OSSpinLock
- 自旋锁,循环等待询问,不释放当前资源
- 轻量级的数据访问,简单的+1、-1
- NSRecursivelock 递归锁
- NSLock
- 互斥
- A方法中[lock lock],再次调用[lock lock]会造成死锁,使用NSRecursivelock
- dispath_semaphore_t
10.RunLoopRunLoop是通过内部维护的事件循环来对事件/消息进行管理的一个对象
- 没有消息需要处理时,休眠以避免资源占用
- 有消息需要处理时,立刻被唤醒
[h1]10-1、数据结构[/h1]NSRunLoop 是CFRunLoop的封装,提供了面向对象的API, 开源地址
- CFRunLoop
- CFRunLoopMode
- Source/Timer/Observer
[h2]a.CFRunLoop数据结构[/h2]- pthread 一一对应 (Runloop和线程的关系)
- currentMode CFRunLoopMode
- modes NSMutableSet
- commonModes NSMutableSet
- 不是一种实际存在的Mode,
- 同步Source/Timer/Observer到多个Mode中的一种技术方案
- commonModeItems observer Timer source,
[h2]b.CFRunLoopMode[/h2]- name NSDefaultRunloopMode
- source0
- source1
- observers
- timers
[h2]c.CFRunLoopSource[/h2]- source0 需要手动唤醒线程
- source1 具备唤醒线程的能力
[h2]d.CFRunLoopObserver[/h2] - kCFRunLoopEntry
- kCFRunloopBeforeTimers
- kCFRunLoopBeforeSources
- kCFRunLoopBeforeWaiting
- …
1个RunLoop有多个Model,一个model有多个 source、Timer、Observer [h1][/h1][h1]10-2、RunLoop的Mode[/h1][h1]10-3、s事件循环的实现机制[/h1]void CFRunLoopRun(),用户态到核心态相互切换
10-4、常驻线程
由于自己创建的线程并没有开启runloop(一一对应关系),需要创建并开启一个Runloop
- 为当前线程开启一个RunLoop.
- 向该RunLoop中添加一个Port/Source等维持RunLoop的事件循环. 没有就会退出
- 启动该RunLoop
11.网络
12.设计模式
[h1]12-1、设计原则[/h1]- 单一指责原则
- 依赖倒置原则
- 抽象不应该依赖于具体实现,具体实现可以依赖于抽象. 声明的接口等
- 开闭原则
- 里氏替换原则
- 接口隔离原则
- 使用多个专门的协议、delegate,datasouce
- 迪米特法则
- 一个对象应当对其他对象有尽可能少的了解
- 高内聚,低耦合
[h1]12-2、责任链[/h1][h1]12-3、桥接[/h1][h1]12-4、适配器[/h1]- 一个现有类需要适应变化的问题
- 对象适配器
- 类适配器
[h2]对象适配器[/h2]成员变量的方式持有原类
12345- (void)request { //额外处理 [self.target operation]; //额外处理}
[h1]12-5、单例[/h1]instance = [[super allocWithZone:NULL] init];避免循环调用
123456+ (id)allocWithZone:(struct _NSZone *)zone { return [self shareInstance];}- (id)copyWithZone:(struct _NSZone *)zone { return self;}[h1]12-6、命令模式[/h1]13.架构&框架
- 图片缓存
- 阅读时长统计
- 复杂页面架构
- 客户端整体架构
[h1]13-1、图片缓存框架[/h1]- 内存的设计上需要考虑哪些问题?
- 储存的Size
- 淘汰策略
[h2]存储的Size[/h2] - 10k以下的图片
- 100k以下20张图片
- 100k以上5张左右
[h3]淘汰策略(内存)[/h3] - 队列先进先出的方式淘汰
- 模拟LRU算法 (最近最久,如30分钟之内是否使用过)
- 每次进行读写时
- 前后台切换时
[h3]磁盘设计[/h3] - 定时检查
- 提高检查出发的频率
- 存储方式
- 大小限制(如100M)
- 淘汰策略(如某一图片储存时间超过7天)
[h3]网络请求图片设计[/h3] - 图片请求最大量
- 请求超时策略
- 请求优先级
[h3]图片解码[/h3] - 对于不同格式的图片,用什么方式?
- 应用策略模式对不同图片进行解码
- 在哪个阶段图片解码处理
- 磁盘读取后
- 网络请求返回后
[h3]线程处理[/h3] [h1]13-2、阅读时长统计[/h1]- 记录器
- 记录管理者
- 前后台切换
- 从无网到有网的变化
- 记录缓存
- 磁盘储存
- 上传器(延时上传)
- 处理数据丢失
[h1]13-3、负责页面架构[/h1][h2]MVVM[/h2]- view(ViewController)
- ViewModel
- Model(Engeer)
[h1]13-4、整体框架[/h1]- 独立于App的通用层
- 通用业务层
- 中间层
- 业务ABCD
[h2]业务解耦[/h2] - OpenUrl
- 依赖注入(中间层)
14.算法
- 字符串反转 ;
- 链表反转
- 有序数组合并
- Hash算法
- 查找两个子视图的共同父视图
- 求无序数组当中的中位数
15.第三方库
15-1、AFNetworking
架构图
- AFURLSessionManager 核心类
- NSUrLSession
- AFSecurityPolicy
- AFNetworkReachablilityManager
- 创建和管理NSUrLSession、NSURLSessionTask
- 实现NSURLSessionDelegate等协议的代理方法
- 引入AFSecurityPolicy保证请求安全
- 引入AFNetworkReachablilityManager监控网络状态
- AFHTTPSessionManeger
- AFURLRequestSerialzation
- AFURLResponseSerial
[h1]15-2、SDWebImage[/h1]架构图
15-3、ReactiveCocoa
函数响应式编程框架,信号,订阅信号ReactiveCocoa中核心类RACSignal -> RACStream
- RACDynamicSignal
- RACReturnSignal
- RACEmptySignal
- RACErrorSignal
[h2]a.信号[/h2]代表一连串的状态
- empty,return,bind,concat,zipWith
[h2]b.订阅 RACSubscriber[/h2]start -> RACSignal -> -subscribeNext: -> RACSubscriber -> -sendNext: -> -sendCompleted
[h1]15-4、AsyncDisplayKit[/h1]提升iOS界面渲染性能的一个框架主要处理问题
- Layout 文本宽高计算、视图布局计算
- Rendering 文本渲染、图片解码、图形绘制
- UIKit Objects 对象创建、对象调整、对象销毁
[h2]基本原理[/h2]- ASNode === UIView === CALayer
- 针对ASNode的修改和提交,会对其进行封装提交到一个全局容器当中
- ASDK也在RunLoop中注册了一个Observer
- 当RunLoop进入休眠前,ASDK执行该Loop内提交的所有任务
|
|