知识点总结


1. 内存管理

  1. autoreleasepool,一个,还是多个。
  2. autoreleasepool 与线程的关系
  3. assign, strong, weak, copy, retain, nonatomic, atomic
  4. MRC 与 ARC 环境下重写 getter 和 setter 方法
  5. weak 属性的对象是如何在对象释放之后,指向 nil 的
  6. __unsafe_unretained__strong__weak__autoreleasing

2. 多线程

  1. GCD, NSThread, NSOperation 区别,对比
  2. 线程和进程
  3. 2.3 单例

3. Runloop

  1. Runloop 与线程的关系

4. Runtime

  1. 消息机制,内部的缓存机制
  2. 黑魔法
  3. ivar 和 property

5. Block

  1. 三种block,原理
  2. 作为成员变量时的修饰,strong,copy
  3. __weak 和 __block 的区别
  4. block 和函数指针的区别
  5. 循环引用

6. KVO

  1. KVC
  2. KVO
  3. KVO 系统是怎么知道监听的对象发生了变化
  4. 如何手动触发 KVO

7. Category 分类

  1. 原理
  2. 扩展 @property
  3. 加载顺序

8. UIView

  1. UIView 与 CALayer 区别
  2. 层级问题
  3. 事件响应链
  4. 绘图架构
  5. 图文混排
  6. CoreText 原理
  7. 动画

9. UIViewController

  1. 生命周期

10. 网络

  1. TCP 协议
  2. GET 与 POST
  3. 三次握手
  4. HTTPS 原理

11. 其它

  1. load 加载的顺序, initialize, 执行时间。
  2. main 函数执行后,系统做了什么
  3. 性能优化 UITableview 滑动优化
  4. 架构 MVC MVVM
  5. 设计模式类问题
  6. 字典的工作原理,系统如何从若干数据中快速读取 Value
  7. NSNotification 和 Delegate 的区别
  8. 方法返回值类型中,id,instancetype,NsObject* 有什么区别
  9. Git & SVN 有什么不同
  10. static & const

12. 算法相关

  1. 不用临时变量怎么实现 swap(a, b)——用加法或者异或都可以
  2. 二维有序数组查找数字——剑指 offer 第 3题
  3. 亿级日志中,查找登陆次数最多的十个用户——(不确定对不对,我的思路是)先用哈希表保存登陆次数和ID,然后用红黑树保存最大的十个数。剑指 offer 第 30题
  4. 简述排序算法——快排,partion函数的原理,堆排(不稳定),归并排序,基数排序。

解答

1.1 autoreleasepool,一个,还是多个。

关键词 事件循环,autoreleasepol 调用 autorelease 时,对象的引用计数不会马上递减,而是先将对象放进自动释放池,通常会在下一次事件循环时递减。 因为自动释放池中的释放操作要等到下一次事件循环时才会执行。

1.2 autoreleasepool 与线程的关系

1.3 assign, strong, weak, copy, retain, nonatomic, atomic

  1. assign : 针对 “纯量类型” 进行简单的赋值操作。可以用于非 OC 对象
  2. strong :表明了一种 “拥有关系”,为这种属性设置新值时,设置方法会先保留新值,并释放旧值,然后再把新值赋上去。
  3. weak :此特质表明了一种 “非拥有关系”,为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。与 assign 类似,然而在属性所指的对象遭到摧毁时,属性值也会清空。必须用于 OC 对象
  4. copy :与 strong 类似,然而设置方法,并不保留新值,而是将其 拷贝。常用此来保护其封装性。
  5. retain :
  6. atomic:原子属性,实际并不能保证线程安全
  7. nonatomic:非原子属性

1.4 MRC 与 ARC 环境下重写 getter 和 setter 方法

MRC

1
2
3
4
5
6
7
8
9
10
11
12
- (void)setStr:(NSString *)str {
if (_str != str) {
[str retain]; // [str copy];
[_str release];
_str = str;
}

}

- (NSString *)str {
return [[_str retain] autorelease];
}

ARC

1
2
3
4
5
6
7
8
9
10
- (void)setStr:(NSString *)str {
if (_str != str) {
_str = str ;
}

}

- (NSString *)str {
return _str;
}

1.5 weak 属性的对象是如何在对象释放之后,指向 nil 的

1.6 __strong__wea__unsafe_unretained__autoreleasing

__strong : 默认语义,表示保留此值
__weak : 不保留此值,变量可以安全使用,系统如果把这个对象回收了,那么该变量也会自动清空(指向 nil )
__unsafe_unretained : 与 __weak 类似,但是该变量不会自动清空,不安全
__autoreleasing :


2.1 GCD, NSThread, NSOperation 区别,对比

2.2 线程和进程

2.3 单例

