1:讲讲你对atomic & nonatomic的理解
首先这个是自旋锁,也就是系统自动生成的读写方法的时候会在赋值和取值的时候用@synthesized(self){}包裹(单例也是这个用法),目的只是保证读写操作是原子操作,所以读写操作的原子性和线程安全是无关的。计算机里的原子操作就是操作的一个单元不会被别的CPU访问打断,所以这里的原子安全不是真正意义的原子安全甚至影响性能。
声明一个引用变量默认是atomic,FMDB这些第三方库有的时候的确是用的atomic但是为了读写性能关系一般使用nonatomic,
2:被 weak 修饰的对象在被释放的时候会发生什么?是如何实现的?知道sideTable 么?里面的结构可以画出来么?
runtime会维护一张全局的weak表,weak修饰的对象或者内存,会被维护在一张weak列表里,key是内存或者对象的地址,value是一个数组,存放的是指向这块内存的指针的地址。
这张表的管理分为初始化,更新,销毁三个步骤,
初始化:objc_initweak 会创建一个新的weak指针指向这块地址。
更新:objc_storeWeak()会更新指针指向,创建对应上面的若引用表entrys。
移除:根据对象地址找到weak表移除对应的数组entrys;
struct SideTable {
// 保证原子操作的自旋锁
spinlock_t slock;
// 引用计数的 hash 表
RefcountMap refcnts;
// weak 引用全局 hash 表
weak_table_t weak_table;
}
struct weak_table_t {
// 保存了所有指向指定对象的 weak 指针
weak_entry_t *weak_entries;
// 存储空间
size_t num_entries;
// 参与判断引用计数辅助量
uintptr_t mask;
// hash key 最大偏移值
uintptr_t max_hash_displacement;
};
3:block 用什么修饰?strong 可以?
block 大多使用copy修饰,strong也可以呀因为他就是一个特殊的OC对象,而且会和copy一样把他从栈区拷贝到堆区。
_NSConcreteStackBlock:
只用到外部局部变量、成员属性变量,且没有强指针引用的block都是StackBlock。
StackBlock的生命周期由系统控制的,一旦返回之后,就被系统销毁了。
_NSConcreteMallocBlock:
有强指针引用或copy修饰的成员属性引用的block会被复制一份到堆中成为MallocBlock,没有强指针引用即销毁,生命周期由程序员控制
_NSConcreteGlobalBlock:
没有用到外界变量或只用到全局变量、静态变量的block为_NSConcreteGlobalBlock,生命周期从创建到应用程序结束。。
4:block 为什么能够捕获外界变量? __block做了什么事?
首先要看内部使用到的是什么类型的值,如果是静态全局变量或者是全局变量,block捕获他们之后只是对其进行++操作,block执行结束之后根据引用计数器进行操作,
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_a_0 *a; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
import <Foundation/Foundation.h>
int global_i = 1;
static int static_global_j = 2;
int main(int argc, const char * argv[])
{
static int static_k = 3;
int val = 4;
void (^myBlock)(void) = ^{
global_i ++;
static_global_j ++;
static_k ++;
NSLog(@"Block中
global_i = %d,static_global_j = %d,static_k = %d,val = %d",global_i,static_global_j,static_k,val);
}; global_i ++;
static_global_j ++;
static_k ++;
val ++;
NSLog(@"Block外 global_i = %d,static_global_j = %d,static_k = %d,val = %d",global_i,static_global_j,static_k,val);
myBlock();
return 0;
}
Block 外 global_i = 2,static_global_j = 3,static_k = 4,val = 5
Block 中 global_i = 3,static_global_j = 4,static_k = 5,val = 4

这个构造函数中,自动变量和静态变量被捕获为成员变量追加到了构造函数中。
到此为止,上面提出的第二个问题就解开答案了。自动变量是以值传递方式传递到Block的构造函数里面去的。Block只捕获Block中会用到的变量。由于只捕获了自动变量的值,并非内存地址,所以Block内部不能改变自动变量的值。Block捕获的外部变量可以改变值的是静态变量,静态全局变量,全局变量。上面例子也都证明过了。
总结一下在Block中改变变量值有2种方式,一是传递内存地址指针到Block中,二是改变存储区方式(__block)
5:谈谈你对事件的传递链和响应链的理解 (这个面试题,如果求职者能够回答一些实际开发相关的处理,不只是简单的概念,予以加分)

首先APp会注册一个端口监测监测系统事件
硬件监测到触摸,打包成事件放到系统端口,app线程监测到后会唤起主线程的runloop
事件的传递是从最外层的父试图到具体的子试图的,相应是从子试图到appdelegate
(1.alpha>0.01 2.userInteractionEnabled == YES 3.hidden = NO)
runloop对事件的处理机制:
6:谈谈 KVC 以及 KVO 的理解?
KVO:当没有存取方法而通过KVC的setValue修改属性值时,同样的在运行时也会在setValue:forKey方法里默认调用上面俩方法
7:RunLoop 的作用是什么?它的内部工作机制了解么?
8:苹果是如何实现 autoreleasepool的?
9:谈谈你对 FRP (函数响应式) 的理解,延伸一下 RxSwift 或者 RAC!
10:平时开发有没有玩过 Instrument ?
分析:这里的内容非常有意思,对于一个iOS高级开发人员,我觉得还有很有必要掌握的!尤其开发3-5年,如果没有掌握这些内容我觉得是不合格的
在一些合适的地方可以进行延伸!
第二题,第一问是关于 weak 这个面试是非常经典的,只要是个iOS开发都能回答一点。但是后面部分就需要他的学习能力了!
第三题和第四题,侧击一下 MRC 一个资深4-5年开发经验或多或少都应该要知道一些 MRC 相关的知识!顺便可以摸摸求职者的探索能力
第五题和第六题:我觉得如果求职者回答了一些实际开发相关以及文档能力的应该予以加分
第七题、第八题、第九题以及第十题都是可以拓展更多的内容!
1:什么是 isa,isa 的作用是什么?
2:一个实例对象的isa 指向什么?类对象指向什么?元类isa 指向什么?
3:objc 中类方法和实例方法有什么本质区别和联系?
4:load 和 initialize 的去呗?
5:_objc_msgForward 函数是做什么的?直接调用会发生什么问题?Invocation
6:简述下 Objective-C 中调用方法的过程
7:能否想向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?
8:谈谈你对切面编程的理解
分析:Runtime 这个模块iOS面试无论初中高都会面试。我觉得这个模块不光只是仅仅问问关于知识点内容,我更新想要听到求职者在这里面的爬坑探索辛历路程!
1:HTTP的缺陷是什么?
2:谈谈三次握手,四次挥手!为什么是三次握手,四次挥手?
3:socket 连接和 Http 连接的区别
4:什么时候POP网路,有了 Alamofire 封装网络 URLSession为什么还要用Moya ?
5:如何实现 dispatch_once
6:能否写一个读写锁?谈谈具体的分析
7:什么时候会出现死锁?如何避免?
8:有哪几种锁?各自的原理?它们之间的区别是什么?最好可以结合使用场景来说
分析:这个模块可能是一般开发人员的盲区。因为一般开发真心没必要去操心这么多!面试官你们可以挑去拷问就OK,面试过程中不能对答如流也是正常!当然如果能够回答上来应该加分!
1.数据结构的存储一般常用的有几种?各有什么特点?
2.集合结构 线性结构 树形结构 图形结构
3.单向链表 双向链表 循环链表
4.数组和链表区别
5.堆、栈和队列
6.输入一棵二叉树的根结点,求该树的深度?
7.输入一课二叉树的根结点,判断该树是不是平衡二叉树?
1.时间复杂度
2.空间复杂度
3.常用的排序算法
4.字符串反转
5.链表反转(头差法)
6.有序数组合并
7.查找第一个只出现一次的字符(Hash查找)
8.查找两个子视图的共同父视图
9.无序数组中的中位数(快排思想)
10.给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。
分析:这个模块是绝大部分开发人员的软肋!这个模块是最能测试求职者思维能力的!但是我不建议面试官直接让求职者手写 在那样的面试紧张环境,手写数据结构或者一些算法代码,是非常有挑战的!思维到我觉得差不多!
1:设计模式是为了解决什么问题的?
2:看过哪些第三方框架的源码,它们是怎么设计的?
3:可以说几个重构的技巧么?你觉得重构适合什么时候来做?
4:开发中常用架构设计模式你怎么选型?
5:你是如何组件化解耦的?
分析:架构设计这一层对于一个iOS中高级开发人员来说。这一块那是他必须要去思考和感受总结的!如果这位求职者开发4-5年了,一直都在做应用层界面开发,那么想必他未来的职业晋升是已经落后了的!面试官不妨在这一个模块单独设计成一面,就和求职者一起交流讨论。毕竟这些思维的设计,也许能够给面试官带来一些不一样的东西!😊
1:tableView 有什么好的性能优化方案?
tableview 是非常容易卡顿的控件。有两个原因,
1.布局复杂,数据量复杂,涉及到大量的计算导致的CPU过载。
针对布局复杂我们使用autolayout时,但不要使用XIB或者SB,要缓存高度。如果不使用autolayout需要动态计算的要在后台计算,关于如果滑动非常快的的时候,网络请求要队列请求,并且根据index大小和当前屏幕的关系,设置请求优先级。可以根据当前用户行为监听runLoopModel去安排后台任务。
2.GPU负荷造成的离屏渲染。首先要明白渲染机制,cup负责计算gpu负责渲染,渲染结束后缓存到缓存区,cpu接到asyn 信号以后去缓存区去渲染后的内容展示到屏幕上这为一个帧,16.67ms在这个过程中无论是cpu还是gpu负荷都会造成掉帧产生卡顿。关于gpu的负荷多发生于一些圆角和阴影的操作,gpu的渲染是对layer的操作,像画家一样一层一层的画上去,但是你所添加的圆角和阴影可能要对之前已经渲染好的layer做修改和调整,这就需要开辟出另外一块内存缓存之前的layer并做修改,就造成了离屏渲染。但是离屏渲染好像又是无法避免的,既然无法避免对于一些layer层级复杂的试图可以打开光栅化shouldRasterize这样就只会触发一次离屏渲染,因为光栅化会缓存之前离屏渲染的内容。
针对不停滑动的问题优化思路
1.参考QQ空间的思路使用缓存数据,在最后结束的时候加载广告使用户无感。
2.智能预加载数据,设置一个下限值,在scrollview滑动的代理方法里根据contentsize和setoff.y的位置计算。判断大于0.7的时候去加载数据。
3.使用asynkit(一个强大的框架,渲染性能好,它会把复杂的layer(不需要响应事件)层级渲染成一层)框架智能加载。根据滑动方向设置两个屏幕的cell加载优先级属性,它会划分出即将加载的、加载一点的已经加载的三种状态。做分别的渲染处理和数据预加载。
4.惰性加载只加载用户屏幕区域内的cell 这种无疑是性能最优的方案,但是滚动过程中的白条和服务端的支持增加了这种方案的风险性.
以上只是加载方案的不同,但最重要的还是做好缓存工作。
1.数据:图片不要太大,使用缓存图片,返回圆角或者透明,数据结构不要太复杂,不要过多消耗cpu计算,设置frame类缓存高度,使用缓存数据
2.布局:autolayout布局消耗计算复杂cpu性能,高度自适应,给预设高度
3.渲染:避免cpu,gpu负荷最好异步渲染,使用drawRect异步绘制
4.层级,避免过多层子试图,和动态增删view,
5.滑动优化:避免滑动太快开辟太多线程进而内存开销太大,滑动停止或者即将停止的时候再去异步加载资源图片
6,复用和懒加载
2: 界面卡顿和检测你都是怎么处理?
1.死锁:主线程拿到锁 A,需要获得锁 B,而同时某个子线程拿了锁 B,需要锁 A,这样相互等待就死锁了。
2.抢锁:主线程需要访问 DB,而此时某个子线程往 DB 插入大量数据。通常抢锁的体验是偶尔卡一阵子,过会就恢复了。
3.主线程大量 IO:主线程为了方便直接写入大量数据,会导致界面卡顿。
4.主线程大量计算:算法不合理,导致主线程某个函数占用大量 CPU。
5.大量的 UI 绘制:复杂的 UI、图文混排等,带来大量的 UI 绘制。
3:谈谈你对离屏渲染的理解?
双刃剑,有些时候的确需要离屏渲染。无论怎样都是合理利用一个时间周期内CPU和GPU的资源,drawRect这个方法就是一个例子,他的确实在CPU上异步绘制的消耗cpu资源,但是有一定情况下它比GPU离屏渲染可能效果会好一点。但是他也有本身的缺点就是addsubview的时候会重新执行。
离屏渲染:
4:如何降低APP包的大小
5:日常如何检查内存泄露?
6:APP启动时间应从哪些方面优化?
分析:现在APP性能优化以及成为iOS中高级开发人员必须要去关系的东西!这一块我个人建议结合实际开发去和求职者交流。而不是仅仅停留在知识点问答,因为没有实际开发能力的性能优化都只是纸上谈兵!
网友评论