1.SDK体积优化,无用代码检测怎么做的?
2.加载速度优化,怎么发现的多次刷新?
3.高考项目
4.循环引用怎么解决?NSTimer 为什么用 weak 不能解决循环引用
5.Swift 中 weak 和 unowned 的区别?什么场景下用 unowned?或者说 unowned 有什么好处?
6.什么场景下用分类?Category 能添加属性吗?为什么不能添加成员变量?苹果为什么这么设计?
7.为什么要设计元类?
8.SDK中有个类有私有成员变量,怎么改它的值?
9.数组的方法被 hook 了,怎么让有些数组不生效,使用系统的默认方法
参考答案
1.SDK体积优化,无用代码检测怎么做的?
通过使用otool工具对编译产生的Mach-O文件,结合项目源码进行分析。
第一步:
找到项目中所有的protocol
中的方法<包括系统<UITabaleViewDelegate>
的和项目的>
第二步:
通过Mach-O,找到项目中所有被引用的方法
第三步:
通过Mach-O,找到项目中所有的方法
第四步:
遍历第三步查出的所有方法,如果当前方法不在代理方法集合,也不在引用方法集合中,那么就认为是没有用到的方法。
参考链接:简书-iOS 脚本查看项目未使用到的方法
2.加载速度优化,怎么发现的多次刷新
3.高考项目
4.循环引用怎么解决?NSTimer为什么用weak不能解决循环引用
解决循环引用的方法
1. 使用 weak
修饰符
在使用闭包时,可以使用weak
或unowned
关键字来解决循环引用问题。
1 | __weak typeof(self) weakSelf = self; |
在Swift中:
1 | weak var weakSelf = self |
2. 使用 delegate
和 weak
修饰符
在使用委托模式时,可以将委托声明为weak
。
1 | @property (nonatomic, weak) id<MyDelegate> delegate; |
在Swift中:
1 | weak var delegate: MyDelegate? |
3. 使用 __block
修饰符
在某些情况下,可以使用__block
修饰符来解决循环引用问题。
1 | __block typeof(self) blockSelf = self; |
为什么 NSTimer
使用 weak
不能解决循环引用
NSTimer
是一个常见的导致循环引用的源头,因为它会强引用其目标对象。即使你在目标对象中使用weak
修饰符,NSTimer
仍然会强引用目标对象,导致循环引用。
使用 NSTimer
的正确方法
- 使用中介对象
你可以创建一个中介对象,这个对象不会强引用目标对象,从而打破循环引用。
1 | @interface TimerProxy : NSObject |
- 使用
GCD
定时器
GCD
提供了一种无需手动管理循环引用的定时器。
1 | dispatch_queue_t queue = dispatch_get_main_queue(); |
在Swift中:
1 | let queue = DispatchQueue.main |
5.swift中weak和unowned的区别?什么场景下用unowned?或者说unowned有什么好处?
在Swift中,weak
和 unowned
是两种用于解决强引用循环(retain cycle)问题的引用类型修饰符。虽然它们都可以用来打破循环引用,但它们的行为和使用场景有所不同。
weak
和 unowned
的区别
weak
可选类型:
weak
修饰的引用会被自动设置为nil
当其引用的对象被释放时。因此,weak
引用必须是可选类型(Optional)。自动解除引用:使用
weak
修饰符可以防止强引用循环,因为当被引用对象被释放时,weak
引用会自动变成nil
。用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class Person {
var name: String
weak var spouse: Person?
init(name: String) {
self.name = name
}
}
var john: Person? = Person(name: "John")
var jane: Person? = Person(name: "Jane")
john?.spouse = jane
jane?.spouse = john
john = nil
// At this point, jane?.spouse is automatically set to nil
unowned
非可选类型:
unowned
修饰的引用在其引用的对象被释放后不会自动变成nil
,并且它必须是非可选类型(Non-Optional)。无自动解除引用:使用
unowned
修饰符不会自动变成nil
,如果你尝试访问一个已经被释放的对象,会导致运行时崩溃。用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26class Person {
var name: String
var car: Car?
init(name: String) {
self.name = name
}
}
class Car {
var model: String
unowned var owner: Person
init(model: String, owner: Person) {
self.model = model
self.owner = owner
}
}
var john: Person? = Person(name: "John")
var johnsCar: Car? = Car(model: "Tesla", owner: john!)
john?.car = johnsCar
john = nil
// At this point, accessing johnsCar?.owner will cause a runtime crash because john has been deallocated
使用场景
什么时候使用 weak
- 当引用的对象可能在其生命周期中变为
nil
时。 - 常见的场景包括委托(delegates)、UI元素的引用(例如:视图控制器对视图的引用),以及任何你预计会变为
nil
的引用。
什么时候使用 unowned
- 当引用的对象在其生命周期中不会变为
nil
,并且你确定引用的对象会一直存在直到被引用对象的生命周期结束。 - 常见的场景包括:两个对象具有相同的生命周期,或一个对象的生命周期完全包含另一个对象的生命周期。
- 例如,闭包对
self
的引用可以使用unowned
修饰符,因为闭包的生命周期通常与其持有者相同或更短。
unowned
的好处
- 性能:由于
unowned
引用不会自动设置为nil
,它的性能稍微优于weak
引用,因为不需要额外的nil检查。 - 非可选类型:
unowned
引用不需要解包,因为它是非可选类型,这可以简化代码。
然而,使用 unowned
引用时需要非常小心,因为如果引用的对象被释放后再访问该引用,会导致运行时崩溃。一般来说,除非你非常确定引用的对象在其生命周期中不会变为 nil
,否则建议使用 weak
引用。
6.什么场景下用分类?Category能添加属性吗?为什么不能添加成员变量?苹果为什么这么设计?
在 Objective-C 中,Category
是用于为现有类添加方法的一种机制,但它不能直接添加成员变量(实例变量)。不过,可以通过一些间接方式为 Category
添加属性。让我们深入探讨一下为什么 Category
不能添加成员变量、苹果为什么设计成这样,以及如何在 Category
中添加属性。
为什么 Category
不能添加成员变量
Category
主要设计目的是为了扩展已有类的功能,而不是修改类的内部结构。添加成员变量会涉及到修改类的内存布局,这在运行时会带来很多复杂性和潜在的问题。
- 内存布局问题:Objective-C 的内存布局是在编译时确定的,而成员变量会影响对象的内存布局。如果允许在
Category
中添加成员变量,那么就意味着当一个新Category
被加载时,需要重新计算和调整所有实例的内存布局,这在运行时是非常复杂和低效的。 - 二进制兼容性:允许
Category
添加成员变量会破坏二进制兼容性。假设一个类库在某个版本中添加了一个包含成员变量的Category
,而在早期版本中没有这个Category
,那么在不同版本之间切换时可能会导致崩溃或未定义行为。 - 动态加载:Objective-C 支持动态加载类和
Category
。如果Category
能够添加成员变量,那么动态加载一个新的Category
时,需要重新分配和调整现有对象的内存,这在设计和实现上是非常困难的。
苹果为什么这么设计
苹果设计 Category
的目的是为了提供一种灵活且轻量级的方式来扩展类的功能,而不改变类的基本结构。这样设计的主要原因包括:
- 简化实现:不允许
Category
添加成员变量使得内存管理和对象模型的实现更加简单和可靠。 - 动态特性:Objective-C 是一种动态语言,
Category
提供了在运行时扩展类功能的能力,而无需重新编译类文件。这种灵活性是Objective-C的一个重要特性。 - 稳定性和兼容性:通过限制
Category
的能力,可以确保类的内存布局在整个应用程序生命周期内保持稳定,从而提高程序的稳定性和兼容性。
如何在 Category
中添加属性
虽然 Category
不能直接添加成员变量,但可以通过关联对象(Associated Objects)来间接实现属性的添加。
1. 使用关联对象
你可以使用 objc_setAssociatedObject
和 objc_getAssociatedObject
函数来为 Category
添加属性。
1 | #import <objc/runtime.h> |
在上面的代码中,我们使用了关联对象来为 Category
添加属性。objc_setAssociatedObject
函数将一个对象与一个键关联起来,而 objc_getAssociatedObject
函数则根据键获取关联的对象。
2. 关联对象策略
objc_setAssociatedObject
的第四个参数是一个关联策略,常见的策略包括:
OBJC_ASSOCIATION_ASSIGN
:相当于assign
属性。OBJC_ASSOCIATION_RETAIN_NONATOMIC
:相当于strong
非原子属性。OBJC_ASSOCIATION_COPY_NONATOMIC
:相当于copy
非原子属性。OBJC_ASSOCIATION_RETAIN
:相当于strong
原子属性。OBJC_ASSOCIATION_COPY
:相当于copy
原子属性。
结论
虽然 Category
不能直接添加成员变量,但通过关联对象,我们可以间接地为 Category
添加属性。这种设计使得 Category
的实现更加简单和稳定,同时保留了扩展类功能的灵活性。苹果选择不允许 Category
添加成员变量,是为了简化内存管理、提高运行时性能和确保二进制兼容性。
7.为什么要设计元类?
8.SDK中有个类有私有成员变量,怎么改它的值?
9.数组的方法被hook了,怎么让有些数组不生效,使用系统的默认方法