dispatch_once 与 static 的区别
dispatch_once 线程安全的,性能较高
static 使用原子锁保证线程安全,但每次获取都需要获取锁,性能较差

1
2
3
4
5
6
7
8
+ (instancetype)shareManager {
static Dispatch *obj;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
obj = [[Dispatch alloc] init];
});
return obj;
}
1
2
3
4
5
6
7
8
9
+ (instancetype)shareManager {
static Static *obj;
@synchronized (self) { // 加锁为了防止多个线程操作
if (!obj) {
obj = [[Static alloc] init];
}
}
return obj;
}

3.1 Runloop 与线程的关系

NSRunLoop 基于 CFRunLoopRef 的封装,提供了面向对象的 API,但不是线程安全的。
CFRunLoopRef 在 CoreFoundation 框架内,提供了纯 C 函数的 API,所有 API 都是线程安全的。

线程和 RunLoop 之间是一一对应的,其对应关系是保存在一个全局的 Dictionary 里。
线程刚创建时并没有 RunLoop,如果你不主动获取,那它一直都不会有。
RunLoop 的创建是发生在第一次获取时,RunLoop 的销毁是发生在线程结束时。
你只能在一个线程的内部获取其 RunLoop (主线程除外)。

一个 RunLoop 包含若干个 Mode,每个 Mode 又包含若干个 Source/Time/Observer。每次调用 RunLoop 的主函数时,只能指定其中的一个 Mode,这个 Mode 成为 CurrentMode。如果需要切换 Mode,只能退出 Loop,再重新指定一个 Mode 进入。

CFRunLoopSourceRef 是事件产生的地方。Source 有两个版本?:Source0 和 Source1。

  • Source0 只包含了一个回调(函数指针),它并不能主动触发事件。使用时需要先调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件。
  • Source1 包含了一个 mach_port 和一个回调(函数指针),被用于通过内核和其它线程相互发送消息。这个 Source 能主动唤醒 RunLoop 的线程。

CFRunLoopTimerRef 是基于时间的触发器。和 NSTimer 无缝桥接,可以混用。其包含一个时间长度和一个回调(函数指针)。当其加入到 RunLoop 时,RunLoop 会注册对应的时间点,当到达那个时间点,RunLoop 会被唤醒以执行那个回调。

CFRunLoopObserverRef 是观察者,每个 Observer 都包含一个回调(函数指针)。当 RunLoop 的状态发生变化时,观察者就能通过这个回调接收到这个变化。

以上 Source/Timer/Observer 被称为 Mode item,一个 item 可以被同时加入到多个 mode。但同一个 item 被重复加入同一个 mode 时是不会有效果的。如果一个 mode 中一个 item 都没有,则 RunLoop 会直接退出,不进入循环

RunLoop 启动后,系统默认注册了5个 Mode:

  1. kCFRunLoopDefaultMode:APP 的默认 mode,通常主线程是在这个 Mode 下运行的。
  2. UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响。
  3. UIInitializationRunLoopMode:在刚启动 APP 时进入的第一个 Mode,启动完成后就不再使用。
  4. GSEventReceiveRunLoopMode:接收系统事件的内部 Mode,通常用不到。
  5. kCFRunLoopCommonModes:这是一个占位的 Mode,没有实际作用。

相关链接:
ibireme:深入理解RunLoop , bestswifter:深入研究 Runloop 与线程保活 , 杨潇玉:Threading Programming Guide


4.1 消息机制,内部的缓存机制

4.2 黑魔法

4.3 ivar 和 property

property 本质由 ivar + getter + setter 构成
ivar 实例变量,getter + setter 存取方法


5.1 三种block,原理

5.2 作为成员变量时的修饰,strong,copy

5.3 __weak 和 __block 的区别

5.4 block 和函数指针的区别

5.6 循环引用


6.1 KVC

KVC 是一种基于 NSKeyValueCoding 非正式协议的机制,能让我们用一个或一串字符串标识符去访问,操作类的属性。

1
2
3
4
5
6
- (nullable id)valueForKey:(NSString *)key;
- (void)setValue:(nullable id)value forKey:(NSString *)key;

- (nullable id)valueForKeyPath:(NSString *)keyPath;
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;

通过这些方法加上正确的标识符(一般与属性同名),可以直接获取或设置类的属性。甚至可以越过多个类的层级结构,直接获取目标属性。

还有对集合操作的方法,直接获取到集合属性的同时,对其求和、取平均数,取最大最小值等操作。

6.2 KVO

KVO 是 Cocoa 提供的一种基于 KVC 的 键值观察 机制,允许一个对象去监听另一个对象的某个属性,当该对象改变时系统通知监听的对象。

6.3 KVO 系统是怎么知道监听的对象发生了变化

