豆瓣面试题

豆瓣面试题

豆瓣面试题

1.是在大学里学的 iOS 吗?怎么学的 iOS?

2.判断2个单链表是否交叉,实现代码,如果有交叉找出交叉点。

方法一:哈希表法

遍历链表 A 并将每个结点的地址/引用存储在哈希表中。然后检查链表 B 中的每一个结点 bi是否在哈希表中。若在,则 bi为相交结点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var getIntersectionNode = function(headA, headB) {
let mySet = new Set();
let curr = headA;
while (curr != null) {
mySet.add(curr);
curr = curr.next;
}
curr = headB;
while (curr != null) {
if (mySet.has(curr)) {
return curr;
}
curr = curr.next;
}
return null;
};

方法二:双指针法

1
2
3
4
5
6
7
8
9
10
11
var getIntersectionNode = function(headA, headB) {
if (headA == null || headB == null) {
return null;
}
let pA = headA, pB = headB;
while (pA != pB) {
pA = pA == null ? headB : pA.next;
pB = pB == null ? headA : pB.next;
}
return pA;
};

3.说一下 @property 属性修饰符,block 为什么用 copy ?weak 的实现原理?atomic 怎么保证安全?怎么解决读写安全?

@property 什么都不写

基本数据类型默认修饰符是 atomic, readWrite, assign

对象类型默认修饰符是 atomic, readWrite, strong

block 为什么用 copy?

如果是全局静态 block 的话,它直到程序结束的时候,才会被被释放。但是我们实际操作中基本上不会使用到不访问外部变量的 block。

如果是保存在栈中的 block,它会随着函数调用结束被销毁。从而导致我们在执行一个包含 block 的函数之后,就无法再访问这个 block。因为(函数结束,函数栈就销毁了,存在函数里面的 block 也就没有了),我们再使用 block 时,就会产生空指针异常。

如果是堆中的 block,也就是 copy 修饰的 block。他的生命周期就是随着对象的销毁而结束的。只要对象不销毁,我们就可以调用的到在堆中的 block。

这就是为什么我们要用 copy 来修饰 block。因为不用 copy 修饰的访问外部变量的 block,只在他所在的函数被调用的那一瞬间可以使用。之后就消失了。

weak 的实现原理

参考链接:iOS 底层解析weak的实现原理

atomic 怎么保证安全?

属性声明为atomic时,在该属性在调用getter和setter方法时,会加上同步锁,即在属性在调用getter和setter方法时,保证同一时刻只能有一个线程调用属性的读/写方法。保证了读和写的过程是可靠的。但并不能保证数据一定是可靠的。

怎么解决读写安全?

  • dispatch_barrier_async
  • pthread_rwlock:读写锁

4.OSSPinLock 为什么不用了?

系统维护了 5 个不同的线程优先级/QoS: background,utility,default,user-initiated,user-interactive。高优先级线程始终会在低优先级线程前执行,一个线程不会受到比它更低优先级线程的干扰。这种线程调度算法会产生潜在的优先级反转问题,从而破坏了 spin lock。

具体来说,如果一个低优先级的线程获得锁并访问共享资源,这时一个高优先级的线程也尝试获得这个锁,它会处于 spin lock 的忙等状态从而占用大量 CPU。此时低优先级线程无法与高优先级线程争夺 CPU 时间,从而导致任务迟迟完不成、无法释放 lock。这并不只是理论上的问题,libobjc 已经遇到了很多次这个问题了,于是苹果的工程师停用了 OSSpinLock。

参考链接:不再安全的 OSSpinLock

5.同时请求两张图片,之后合成一张图

  • dispatch_group 配合 dispatch_semaphore
  • dispatch_group dispatch_enter dispatch_leave
  • dispatch_barrier

还有其他的方式吗?只使用 dispatch_semphore 和 dispatch_async 怎么实现?

6.Runloop 有哪些使用场景?

  • 控制线程生命周期,实现常驻线程。
  • TableView 延迟加载图片。滑动时不调用赋值图片的方法,等滑动完毕切换到 NSDefaultRunLoopMode 时才调用。

    1
    [self.img performSelector:@selector(setImage:) withObject:image afterDelay:0 inModes:[NSDefaultRunLoopMode]];
  • 解决 NSTimer 在滑动时停止的问题。

  • 监测 RunLoop 的状态监测应用卡顿。

