iOS 知识点总结
知识点总结
1. 内存管理
- autoreleasepool,一个,还是多个。
- autoreleasepool 与线程的关系
- assign, strong, weak, copy, retain, nonatomic, atomic
- MRC 与 ARC 环境下重写 getter 和 setter 方法
- weak 属性的对象是如何在对象释放之后,指向 nil 的
-
__unsafe_unretained
,__strong
,__weak
,__autoreleasing
2. 多线程
3. Runloop
4. Runtime
5. Block
6. KVO
7. Category 分类
8. UIView
9. UIViewController
10. 网络
11. 其它
- load 加载的顺序, initialize, 执行时间。
- main 函数执行后,系统做了什么
- 性能优化 UITableview 滑动优化
- 架构 MVC MVVM
- 设计模式类问题
- 字典的工作原理,系统如何从若干数据中快速读取 Value
- NSNotification 和 Delegate 的区别
- 方法返回值类型中,id,instancetype,NsObject* 有什么区别
- Git & SVN 有什么不同
- static & const
12. 算法相关
- 不用临时变量怎么实现 swap(a, b)——用加法或者异或都可以
- 二维有序数组查找数字——剑指 offer 第 3题
- 亿级日志中,查找登陆次数最多的十个用户——(不确定对不对,我的思路是)先用哈希表保存登陆次数和ID,然后用红黑树保存最大的十个数。剑指 offer 第 30题
- 简述排序算法——快排,partion函数的原理,堆排(不稳定),归并排序,基数排序。
解答
1.1 autoreleasepool,一个,还是多个。
关键词 事件循环,autoreleasepol 调用 autorelease 时,对象的引用计数不会马上递减,而是先将对象放进自动释放池,通常会在下一次事件循环时递减。 因为自动释放池中的释放操作要等到下一次事件循环时才会执行。
1.2 autoreleasepool 与线程的关系
1.3 assign, strong, weak, copy, retain, nonatomic, atomic
- assign : 针对 “纯量类型” 进行简单的赋值操作。可以用于非 OC 对象
- strong :表明了一种 “拥有关系”,为这种属性设置新值时,设置方法会先保留新值,并释放旧值,然后再把新值赋上去。
- weak :此特质表明了一种 “非拥有关系”,为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。与 assign 类似,然而在属性所指的对象遭到摧毁时,属性值也会清空。必须用于 OC 对象
- copy :与 strong 类似,然而设置方法,并不保留新值,而是将其 拷贝。常用此来保护其封装性。
- retain :
- atomic:原子属性,实际并不能保证线程安全
- nonatomic:非原子属性
1.4 MRC 与 ARC 环境下重写 getter 和 setter 方法
MRC
1 | - (void)setStr:(NSString *)str { |
ARC
1 | - (void)setStr:(NSString *)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 | + (instancetype)shareManager { |
1 | + (instancetype)shareManager { |
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:
- kCFRunLoopDefaultMode:APP 的默认 mode,通常主线程是在这个 Mode 下运行的。
- UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响。
- UIInitializationRunLoopMode:在刚启动 APP 时进入的第一个 Mode,启动完成后就不再使用。
- GSEventReceiveRunLoopMode:接收系统事件的内部 Mode,通常用不到。
- 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 | - (nullable id)valueForKey:(NSString *)key; |
通过这些方法加上正确的标识符(一般与属性同名),可以直接获取或设置类的属性。甚至可以越过多个类的层级结构,直接获取目标属性。
还有对集合操作的方法,直接获取到集合属性的同时,对其求和、取平均数,取最大最小值等操作。
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 | // 父类,分别创建两个构造方法 |
好处: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题