当某个类的对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类(类名就是在该类的前面加上 NSKVONotifying_ 前缀),在这个派生类中重写基类中任何被观察属性的 setter 方法。

派生类在被重写的 setter 方法实现真正的通知机制,就如前面手动实现键值观察那样,调用 willChangeValueForKey:didChangeValueForKey: 方法。这么做是基于设置属性会调用 setter 方法,而通过重写就获得了 KVO 需要的通知机制。当然前提是要通过遵循 KVO 的属性设置方式来变更属性值,如果仅是直接修改属性对应的成员变量,是无法实现 KVO 的。

同时派生类还重写了 class 方法以“欺骗”外部调用者它就是起初的那个类。然后系统将这个对象的 isa 指针指向这个新诞生的派生类,因此这个对象就成为该派生类的对象了,因而在该对象上对 setter 的调用就会调用重写的 setter,从而激活键值通知机制。此外,派生类还重写了 dealloc 方法来释放资源。

相关阅读:谈谈 KVO Objective-C中的KVC和KVO

6.4 如何手动触发 KVO

重写 + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key 来过滤通知
调用 - (void)willChangeValueForKey:(NSString *)key - (void)didChangeValueForKey:(NSString *)key;


7.1 Category 原理

7.2 Category 扩展 @property

7.3 Category 加载顺序


8.1 UIView 与 CALayer 区别

8.2 UIView 层级问题

8.3 事件响应链

8.4 绘图架构

8.5 图文混排

8.6 CoreText 原理

8.7 动画


9.1 UIViewController 生命周期


10.1 TCP 协议

10.2 GET 与 POST

10.3 三次握手

10.4 HTTPS 原理


11.1 load 加载的顺序, initialize, 执行时间。

load 当文件被装载时调用, main 函数之前。自动调用,不可手动调用。
加载顺序为 complie source 中的文件顺序,父类-> 子类-> 分类。
子类没有实现 load 方法,就不会调用父类的。
内部使用了锁,线程安全的,避免阻塞线程。
常用于 Method Swizzle

initialize 实例化对象之前调用,只会调用一次,main 函数之后,自动调用。
即使子类没有实现 initialize 方法,仍会调用父类的方法。
常用于初始化全局变量或静态变量。

11.2 main 函数执行后,系统做了什么

11.3 性能优化 UITableview 滑动优化

11.4 架构 MVC MVVM

11.5 设计模式类问题

11.6 字典的工作原理,系统如何从若干数据中快速读取 Value

11.7 NSNotification 和 Delegate 的区别

方法返回值类型中,id,instancetype,NObject* 有什么区别

id : 万能指针,可以指向任意对象类型,编译器不会做类型检。
instancetype : 作为一个拥有关联类型的方法的返回类型,这些方法的返回结果以当前所在的类为类型。
NSObject* : 并不是所有对象都是继承自 NSObject,如 NSProxy,所以并不能代指所有类型。

问:为什么构造方法要用 instancetype 取代 id

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
26
27
28
29
30
31
32
33
34
35
36
37
38
// 父类,分别创建两个构造方法
@interface Person:NSObject
+ (id)person1;
+ (instancetype)person2;
@end
@implementation Person
+ (id)person1 {
return [[self alloc] init];
}
+ (instancetype)person2 {
return [[self alloc] init];
}
@end

// 子类,创建一个实例方法
@interface Student:Person
- (void)hello;
@end

@implementation Student
- (void)hello {
NSLog(@"Hello World");
}
@end

// main 函数分别调用
int main(int argc, const char *argv[])
{
@autoreleasepool
{
[[Student person1] hello]; // 编译正常,运行正常
[[Student person2] hello]; // 编译正常,运行正常
[[Person person1] hello]; // 编译正常,运行crash (unrecognized selector sent to instance)
[[Person person2] hello]; // 编译失败 (No visible @interface for 'Person' declares the selector 'hello')

}
}

好处:id 可以指向任意类型,编译器不会做类型检测,即使我们调用了一个不属于该类的方法,仍然可以正常编译。 instancetype 可以更好的识别出类型,帮助我们在编译阶段发现问题。

11.9 Git & SVN 有什么不同

SVN 可以 lock 文件,SVN 有版本号,SVN 强制所有人提交代码前对齐基线,SVN 不可以离线log

11.10 static & const


12.1 不用临时变量怎么实现 swap(a, b)——用加法或者异或都可以

12.2 二维有序数组查找数字——剑指 offer 第 3题

12.3 亿级日志中,查找登陆次数最多的十个用户——(不确定对不对,我的思路是)先用哈希表保存登陆次数和ID,然后用红黑树保存最大的十个数。剑指 offer 第 30题

12.4 简述排序算法——快排,partion函数的原理,堆排(不稳定),归并排序,基数排序。