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了,怎么让有些数组不生效,使用系统的默认方法