7.子线程怎么销毁?换一种问法,常驻线程怎么销毁?NSPort 的生命周期怎么控制?

1
2
3
4
5
6
7
8
- (void)_stopThread {
self.isRun = NO;

// 停止当前线程的Runloop
CFRunLoopStop(CFRunLoopGetCurrent());

self.innerThread = nil;
}

参考链接:iOS RunLoop 常驻线程简化实现

8.说一下响应者链。有三个视图 A、B、C 依次被添加到 self.view 上,frame 相同,复写 C 的 pointInSide 方法缩小范围,点击 C 的边缘 C 会响应吗?C 不响应的话,B 会响应吗?

点击 C 的边缘 C 不会响应,B 会响应。

9.组件是怎么拆分的?组件化是怎么传值的?组件的注册是写在哪里的?写在 +load 会不会有性能问题?怎么解决?

参考链接:iOS组件化方案

10.了解 HTTPDNS 吗?询问的操作是谁去问?本层->上层->上上层

  • 使用HTTP(HTTPS)协议绕过运营商的 Local DNS,避免域名劫持,也更准确地判断客户端地区和运营商,得到更精准的解析结果;
  • HTTPDNS 能够直接得到客户端的出口网关 IP,从而更准确地判断客户端地区和运营商,得到更精准的解析结果;

参考链接:HTTPDNS

11.性能优化,tableView 怎么缓存行高?高度存在哪儿?key 是什么?横竖屏怎么缓存行高?一个页面大概率不会横屏,缓存横竖屏两份行高是不是有些浪费,怎么去处理行高?tableView 怎么控制缓存数量,比如最大1000条数据的缓存量。

提前计算好cell的高度,缓存在相应的数据源模型中。cellHeight

12.加载大图,地图这种大图怎么加载优化?系统 API 的原理是什么?

  • 分片比例裁剪方式。参考苹果给出的 demo,利用 CGImageCreateWithImageInRect 截取原图对应位置的内容,再通过 CGContextDrawImage 渲染到指定位置;
  • 利用 CATiledLayer 层级的API,自动进行绘制;

参考链接:iOS 大图显示解决办法

13.项目怎么分工的?你负责什么?有没有什么值得说的点?H5 与原生交互有什么经验分享吗?怎么给 webView 加载的页面发的网络请求统一加上 header?

14.AFN 早期版本为什么要使用常驻线程?怎么统一设置 header?

1
[requestSerializer setValue:value forHTTPHeaderField:httpHeaderField];

15.RN 和 Flutter 的底层实现有什么区别?

在 Android 和 iOS 上,默认情况下 Flutter 和 React Native 都需要一个原生平台的 Activity / ViewController 支持,且在原生层面属于一个“单页面应用”,而它们之间最大的不同点其实在于 UI 构建 :

  • React Native :

React Native 是一套 UI 框架,默认情况下 React Native 会在 Activity 下加载 JS 文件,然后运行在 JavaScriptCore 中解析 Bundle 文件布局,最终堆叠出一系列的原生控件进行渲染。

简单来说就是 通过写 JS 代码配置页面布局,然后 React Native 最终会解析渲染成原生控件,如 <View> 标签对应 ViewGroup/UIView ,<ScrollView> 标签对应 ScrollView/UIScrollView ,<Image> 标签对应 ImageView/UIImageView 等。

  • Flutter :

如果说 React Native 是为开发者做了平台兼容,那 Flutter 则更像是为开发者屏蔽平台的概念。

Flutter 中绝大部分的 Widget 都与平台无关, 开发者基于 Framework 开发 App ,而 Framework 运行在 Engine 之上,由 Engine 进行适配和跨平台支持。这个跨平台的支持过程,其实就是将 Flutter UI 中的 Widget “数据化” ,然后通过 Engine 上的 Skia 直接绘制到屏幕上 。

参考链接:Flutter 与 React Native 的对比分析

16.平时怎么学习 iOS?上家公司的离职原因,未来规划,有没有要问我的?

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×