iOS面试-字节跳动
字节一面内容:
1、 自我介绍
2、 介绍一下简历中的一个项目
3、 面向对象的三个要素?4、 详细说说「多态」?
面向对象编程(OOP)的三个核心要素是:
-
封装(Encapsulation)
- 将数据(属性)和操作数据的方法(行为)捆绑在一起,形成一个独立的“对象”。
- 通过访问控制(如
private、protected、public)隐藏内部实现细节,仅暴露必要的接口。 - 优点:降低耦合、便于维护。
-
继承(Inheritance)
- 子类继承父类的属性和方法。
- 支持代码复用,同时可以通过方法重写(Override)实现多态。
- 类型:单继承,iOS 通过协议实现多继承(本身不支持多继承)
-
多态(Polymorphism)
同一操作作用于不同对象时,可以有不同的行为。
编译时多态(方法重载,Overload)。
运行时多态(方法重写,Override,通过继承+虚函数/接口实现)。
// 封装
class Animal {
private var name: String
func getName() -> String {
return name
}
}
// 继承
class Dog: Animal {
}
// 多态
class Cat: Animal {
override func getName() -> String {
return "Cat: \(super.getName())"
}
}
5、 Java,python,OC运行效率孰高?
性能排序(大致)
1.Objective-C ≈ C/C++(原生编译,最低开销)
2.Java(JIT 优化,但需虚拟机)
3.Python(解释执行,动态类型)
关键场景对比
| 场景 | Objective-C | Java | Python |
|---|---|---|---|
| 数值计算 | 极快(C 混编) | 快(JIT 优化) | 慢(除非用 NumPy) |
| 移动开发 | iOS 首选 | Android 首选 | 不适用 |
| 高并发后端 | 不常见 | 优秀(Netty 等) | 一般(异步框架) |
| 开发效率 | 低(手动内存管理) | 中等 | 极高(简洁语法) |
总结
- 追求极致性能:Objective-C(或 Swift/C++)是首选,尤其适合 iOS 和系统级开发。
- 平衡效率与生态:Java 适合跨平台企业应用,JIT 优化使其性能稳定。
- 快速开发/原型:Python 牺牲性能换取开发速度,但可通过扩展弥补短板。
6、 Property,其中copy如何?
7、 Property(nonatomatic, copy) NSMutableArray有什么问题?
可变的对象,变成了不可变的对象,不能使用增/删/改的函数
@property (nonatomic, copy) NSString *copyString;
@property (nonatomic, strong) NSString *strongString;
NSMutableString *mutableString = [NSMutableString stringWithString:@"Hello"];
self.copyString = mutableString;
self.strongString = mutableString;
[mutableString appendString:@" World"];
NSLog(@"---copy:%@", self.copyString);
NSLog(@"---strong:%@", self.strongString);
// 输出 "Hello"(copyString 是原字符串的独立副本,不受修改影响)
// 输出 "Hello World"(strongString 被 mutableString 的修改影响)
8、 Copy和MutableCopy的区别?
Copy:浅拷贝
- 可变性(Mutability)
-
copy
返回一个不可变对象,例如:NSMutableArray调用copy后返回NSArray。NSMutableArray *mutableArray = [NSMutableArray arrayWithObjects:@1, @2, nil]; NSArray *copiedArray = [mutableArray copy]; // 返回不可变数组 // copiedArray 无法调用 addObject: 等方法。 -
mutableCopy- 返回一个可变对象,例如:
NSArray调用mutableCopy后返回NSMutableArray。 - 代码示例:
NSArray *array = @[@1, @2]; NSMutableArray *mutableCopiedArray = [array mutableCopy]; // 返回可变数组 [mutableCopiedArray addObject:@3]; // 可以修改
- 返回一个可变对象,例如:
- 拷贝的深度(深拷贝 vs 浅拷贝)
-
对于非集合类型(如
NSString、NSNumber):
copy通常是浅拷贝(由于不可变对象的优化,可能直接返回原对象)。
mutableCopy是深拷贝(生成新的可变对象,如NSMutableString)。 -
对于集合类型(如
NSArray、NSDictionary):
copy和mutableCopy默认都是浅拷贝(只拷贝容器本身,容器内的元素仍是原对象的引用)。
如果需要深拷贝,需手动遍历或使用NSCopying/NSMutableCopying协议实现。
系统提供的深拷贝方法(如NSKeyedArchiver):NSArray *deepCopiedArray = [NSKeyedUnarchiver unarchiveObjectWithData: [NSKeyedArchiver archivedDataWithRootObject:originalArray]];
- 适用场景
-
使用
copy:
保护对象的不可变性(如@property修饰符)。
防止外部修改内部数据(例如NSMutableString传入后通过copy转为NSString)。 -
使用
mutableCopy:
需要修改拷贝后的对象时(如将NSArray转为NSMutableArray并增删元素)。
4. @property 中的 copy 修饰符
在定义属性时,copy 修饰符会自动调用 copy 方法,确保存储的是不可变对象:
@property (nonatomic, copy) NSString *name; // 传入 NSMutableString 会被转为 NSString
总结对比表
| 方法/特性 | 返回对象类型 | 可变性 | 拷贝深度(默认) |
|---|---|---|---|
copy |
不可变 | 不可修改 | 浅拷贝 |
mutableCopy |
可变 | 可修改 | 浅拷贝/深拷贝 |
注意事项
-
自定义类的拷贝:
若需支持copy或mutableCopy,需实现NSCopying/NSMutableCopying协议。 -
性能:
深拷贝比浅拷贝开销更大,需根据场景选择。 -
Objective-C 与 Swift:
在 Swift 中,copy和mutableCopy不常用,更多依赖值类型的原生拷贝语义(如Array、Dictionary的赋值行为)。
9、 解释下类别(Category),原理?
在Swift中类似的概念叫做扩展(Extension)。
类别的主要特点
- 扩展现有类:可以为系统类或自定义类添加方法
- 不需要子类化:避免创建大量子类
- 运行时添加:类别的方法在运行时被添加到类中
-
不能添加实例变量:只能添加方法,不能添加属性或实例变量
可以使用关联对象来添加属性或实例变量
类别的原理
类别在底层是通过runtime实现的,其工作原理如下:
- 编译时:类别中的方法会被编译成独立的函数
- 运行时:当程序加载时,runtime会将类别中的方法添加到原始类的方法列表中
- 方法合并:如果类别方法与原始类方法同名,类别方法会"覆盖"原始类方法(实际上是将类别方法插入到方法列表前面)
10、解释下封装,重载?
11、 OC存在多重继承吗?
12、了解表视图UITableView吗,解释一下复用原理?
维护一个不可见的重用池:超出屏幕显示区域(不会被销毁),放入队列,滑动到屏幕显示区域,再从队列中拿出来使用。
每个可复用的单元格都有一个唯一的标识符。
13、说明一下表视图UITableView的滑动卡顿的优化方法?
使用 CADisplayLink 监控帧率:实时检测卡顿情况
1.设置预估高度
2.异步计算高度,并缓存高度
3.离屏渲染优化:圆角、阴影、光栅话、避免使用透明视图(alpha < 1.0)
4.异步加载图片:使用缓存机制(如 NSCache、SDWebImage、Kingfisher)
5.提前加载处理大批量数据
14、viewDidLoad和viewDidAppear的调用时机(一次和多次的区别);
15、页面间的传值方式有哪些?
公有属性、公有方法和协议、block传值、通知,extern全局变量传值,NSUserDefault简单数据存储传值
16、通知和delegate的区别?
通知:基于 观察者模式,是一种 一对多 的通信机制。(全局范围)
delegate:基于 代理模式,是一种 一对一 的通信机制。(指定范围)
17、 通知的发送和接收是否在同一线程?(NSNotification)
通知的发送和接收是否在同一线程取决于发送通知的方式以及运行循环(RunLoop)的情况。以下是关键点总结:
| 场景 | 接收线程 |
|---|---|
同步发送 + 未指定 queue
|
与发送线程相同 |
同步发送 + 指定 queue
|
指定的队列(如主线程) |
| 异步发送(手动切换队列) | 取决于切换后的队列 |
18、HTTP和HTTPS区别?
19、OC中多线程一般有几个方案?
20、了解NSURLConnection和Session吗?
1.断点续传
2.取消/恢复请求
3.后台请求
4.自定义缓存存储
5.复用性
以上NSURLConnection不支持,NSURLSession支持
NSURLConnection:2018年被标记为废弃(deprecated)
NSURLConnection通过单一Delegate处理所有请求的回调
21、说一下NSURLSession具体的实现原理
NSURLSession 通过 CFNetwork 进行底层数据传输,使用 GCD 管理任务,支持后台下载、断点续传和缓存控制。它是 Apple 推荐的网络请求方式,替代了 NSURLConnection,并提供了更强的性能和可扩展性。
1.创建 NSURLSession 对象
2.创建并启动 NSURLSessionTask
3.数据传输
4.任务完成
22、http的头部的几个码?
1xx(信息性状态码)
2xx(成功状态码)
3xx(重定向状态码)
4xx(客户端错误状态码)
5xx(服务器错误状态码)
23、编程题:实现一个二叉树的倒置
字节二面内容:
1、老虎吃羊问题。(博弈论,老虎要吃羊,假设所有老虎是理智的,即首先为了生存,其次为了饱腹,老虎吃了羊后会变成羊,同样会被其他老虎吃掉。现在,N只老虎和1只羊,请问N为多少时,老虎们会吃羊。动态规划问题,奇数吃,偶数不吃。)
归纳推理:
看起来,当老虎的数量为奇数时,老虎会选择吃羊;当老虎的数量为偶数时,老虎不会吃羊。
归纳假设:对于所有正整数k,
当N=2k-1(奇数)时,老虎会吃羊。
当N=2k(偶数)时,老虎不会吃羊。
2、青蛙跳格子,斐波拉契数列;
f(n) = f(n-1) + f(n-2);条件 n ≥ 2
3、如果让你自己实现SDWebImage的二级存储机制,你如果实现?
如果要自己实现类似 SDWebImage 的二级存储机制(内存缓存 + 磁盘缓存),我会设计一个 ImageCacheManager,结合 NSCache(内存缓存)和 NSFileManager(磁盘缓存),并支持 LRU 淘汰策略、异步操作和 线程安全。
ImageCacheManager
├── MemoryCache (NSCache:内存缓存)
│ ├── 存储解码后的 UIImage(快速读取)
│ └── 基于 cost(像素大小)自动清理
├── DiskCache (文件系统:磁盘缓存)
│ ├── 存储原始图片数据(NSData)
│ └── 基于 LRU 的过期清理
└── 统一接口
├── 异步存储(内存 + 磁盘)
└── 异步读取(优先内存,其次磁盘)
4、说一下对于http的理解?
示例 URL: https://www.example.com/index.html
- 解析 URL:获取协议、端口、域名、路径
- DNS 解析(域名 → IP 地址)
- 建立 TCP 连接(三次握手)
- TLS 握手(HTTPS 安全连接,加密认证)
- 发送 HTTP 请求
- 服务器处理请求,返回 HTTP 响应
- 关闭 TCP 连接(四次挥手)
如果 HTTP 头 Connection: close 或没有 keep-alive,浏览器会关闭 TCP 连接:
FIN(要终止):客户端 → 服务器(FIN=1, Seq=u)。
ACK(确认):服务器 → 客户端(ACK=1, Ack=u+1)。
FIN(已经终止):服务器 → 客户端(FIN=1, Seq=v)。
ACK(确认):客户端 → 服务器(ACK=1, Ack=v+1)。
TCP 连接关闭。
5、http的返回状态码有了解吗?
200 OK(成功)
301 Moved Permanently(永久重定向)
404 Not Found(资源不存在)
500 Internal Server Error(服务器错误)
6、为什么说http是无状态的?
指的是 协议本身不会保留之前的请求信息,每个 HTTP 请求都是独立的,服务器默认不会记住客户端的历史请求状态。
- 降低服务器负担:服务器不需要存储每个客户端的历史状态,节省内存和计算资源。
- 提高可扩展性:无状态使得 HTTP 更容易支持高并发(如 CDN、负载均衡)。
- 简化协议设计:每个请求独立处理,无需维护复杂的会话逻辑。
7、iOS 为什么不用原生的APNS技术实现呢?
必须联网:APNS 需要设备与苹果服务器保持网络连接,若网络不稳定或断开(如用户在中国大陆,苹果服务器偶尔受限),推送可能延迟或丢失。
极光推送与纯 APNS 的区别:
极光推送的关键技术点
(1)Device Token 管理
极光服务器会维护 deviceToken 与用户标识(如 user_id、alias)的映射关系,使开发者可以按业务逻辑推送,而无需直接处理 deviceToken。
如果用户卸载应用或更换设备,极光会检测并更新无效的 deviceToken。
总结
- 极光推送在 iOS 上仍然是基于 APNS 的封装,没有绕过苹果的限制。
- 相比直接使用 APNS,极光提供了更便捷的 API、用户标签管理、推送统计等功能。
- 在 Android 上,极光可以替代 FCM 使用长连接,但在 iOS 上必须走 APNS。
- 如果你的应用只需要基础推送功能,可以直接用 APNS;如果需要更高级的功能(如统计、分组推送),极光是一个不错的选择。