├── MD ├── 内存 │ ├── 基础 │ │ ├── Q1.md │ │ ├── 堆空间申请过程.md │ │ ├── let-var内存布局.md │ │ ├── 引用计数.md │ │ ├── 堆区栈区.md │ │ ├── iOS内存布局.md │ │ ├── retain-release.md │ │ ├── ARC.md │ │ └── 内存对齐.md │ ├── 布局 │ │ ├── Q1.md │ │ ├── 类.md │ │ ├── 结构体.md │ │ ├── 枚举.md │ │ └── 协议.md │ ├── copy-on-write │ │ ├── Q2.md │ │ ├── copy-on-write.md │ │ └── Q1.md │ ├── 循环引用 │ │ ├── Q1.md │ │ ├── SideTable.md │ │ ├── 循环引用.md │ │ ├── weak-unowned.md │ │ └── weak.md │ └── 自动释放池 │ │ ├── autoreleasepool-runloop.md │ │ └── autoreleasepool.md ├── UI │ ├── 事件传递和响应 │ │ ├── Q1.md │ │ ├── 事件.md │ │ └── hitTest.md │ └── 图像 │ │ ├── Q1.md │ │ ├── 图像绘制.md │ │ ├── 离屏渲染.md │ │ └── 卡顿掉帧.md ├── Swift底层本质 │ ├── 特性和优化 │ │ ├── 面向协议编程.md │ │ ├── 反射.md │ │ ├── map.md │ │ ├── filter-reduce.md │ │ ├── Swift编译流程.md │ │ └── 优化.md │ ├── 探究本质 │ │ ├── 存储属性.md │ │ ├── let和var的区别.md │ │ ├── lazy.md │ │ ├── inout.md │ │ ├── inout2.md │ │ ├── 枚举2.md │ │ ├── inout3.md │ │ ├── 观察属性.md │ │ ├── 枚举.md │ │ └── 计算属性.md │ ├── 闭包 │ │ ├── 闭包捕获2.md │ │ ├── 闭包捕获3.md │ │ ├── 逃逸闭包.md │ │ ├── DispatchQueue.md │ │ ├── 闭包捕获.md │ │ └── 闭包.md │ ├── 指针 │ │ └── 指针.md │ ├── 探究本质2 │ │ ├── Option.md │ │ ├── Array.md │ │ └── String.md │ ├── 多态 │ │ ├── 多态.md │ │ ├── 方法派发.md │ │ └── Q1.md │ └── 关键字 │ │ ├── type.md │ │ ├── Any.md │ │ ├── Self和self的区别.md │ │ ├── is.md │ │ ├── throws.md │ │ ├── 权限.md │ │ └── self.md ├── 多线程 │ ├── 基础 │ │ ├── Thread.md │ │ ├── 多线程方案.md │ │ ├── 多线程隐患.md │ │ ├── 死锁.md │ │ ├── 常驻线程.md │ │ ├── 并发.md │ │ ├── 多线程理解.md │ │ ├── 多线程通信.md │ │ └── 进程.md │ ├── 多线程同步和锁 │ │ ├── 锁.md │ │ ├── os_unfair_lock.md │ │ ├── NSLock.md │ │ ├── OSSpinLock.md │ │ ├── 多读单写.md │ │ ├── NSConditionLock.md │ │ ├── 各种锁的理解.md │ │ └── pthread_mutex.md │ ├── GCD │ │ ├── DispatchSourceTimer.md │ │ ├── 栅栏.md │ │ ├── DispatchSource.md │ │ ├── 任务提交.md │ │ ├── 队列2.md │ │ ├── 延迟执行.md │ │ ├── DispatchSemaphore.md │ │ ├── 队列3.md │ │ ├── 死锁.md │ │ ├── DispatchGroup.md │ │ └── 队列.md │ └── PerformSelector │ │ └── PerformSelector.md ├── Runtime │ ├── isa │ │ ├── isa.md │ │ ├── 实例对象、类对象和元类.md │ │ └── 优化.md │ ├── 消息机制 │ │ ├── 动态方法解析.md │ │ ├── 消息转发.md │ │ └── 消息发送.md │ └── 数据结构 │ │ └── 数据结构.md └── RunLoop │ ├── 常见问题 │ ├── PerformSelector.md │ ├── 手势识别.md │ ├── GCD.md │ ├── 事件响应.md │ ├── Timer.md │ ├── AutoreleasePool.md │ └── UI.md │ ├── 基础 │ ├── mode.md │ ├── thread.md │ ├── loop.md │ ├── stop.md │ ├── RunLoop.md │ ├── observer.md │ ├── run.md │ └── source.md │ └── 应用 │ └── 常驻线程.md ├── Image ├── 多线程 │ ├── lock.png │ ├── parallel.jpg │ └── concurrency.jpg ├── Runtime │ ├── isa.png │ ├── msg_send.png │ ├── forwarding.png │ ├── method_resolver.png │ ├── instance-class-meta.jpg │ ├── class_ro_t_method_list.png │ ├── class_rw_t_method_array.png │ └── objc_class_class_data_bits_t.png ├── UI │ ├── hitTest.png │ ├── screen-fps.png │ ├── flow-of-UITouch.png │ └── image-to-screen.png ├── 内存 │ ├── vwt-pwt.png │ ├── stride-padding.png │ ├── stride-nopadding.png │ ├── ios-memory-layout.png │ ├── alignment-internal-2.png │ ├── existential-container.png │ └── existential-container-value-buffer.png ├── RunLoop │ ├── runloop.jpeg │ ├── runloop-loop.jpeg │ ├── runloop-mode.jpeg │ └── runloop-thread.jpeg └── Swift底层本质 │ ├── method-dispatch.png │ └── swift-compilation-process.jpg ├── TODO.md ├── LICENSE └── README.md /MD/内存/基础/Q1.md: -------------------------------------------------------------------------------- 1 | ## 你对 iOS 内存管理的理解? 2 | 3 | -------------------------------------------------------------------------------- /MD/UI/事件传递和响应/Q1.md: -------------------------------------------------------------------------------- 1 | ## 事件传递具体有哪些应用场景? 2 | 3 | -------------------------------------------------------------------------------- /MD/内存/基础/堆空间申请过程.md: -------------------------------------------------------------------------------- 1 | ## Swift 对象堆空间申请过程? 2 | 3 | -------------------------------------------------------------------------------- /MD/内存/布局/Q1.md: -------------------------------------------------------------------------------- 1 | ## Swift 和 OC 类对象内存布局的区别? 2 | 3 | -------------------------------------------------------------------------------- /MD/Swift底层本质/特性和优化/面向协议编程.md: -------------------------------------------------------------------------------- 1 | ## 面向协议编程的理解?对函数式编程的理解? 2 | 3 | -------------------------------------------------------------------------------- /MD/内存/copy-on-write/Q2.md: -------------------------------------------------------------------------------- 1 | ## Swift 对象的深度复制(使用 Codable 协议) 2 | 3 | -------------------------------------------------------------------------------- /MD/内存/基础/let-var内存布局.md: -------------------------------------------------------------------------------- 1 | ## Swift 里 let 和 var 变量的内存布局有何不同? 2 | 3 | -------------------------------------------------------------------------------- /MD/内存/循环引用/Q1.md: -------------------------------------------------------------------------------- 1 | ## 说说循环引用的场景和解决思路?闭包为什么会产生循环引用?手写循环引用例子 2 | 3 | -------------------------------------------------------------------------------- /MD/多线程/基础/Thread.md: -------------------------------------------------------------------------------- 1 | ## NSThread内部实现的原理是什么?启动流程又是怎样的?2 种初始化方法有什么区别? -------------------------------------------------------------------------------- /MD/Swift底层本质/探究本质/存储属性.md: -------------------------------------------------------------------------------- 1 | ## 类型存储属性和 lazy 一样是延迟加载吗?如果一样那么是线程安全的吗?为什么? 2 | 3 | -------------------------------------------------------------------------------- /MD/Runtime/isa/isa.md: -------------------------------------------------------------------------------- 1 | ## isa 的指向关系? 2 | 3 | ![](../../../Image/Runtime/isa.png) -------------------------------------------------------------------------------- /MD/Swift底层本质/闭包/闭包捕获2.md: -------------------------------------------------------------------------------- 1 | ## 捕获多个值时它们在内存中是连续存储的吗? 2 | 3 | 不是,每个局部变量都会在堆空间中创建一个“类” -------------------------------------------------------------------------------- /MD/多线程/多线程同步和锁/锁.md: -------------------------------------------------------------------------------- 1 | ## 多线程同步方案有哪些?哪些锁的性能最好? 2 | 3 | ![](../../../Image/多线程/lock.png) -------------------------------------------------------------------------------- /Image/多线程/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/多线程/lock.png -------------------------------------------------------------------------------- /Image/Runtime/isa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/Runtime/isa.png -------------------------------------------------------------------------------- /Image/UI/hitTest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/UI/hitTest.png -------------------------------------------------------------------------------- /Image/内存/vwt-pwt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/内存/vwt-pwt.png -------------------------------------------------------------------------------- /Image/多线程/parallel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/多线程/parallel.jpg -------------------------------------------------------------------------------- /Image/UI/screen-fps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/UI/screen-fps.png -------------------------------------------------------------------------------- /Image/RunLoop/runloop.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/RunLoop/runloop.jpeg -------------------------------------------------------------------------------- /Image/Runtime/msg_send.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/Runtime/msg_send.png -------------------------------------------------------------------------------- /Image/内存/stride-padding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/内存/stride-padding.png -------------------------------------------------------------------------------- /Image/多线程/concurrency.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/多线程/concurrency.jpg -------------------------------------------------------------------------------- /Image/Runtime/forwarding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/Runtime/forwarding.png -------------------------------------------------------------------------------- /Image/UI/flow-of-UITouch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/UI/flow-of-UITouch.png -------------------------------------------------------------------------------- /Image/UI/image-to-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/UI/image-to-screen.png -------------------------------------------------------------------------------- /Image/内存/stride-nopadding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/内存/stride-nopadding.png -------------------------------------------------------------------------------- /MD/内存/循环引用/SideTable.md: -------------------------------------------------------------------------------- 1 | ## 在 SideTable 里的存取过程又是怎样的?Side Table 的组成?为什么有多张 Side Table?Side Table 为什么会有一把自旋锁? 2 | 3 | -------------------------------------------------------------------------------- /Image/RunLoop/runloop-loop.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/RunLoop/runloop-loop.jpeg -------------------------------------------------------------------------------- /Image/RunLoop/runloop-mode.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/RunLoop/runloop-mode.jpeg -------------------------------------------------------------------------------- /Image/内存/ios-memory-layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/内存/ios-memory-layout.png -------------------------------------------------------------------------------- /Image/RunLoop/runloop-thread.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/RunLoop/runloop-thread.jpeg -------------------------------------------------------------------------------- /Image/Runtime/method_resolver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/Runtime/method_resolver.png -------------------------------------------------------------------------------- /Image/内存/alignment-internal-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/内存/alignment-internal-2.png -------------------------------------------------------------------------------- /Image/内存/existential-container.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/内存/existential-container.png -------------------------------------------------------------------------------- /Image/Runtime/instance-class-meta.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/Runtime/instance-class-meta.jpg -------------------------------------------------------------------------------- /Image/Swift底层本质/method-dispatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/Swift底层本质/method-dispatch.png -------------------------------------------------------------------------------- /Image/Runtime/class_ro_t_method_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/Runtime/class_ro_t_method_list.png -------------------------------------------------------------------------------- /Image/Runtime/class_rw_t_method_array.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/Runtime/class_rw_t_method_array.png -------------------------------------------------------------------------------- /MD/Swift底层本质/探究本质/let和var的区别.md: -------------------------------------------------------------------------------- 1 | ## let 和 var 的区别 2 | 3 | ### let 4 | 5 | let 用于修饰不可变变量 6 | 7 | ### var 8 | 9 | var 用于修饰可变变量 10 | -------------------------------------------------------------------------------- /MD/Swift底层本质/闭包/闭包捕获3.md: -------------------------------------------------------------------------------- 1 | ## 一个捕获到 Int 值的闭包在内存中占几个字节? 2 | 3 | 占用 32 个字节,实际使用 24 个字节。 4 | 5 | 1-8 为类信息 6 | 7 | 9-16 为引用计数 8 | 9 | 17-24 为 Int 所占的空间 -------------------------------------------------------------------------------- /Image/Runtime/objc_class_class_data_bits_t.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/Runtime/objc_class_class_data_bits_t.png -------------------------------------------------------------------------------- /Image/Swift底层本质/swift-compilation-process.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/Swift底层本质/swift-compilation-process.jpg -------------------------------------------------------------------------------- /Image/内存/existential-container-value-buffer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RayJiang16/Swift-Review/HEAD/Image/内存/existential-container-value-buffer.png -------------------------------------------------------------------------------- /MD/Swift底层本质/探究本质/lazy.md: -------------------------------------------------------------------------------- 1 | ## lazy 属性可以用 let 修饰吗?lazy 属性是线程安全的吗? 2 | 3 | ### lazy 属性可以用 let 修饰吗? 4 | 5 | 不可以 6 | 7 | ### lazy 属性是线程安全的吗? 8 | 9 | 不是 10 | -------------------------------------------------------------------------------- /MD/Swift底层本质/指针/指针.md: -------------------------------------------------------------------------------- 1 | ## Swift 里有那几种类型的指针?有什么区别? 2 | 3 | 未总结,详情见 Reference 4 | 5 | 6 | 7 | ### Reference 8 | 9 | https://www.jianshu.com/p/103591adc3d1 10 | -------------------------------------------------------------------------------- /MD/Swift底层本质/探究本质/inout.md: -------------------------------------------------------------------------------- 1 | ## inout 修饰的函数参数本质是什么? 2 | 3 | 本质是将变量的地址传递过去 4 | 5 | 6 | 7 | ### Reference 8 | 9 | https://juejin.im/post/5d5c0010e51d456201486e4c#heading-5 10 | -------------------------------------------------------------------------------- /MD/内存/循环引用/循环引用.md: -------------------------------------------------------------------------------- 1 | ## 对循环引用的理解?强引用和弱引用的区别? 2 | 3 | ### 对循环引用的理解? 4 | 5 | 两个实例对象互相强引用对方,就会产生循环引用。 6 | 7 | 8 | 9 | ### 强引用和弱引用的区别? 10 | 11 | 强引用是持有对象,并使被持有的对象引用计数+1 12 | 13 | 弱引用是持有对象,但是被持有的对象引用计数不变 -------------------------------------------------------------------------------- /MD/Swift底层本质/特性和优化/反射.md: -------------------------------------------------------------------------------- 1 | ## 对反射机制的理解? 2 | 3 | Swift 的反射机制是基于一个叫 **Mirror** 的 `struct` 来实现的。你为具体的 `subject` 创建一个 `Mirror`,然后就可以通过它查询这个对象 `subject` 。 4 | 5 | 6 | 7 | ### Reference 8 | 9 | https://www.jianshu.com/p/0192553b14e0 10 | -------------------------------------------------------------------------------- /MD/Swift底层本质/闭包/逃逸闭包.md: -------------------------------------------------------------------------------- 1 | ## 逃逸闭包是什么? 2 | 3 | 当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。当你定义接受闭包作为参数的函数时,你可以在参数名之前标注 @escaping,用来指明这个闭包是允许“逃逸”出这个函数的。 4 | 5 | 6 | 7 | ### Reference 8 | 9 | https://www.jianshu.com/p/d386392fa8c0 10 | -------------------------------------------------------------------------------- /MD/多线程/GCD/DispatchSourceTimer.md: -------------------------------------------------------------------------------- 1 | ## DispatchSourceTimer 和 Timer 比哪个更精准的? 2 | 3 | `DispatchSourceTimer` 更精准,因为 `Timer` 会受 `RunLoop` 影响,如果 `RunLoop` 要处理的任务很多,会导致 `Timer` 的精度下降。 4 | 5 | 6 | 7 | ### Reference 8 | 9 | https://www.jianshu.com/p/faa6ffe4fac3 -------------------------------------------------------------------------------- /MD/多线程/GCD/栅栏.md: -------------------------------------------------------------------------------- 1 | ## barrier 的理解? 2 | 3 | ### barrier 的理解? 4 | 5 | 栅栏任务的主要特性是可以对队列中的任务进行阻隔,执行栅栏任务时,它会先等待队列中已有的任务全部执行完成,然后它再执行,在它之后加入的任务也必须等栅栏任务执行完后才能执行。 6 | 7 | 8 | 9 | ### Reference 10 | 11 | https://juejin.im/post/5acaea17f265da239a601a01#heading-12 -------------------------------------------------------------------------------- /MD/Swift底层本质/探究本质2/Option.md: -------------------------------------------------------------------------------- 1 | ## 可选类型的本质? 2 | 3 | `Optional` 的本质是一个枚举。“?” 是 Apple 提供的语法糖 4 | 5 | 6 | 7 | ### Code 8 | 9 | ```swift 10 | public enum Optional : ExpressibleByNilLiteral { 11 | case none 12 | case some(Wrapped) 13 | } 14 | ``` 15 | 16 | -------------------------------------------------------------------------------- /MD/Runtime/isa/实例对象、类对象和元类.md: -------------------------------------------------------------------------------- 1 | ## 实例对象、类对象和元类对象的联系和区别有哪些? 2 | 3 | ![](../../../Image/Runtime/instance-class-meta.jpg) 4 | 5 | 当发送消息给实例对象时,消息是在寻找这个对象的类的方法列表(实例方法) 6 | 7 | 当发送消息给类对象时,消息是在寻找这个类的元类的方法列表(类方法) 8 | 9 | 10 | 11 | ### Reference 12 | 13 | https://www.jianshu.com/p/40c0ca04fb20 -------------------------------------------------------------------------------- /MD/Swift底层本质/闭包/DispatchQueue.md: -------------------------------------------------------------------------------- 1 | ## DispatchQueue.async 闭包体内为什么要强制加 self. 访问成员变量? 2 | 3 | 所有逃逸闭包都需要加 `self.` 访问成员变量,`self` 用于保留/捕获要发送消息的对象的实例。 4 | 5 | 6 | 7 | ### Reference 8 | 9 | https://stackoverflow.com/questions/53471017/why-escaping-closures-require-us-to-refer-to-self-explicitly 10 | -------------------------------------------------------------------------------- /MD/Runtime/消息机制/动态方法解析.md: -------------------------------------------------------------------------------- 1 | ## 动态方法解析阶段的过程? 2 | 3 | ![](../../../Image/Runtime/method_resolver.png) 4 | 5 | 1. 先走完消息发送的流程,如果找到直接调用。 6 | 2. 没有找到方法就进行动态方法解析,这时可以动态增加方法 7 | 3. 再走一遍消息发送的流程,如果找到就调用。 8 | 4. 再次没有找到方法就进入消息转发流程。 9 | 10 | 11 | 12 | ### Reference 13 | 14 | https://www.jianshu.com/p/198f031f44ea -------------------------------------------------------------------------------- /MD/Swift底层本质/探究本质/inout2.md: -------------------------------------------------------------------------------- 1 | ## inout 参数能传递计算属性吗?传递计算属性的底层原理是什么? 2 | 3 | ### inout 参数能传递计算属性吗? 4 | 5 | 可以传递计算属性 6 | 7 | ### 传递计算属性的底层原理是什么? 8 | 9 | 传递的是变量的地址,不过地址通过 `getter` 之后,将地址传递到函数内部的。 10 | 11 | 12 | 13 | ### Reference 14 | 15 | https://juejin.im/post/5d5c0010e51d456201486e4c#heading-5 16 | -------------------------------------------------------------------------------- /MD/Swift底层本质/探究本质2/Array.md: -------------------------------------------------------------------------------- 1 | ## 数组在内存中占多少个字节?数组存储在栈空间还是堆空间? 2 | 3 | ### 数组在内存中占多少个字节? 4 | 5 | 数组占 8 字节,指向了存储数据的真实地址。默认数组大小是 4,负载超过 0.5 则进行扩容,扩容系数是 2。 6 | 7 | ### 数组存储在栈空间还是堆空间? 8 | 9 | 未知 10 | 11 | 12 | 13 | ### Reference 14 | 15 | https://juejin.im/post/5d5c0010e51d456201486e4c#heading-12 16 | -------------------------------------------------------------------------------- /MD/多线程/GCD/DispatchSource.md: -------------------------------------------------------------------------------- 1 | ## DispatchSource 的理解? 2 | 3 | GCD 中提供了一个 `DispatchSource` 类,它可以帮你监听系统底层一些对象的活动,例如这些对象: `Mach port`、`Unix descriptor`、`Unix signal`、`VFS node`,并允许你在这些活动发生时,向队列提交一个任务以进行异步处理。 4 | 5 | 6 | 7 | 8 | ### Reference 9 | 10 | https://juejin.im/post/5acaea17f265da239a601a01#heading-29 -------------------------------------------------------------------------------- /MD/多线程/GCD/任务提交.md: -------------------------------------------------------------------------------- 1 | ## GCD 任务提交方式有哪些?DispatchWorkItem 提交有什么好处? 2 | 3 | ### GCD 任务提交方式有哪些? 4 | 5 | 1. DispatchGroup 6 | 2. DispatchWorkItem 7 | 8 | 9 | 10 | ### DispatchWorkItem 提交有什么好处? 11 | 12 | 可以使代码更加简洁,它将业务内聚,将模块分离。 13 | 14 | 15 | 16 | ### Reference 17 | 18 | https://www.jianshu.com/p/65c333777571 -------------------------------------------------------------------------------- /MD/多线程/GCD/队列2.md: -------------------------------------------------------------------------------- 1 | ## GCD 有哪几种队列?主队列和全局队列分别是什么队列? 2 | 3 | ### GCD 有哪几种队列? 4 | 5 | 2种队列 6 | 7 | 串行队列: 队列中的任务在当前线程中依次执行,后面追加的任务会等到前面追加的任务执行完了才开始执行,不开新线程。 8 | 9 | 并发队列:并发队列能够让多个任务(同一时候)运行,它会自己主动开启多个线程同一时候运行任务,并发功能仅仅有在异步函数(`async`)下才会有效。 10 | 11 | 12 | 13 | ### 主队列和全局队列分别是什么队列? 14 | 15 | 主队里是串行队列 16 | 17 | 全局队列是并发队列 -------------------------------------------------------------------------------- /MD/UI/图像/Q1.md: -------------------------------------------------------------------------------- 1 | ## 哪些场景会触发离屏渲染?怎么解决? 2 | 3 | - shouldRasterize(光栅化) 4 | - masks(遮罩) 5 | - shadows(阴影) 6 | - edge antialiasing(抗锯齿) 7 | - group opacity(不透明) 8 | - 复杂形状设置圆角等 9 | - 渐变 10 | 11 | 设置圆角可以通过 `UIBezierPath` 画一个中间透明的圆,达到圆角的效果。 12 | 13 | 14 | 15 | ### Reference 16 | 17 | https://www.jianshu.com/p/ca51c9d3575b 【建议阅读】 18 | -------------------------------------------------------------------------------- /MD/内存/基础/引用计数.md: -------------------------------------------------------------------------------- 1 | ## 引用计数的存储方式? 2 | 3 | 引用计数可以直接存储在优化过的 `isa` 指针中,也可能存储在 `SideTable` 类中。 4 | 如果对象地址为一个 `Tagged Pointer`,那么则没有引用计数, 5 | 如果为一个优化过的指针且 `has_sidetable_rc = 0`,则引用计数存储在 `isa` 结构的 `extra_rc` 中。 6 | 其他情况下,基本是引用计数存在 `sideTable` 中的` refcnts` 中。 7 | 8 | 9 | 10 | ### Reference 11 | 12 | https://www.jianshu.com/p/a2c99437ac8c 13 | -------------------------------------------------------------------------------- /MD/Swift底层本质/多态/多态.md: -------------------------------------------------------------------------------- 1 | ## Swift 里是怎样实现多态的? 2 | 3 | ### 编译时多态 4 | 5 | 方法重载:可以使用相同的方法名执行不同的操作,但应具有不同的签名。 6 | 7 | 方法签名包括以下内容: 8 | 9 | - 方法名称 10 | - 参数数量 11 | - 数据类型和参数顺序 12 | 13 | ### 运行时多态 14 | 15 | `override`:编译期不知道要调用哪个方法,到运行期才知道要调用哪个方法 16 | 17 | 18 | 19 | ### Reference 20 | 21 | https://www.cosmiclearn.com/swift/polymorphism.php 22 | -------------------------------------------------------------------------------- /MD/Swift底层本质/闭包/闭包捕获.md: -------------------------------------------------------------------------------- 1 | ## 闭包值捕获的原理是什么?捕获到的值存储在哪里? 2 | 3 | ### 闭包值捕获的原理是什么? 4 | 5 | 闭包捕获的局部变量时,会在堆空间中申请一块内存。疑似创建了一个“类”,将局部变量的作为“类”的成员变量存储在堆上。这块空间前 16 个字节存储基本信息,前 8 个字节存储类信息,后 8 个字节存储引用计数。再后面存储捕获的局部变量。 6 | 7 | ### 捕获到的值存储在哪里? 8 | 9 | 闭包将局部变量复制到了堆空间上。 10 | 11 | 12 | 13 | ### Reference 14 | 15 | https://juejin.im/post/5d5c0010e51d456201486e4c#heading-2 16 | -------------------------------------------------------------------------------- /MD/UI/图像/图像绘制.md: -------------------------------------------------------------------------------- 1 | ## 图像绘制的原理和过程? 2 | 3 | ![](../../../Image/UI/image-to-screen.png) 4 | 5 | 图片渲染到屏幕的过程: 6 | 7 | 读取文件 → 计算 Frame → 图片解码 → 解码后纹理图片位图数据通过数据总线交给 GPU → GPU 获取图片 Frame → 顶点变换计算 → 光栅化 → 根据纹理坐标获取每个像素点的颜色值(如果出现透明值需要将每个像素点的颜色*透明度值) → 渲染到帧缓存区 → 渲染到屏幕 8 | 9 | 10 | 11 | ### Reference 12 | 13 | https://juejin.im/post/5d6a0809f265da03de3b193a 14 | 15 | -------------------------------------------------------------------------------- /MD/Swift底层本质/探究本质/枚举2.md: -------------------------------------------------------------------------------- 1 | ## 枚举可以定义存储属性吗?枚举可以定义类型存储属性吗? 2 | 3 | ### 枚举可以定义存储属性吗? 4 | 5 | 不可以 6 | 7 | ### 枚举可以定义类型存储属性吗? 8 | 9 | 可以 10 | 11 | 12 | 13 | ### Code 14 | 15 | ```swift 16 | enum TestEnum { 17 | case a 18 | case b 19 | 20 | var x = 1 // Error: Enums must not contain stored properties 21 | static var y = 2 22 | } 23 | ``` 24 | 25 | -------------------------------------------------------------------------------- /MD/内存/布局/类.md: -------------------------------------------------------------------------------- 1 | ## 类的内存布局? 2 | 3 | 类的对象存储在堆区,栈区只存放类的指针(8 个字节),所以不管给类增加属性还是减少属性,用 `MemoryLayout` 获取类的内存大小永远是 8 个字节。 4 | 5 | 一个空的类占 16 个字节,其中前 8 个字节是类型信息,后 8 个字节是保存引用计数。 6 | 7 | 类的内存大小是 16 的倍数,即增加一个 `Int` 类型的属性(8 个字节),类的大小会变成 32 个字节。 8 | 9 | 所以相同的数据结构,用 `class` 会比 `struct` 开销更多,效率更低。 10 | 11 | 12 | 13 | ### Reference 14 | 15 | https://www.jianshu.com/p/7b930581f01f -------------------------------------------------------------------------------- /MD/内存/基础/堆区栈区.md: -------------------------------------------------------------------------------- 1 | ## 堆区和栈区的区别?为什么要设计堆和栈,主要解决哪些问题? 2 | 3 | ### 堆区和栈区的区别? 4 | 5 | #### 堆区 6 | 7 | 一般由程序员分配释放,若程序员不释放,则可能会引起内存泄漏。注堆和数据结构中的堆栈不一样,其类是与链表。 8 | 9 | #### 栈区 10 | 11 | 由编译器自动分配释放,存放函数的参数值,局部变量等值。其操作方式类似于数据结构中的栈。 12 | 13 | 14 | 15 | ### 为什么要设计堆和栈,主要解决哪些问题? 16 | 17 | 未知 18 | 19 | 20 | 21 | ### Reference 22 | 23 | https://www.jianshu.com/p/f4b80291ba0f 24 | -------------------------------------------------------------------------------- /MD/RunLoop/常见问题/PerformSelector.md: -------------------------------------------------------------------------------- 1 | ## Perform Selector after Delay 的实现原理? 2 | 3 | 当调用 NSObject 的 `performSelecter:afterDelay:` 后,实际上其内部会创建一个 Timer 并添加到当前线程的 RunLoop 中。所以如果当前线程没有 RunLoop,则这个方法会失效。 4 | 5 | 当调用 `performSelector:onThread:` 时,实际上其会创建一个 Timer 加到对应的线程去,同样的,如果对应线程没有 RunLoop 该方法也会失效。 6 | 7 | 8 | 9 | ### Reference 10 | 11 | https://blog.ibireme.com/2015/05/18/runloop/ -------------------------------------------------------------------------------- /MD/多线程/基础/多线程方案.md: -------------------------------------------------------------------------------- 1 | ## 多线程有哪些实现方案? 2 | 3 | 1. NSThread 4 | - 封装程度最小最轻量级的,使用更灵活,但要手动管理线程的生命周期、线程同步和线程加锁等,开销较大。 5 | 2. GCD 6 | - 对线程操作进行了封装,加入了很多新的特性,内部进行了效率优化,提供了简洁的C语言接口,使用更加简单高效。 7 | 3. NSOperation/NSOperationQueue 8 | - 基于GCD的一个抽象基类,将线程封装成要执行的操作,不需要管理线程的生命周期和同步,但比GCD可控性更强。 9 | 10 | 11 | 12 | ### Reference 13 | 14 | https://blog.csdn.net/cordova/article/details/69060085 -------------------------------------------------------------------------------- /MD/内存/自动释放池/autoreleasepool-runloop.md: -------------------------------------------------------------------------------- 1 | ## AutoreleasePool 和 Runloop 的关系? 2 | 3 | 每一个线程,包括主线程,都会拥有一个专属的 `RunLoop` 对象,并且会在有需要的时候自动创建。子线程的`runloop` 需要自己手动创建,如果子线程的 `runloop` 没有任何事件,`runloop`会马上退出。 4 | 5 | 另外每一个线程都会维护自己的 `autoreleasepool` 堆栈,当 `runloop` 迭代结束时会向 `autoreleasepool` 发送 `release` 消息。 6 | 7 | 8 | 9 | ### Reference 10 | 11 | https://blog.sunnyxx.com/2014/10/15/behind-autorelease/ -------------------------------------------------------------------------------- /MD/多线程/GCD/延迟执行.md: -------------------------------------------------------------------------------- 1 | ## GCD 延迟执行 DispatchTime 和 DispatchWillTime 有什么区别? 2 | 3 | ### DispatchTime 4 | 5 | 它通过时间间隔的方式来表示一个时间点,初始时间从系统最近一次开机时间开始计算,而且在系统休眠时暂停计时,等系统恢复后继续计时,精确到纳秒。 6 | 7 | 8 | 9 | ### DispatchWillTime 10 | 11 | 它表示一个绝对时间的时间戳,可以直接使用字面量表示延时,也可以借用 `timespec` 结构体来表示,精确到微秒。 12 | 13 | 14 | 15 | ### Reference 16 | 17 | https://juejin.im/post/5acaea17f265da239a601a01#heading-61 -------------------------------------------------------------------------------- /MD/Swift底层本质/多态/方法派发.md: -------------------------------------------------------------------------------- 1 | ## Swift 支持哪些方法派发方式?引用类型、值类型、协议的方法派发有什么不同? 2 | 3 | ### Swift 支持哪些方法派发方式? 4 | 5 | - 直接派发 (Direct Dispatch) 6 | - 函数表派发 (Table Dispatch) 7 | - 消息机制派发 (Message Dispatch) 8 | 9 | ### 引用类型、值类型、协议的方法派发有什么不同? 10 | 11 | ![](../../../Image/Swift底层本质/method-dispatch.png) 12 | 13 | 14 | 15 | ### Reference 16 | 17 | https://blog.csdn.net/u011342466/article/details/79640605 【建议阅读】 18 | -------------------------------------------------------------------------------- /MD/内存/基础/iOS内存布局.md: -------------------------------------------------------------------------------- 1 | ## iOS 内存布局结构? 2 | 3 | ![](../../../Image/内存/ios-memory-layout.png) 4 | 5 | - 代码段:编译之后的代码 6 | - 数据段: 7 | - 字符串常量:比如 NSString *str = @"123" 8 | - 已初始化数据:已初始化的全局变量、静态变量等 9 | - 未初始化数据:未初始化的全局变量、静态变量等 10 | - 堆:函数调用开销,比如局部变量。分配的内存空间地址越来越小 11 | - 栈:通过 alloc、malloc、calloc 等动态分配的空间,分配的内存空间地址越来越大 12 | 13 | 14 | 15 | ### Reference 16 | 17 | https://www.jianshu.com/p/5fadc22ba33b 18 | -------------------------------------------------------------------------------- /MD/UI/图像/离屏渲染.md: -------------------------------------------------------------------------------- 1 | ## 什么是离屏渲染?为什么会有离屏渲染机制?离屏渲染消耗性能的原因? 2 | 3 | ### 什么是离屏渲染? 4 | 5 | 离屏渲染指的是 GPU 在当前屏幕缓冲区以外开辟了一个缓冲区进行渲染操作。 6 | 7 | ### 为什么会有离屏渲染机制? 8 | 9 | 当使用圆角,阴影,遮罩的时候,图层属性的混合体被指定为在未预合成之前 (下一个 VSync 信号开始前) 不能直接在屏幕中绘制,所以就需要屏幕外渲染。 10 | 11 | ### 离屏渲染消耗性能的原因? 12 | 13 | 1. 创建了新的缓冲区 14 | 2. 上下文的频繁切换 15 | 16 | 17 | 18 | ### Reference 19 | 20 | https://juejin.im/post/5c0931d451882531b81b20fa#heading-11 21 | -------------------------------------------------------------------------------- /MD/内存/布局/结构体.md: -------------------------------------------------------------------------------- 1 | ## 结构体的内存布局? 2 | 3 | 结构体和元组当前共享相同的布局算法,在编译器实现中称为“通用”布局算法。算法如下: 4 | 5 | - 一开始设置 size 为 0,alignment 为 1 6 | - 遍历字段,对于每个字段: 7 | - 先根据这个字段的 alignment 来更新 size,让这个字段能够对齐 8 | - size 增加这个字段的大小 9 | - 更新 alignment 为 Max(alignment,这个字段的 alignment) 10 | - 最终拿到总的 size 和 alignment,然后 size 根据 alignment 对其,得到 strip 11 | 12 | 13 | 14 | ### Reference 15 | 16 | https://www.jianshu.com/p/d341974404a7 -------------------------------------------------------------------------------- /MD/RunLoop/基础/mode.md: -------------------------------------------------------------------------------- 1 | ## RunLoop 有哪几种 mode?对常见 mode 的理解? 2 | 3 | RunLoop 一共有 5 种 mode。 4 | 5 | ![](../../../Image/RunLoop/runloop-mode.jpeg) 6 | 7 | iOS 中暴露出来的有 3 个 mode: 8 | 9 | - default:默认情况下的 mode。 10 | - tracking:当 `UIScrollView` 滑动的时候会切换到 tracking mode。 11 | - common:会将事件分别注册到 default 和 tracking mode 中。 12 | 13 | 14 | 15 | ### Reference 16 | 17 | https://juejin.im/post/5aca2b0a6fb9a028d700e1f8#heading-3 -------------------------------------------------------------------------------- /MD/内存/循环引用/weak-unowned.md: -------------------------------------------------------------------------------- 1 | ## weak 和 unowned 有什么区别? 2 | 3 | 为了避免循环引用,需要用弱引用来代替强引用。 4 | 5 | `weak` 和 `unowned` 都是弱引用,他们的区别是 6 | 7 | `weak` 标识的变量,当引用的对象被释放会将指针指向 `nil`,所以 `weak` 的变量一定是可选型。 8 | 9 | `unowned` 标识的变量,当引用的对象被释放,它仍然会指向被释放的内存区域,如果这个时候访问对象会导致程序崩溃。 10 | 11 | 关于两者使用的选择,Apple 给我们的建议是如果能够确定在访问时不会已被释放的话,尽量使用 `unowned` ,如果存在被释放的可能,那就选择用 `weak`。 12 | 13 | 14 | 15 | ### Reference 16 | 17 | https://www.cnblogs.com/yajunLi/p/5972176.html -------------------------------------------------------------------------------- /MD/Swift底层本质/闭包/闭包.md: -------------------------------------------------------------------------------- 1 | ## 闭包是什么?闭包表达式和闭包是什么关系? 2 | 3 | ### 闭包是什么? 4 | 5 | 一个函数和它所捕获的常量、变量环境组合起来,成为**闭包** 6 | 7 | 闭包是自包含的函数代码块,可以在代码中被传递和使用。 闭包可以捕获和存储其所在上下文中任意常量和变量的引用。被称为包裹常量和变量。 Swift 会为你管理在捕获过程中涉及到的所有内存操作。 8 | 9 | ### 闭包表达式和闭包是什么关系? 10 | 11 | 闭包表达式是一种未命名的闭包,用轻量级语法编写,可以从周围的上下文中捕获值。 12 | 13 | 14 | 15 | ### Reference 16 | 17 | https://juejin.im/post/5b29e66251882574b72f0a93 18 | 19 | https://www.codingexplorer.com/closure-expressions-swift/ 20 | -------------------------------------------------------------------------------- /MD/内存/copy-on-write/copy-on-write.md: -------------------------------------------------------------------------------- 1 | ## 什么是 Copy on write? 2 | 3 | 当一个 `struct` 类型的实例被两个变量持有时,这两个变量的指针地址是一样的,当其中一个变量修改 `struct` 中的属性时,Swift 会将这个 `struct` 复制一份,将变量的指针指向新的地址,再进行修改操作。 4 | 5 | 6 | 7 | ### Code 8 | 9 | ```swift 10 | var array1: [Int] = [0, 1, 2, 3] //0x600000078de0 11 | var array2 = array1 //0x600000078de0 12 | array2.append(4) //0x6000000aa100 13 | ``` 14 | 15 | 16 | 17 | ### Reference 18 | 19 | https://www.jianshu.com/p/e8b1336d9d5d -------------------------------------------------------------------------------- /MD/Swift底层本质/探究本质/inout3.md: -------------------------------------------------------------------------------- 1 | ## inout 参数传递观察型属性会触发观察的 willSet 和 didSet 方法吗?底层原理是什么?为什么这样设计? 2 | 3 | ### inout 参数传递观察型属性会触发观察的 `willSet` 和 `didSet` 方法吗? 4 | 5 | 会触发 6 | 7 | ### 底层原理是什么? 8 | 9 | `inout` 如果有物理内存地址,且没有属性观察器,直接将内存地址传入函数。 如果是计算属性,或者设置了属性观察器,采取 `Copy-In-Copy-out` 做法,调用该函数,先复制实参的值,产生副本,将副本的内存地址传入函数(副本进行引用传递),在函数内部可以修改副本的值,函数返回后,将副本的值覆盖实参的值。 10 | 11 | 12 | 13 | ### Reference 14 | 15 | https://juejin.im/post/5d5c0010e51d456201486e4c#heading-5 16 | -------------------------------------------------------------------------------- /MD/Swift底层本质/多态/Q1.md: -------------------------------------------------------------------------------- 1 | ## 为什么建议使用 struct 而不使用 class? 2 | 3 | - `struct` 是值类型,`class` 是引用类型 4 | - `struct` 在栈中,`class` 在堆中,因此 `struct` 比 `class` 更快 5 | - `struct` 不可以继承,`class` 可以继承,但是他们都可以遵守 `protocol` 6 | 7 | 为了避免不断继承导致 `class` 过于臃肿,推荐使用 `struct` + `protocol`。 8 | 9 | 当然在某些时候我们需要多个变量引用一份实例,这时候就要用 `class` 了,不过这种情景出现的频率较低,所以大部分情况下还是使用 `struct` 10 | 11 | 12 | 13 | ### Reference 14 | 15 | https://stackoverflow.com/questions/24232799/why-choose-struct-over-class 16 | -------------------------------------------------------------------------------- /MD/Swift底层本质/关键字/type.md: -------------------------------------------------------------------------------- 1 | ## .type 和 type(of: ) 的区别 2 | 3 | ### .type 4 | 5 | ```swift 6 | let staicMetaType: String.Type = String.self 7 | ``` 8 | 9 | 10 | 11 | ### type(of: ) 12 | 13 | ```swift 14 | let instanceMetaType: String.Type = type(of: "String") 15 | ``` 16 | 17 | 18 | 19 | ### 区别 20 | 21 | 他们都用于获得元类型的值,`.self` 取到的是静态的元类型,声明的时候是什么类型就是什么类型。`type(of:)` 取的是运行时候的元类型,也就是这个实例的类型。 22 | 23 | 24 | 25 | ### Reference 26 | 27 | https://www.jianshu.com/p/36083d0404b9 28 | -------------------------------------------------------------------------------- /MD/RunLoop/基础/thread.md: -------------------------------------------------------------------------------- 1 | ## RunLoop 和线程有什么关系? 2 | 3 | 1. 每条线程都有唯一的一个与之对应的 RunLoop 对象。 4 | 2. 主线程的 RunLoop 已经自动创建好了,子线程的 RunLoop 需要主动创建。 5 | 3. RunLoop 在第一次获取时创建,在线程结束时销毁。 6 | 7 | RunLoop 与线程的关系如下图: 8 | 9 | ![](../../../Image/RunLoop/runloop-thread.jpeg) 10 | 11 | > 图中展现了 Runloop 在线程中的作用:从 input source 和 timer source 接受事件,然后在线程中处理事件。 12 | 13 | 14 | 15 | ### Reference 16 | 17 | https://juejin.im/post/5aca2b0a6fb9a028d700e1f8#heading-2 18 | 19 | https://imlifengfeng.github.io/article/487/ -------------------------------------------------------------------------------- /MD/Swift底层本质/关键字/Any.md: -------------------------------------------------------------------------------- 1 | ## Any, AnyObject, Anyclass 的区别 2 | 3 | ### Any 4 | 5 | `Any` 可以表示任意类型,甚至包括方法 (func) 类型 6 | 7 | 8 | ### AnyObject 9 | 10 | `AnyObject` 可以代表任何 `class` 类型的实例 11 | 12 | 13 | 14 | ### AnyClass 15 | 16 | 通过 `AnyObject.Type` 这种方式得到的就是一个元类型(Meta),也就是 `AnyClass` 17 | 18 | ```swift 19 | typealias AnyClass = AnyObject.Type 20 | ``` 21 | 22 | 23 | 24 | ### Reference 25 | 26 | https://swifter.tips/any-anyobject/ 27 | 28 | https://www.jianshu.com/p/bab58a90110c 29 | -------------------------------------------------------------------------------- /MD/内存/循环引用/weak.md: -------------------------------------------------------------------------------- 1 | ## weak 指针实现原理?为什么对象销毁后会被置为 nil? 2 | 3 | ### weak 指针实现原理? 4 | 5 | Runtime 维护了一个 `weak` 表,用于存储指向某个对象的所有 `weak` 指针。`weak` 表其实是一个哈希表,Key 是所指对象的地址,Value 是 `weak` 指针的地址(这个地址的值是所指对象的地址)数组。 6 | 7 | 8 | 9 | ### 为什么对象销毁后会被置为 nil? 10 | 11 | 1. 从 `weak` 表中获取被释放对象的地址为键值的记录 12 | 2. 将包含在记录中的所有附有 `weak` 修饰符变量的地址,赋值为 `nil` 13 | 3. 将 `weak` 表中该记录删除 14 | 4. 从引用计数表中删除废弃对象的地址为键值的记录 15 | 16 | 17 | 18 | ### Reference 19 | 20 | https://blog.csdn.net/xiaohuoziooo/article/details/88029300 -------------------------------------------------------------------------------- /MD/多线程/多线程同步和锁/os_unfair_lock.md: -------------------------------------------------------------------------------- 1 | ## os_unfair_lock 怎样使用? 2 | 3 | `OSSpinLock` 已经不在安全,然后苹果出了 `os_unfair_lock` 来替代 `OSSpinLock`。 4 | 5 | ```swift 6 | // 初始化锁 7 | var lock = os_unfair_lock() 8 | // 尝试加锁,成功返回 true,失败返回 false 9 | os_unfair_lock_trylock(&lock) 10 | // 加锁 11 | os_unfair_lock_lock(&lock) 12 | // 解锁 13 | os_unfair_lock_unlock(&lock) 14 | ``` 15 | 16 | 注意事项: 17 | 18 | 1. trylock 不会阻塞线程,lock 会阻塞线程。 19 | 2. trylock 和 lock 只能选一个使用,如果用了 trylock,在 unlock 之前不能在调用 lock,否则会崩溃。 20 | 21 | -------------------------------------------------------------------------------- /MD/RunLoop/常见问题/手势识别.md: -------------------------------------------------------------------------------- 1 | ### 手势识别的过程? 2 | 3 | 当 `_UIApplicationHandleEventQueue()` 识别了一个手势时,其首先会调用 Cancel 将当前的 touchesBegin/Move/End 系列回调打断。随后系统将对应的 UIGestureRecognizer 标记为待处理。 4 | 5 | 苹果注册了一个 Observer 监测 BeforeWaiting (Loop即将进入休眠) 事件,这个Observer的回调函数是 `_UIGestureRecognizerUpdateObserver()`,其内部会获取所有刚被标记为待处理的 GestureRecognizer,并执行GestureRecognizer 的回调。 6 | 7 | 当有 UIGestureRecognizer 的变化(创建/销毁/状态改变)时,这个回调都会进行相应处理。 8 | 9 | 10 | 11 | ### Reference 12 | 13 | https://blog.ibireme.com/2015/05/18/runloop/ -------------------------------------------------------------------------------- /MD/Swift底层本质/特性和优化/map.md: -------------------------------------------------------------------------------- 1 | ## map, flatMap, compactMap 的区别? 2 | 3 | ### map 4 | 5 | 可以对数组中的每一个元素做一次处理 6 | 7 | 8 | 9 | ### flatMap 10 | 11 | 能把数组中存有数组的数组(二维数组、N维数组)展平变成一个新的数组 12 | 13 | 14 | 15 | ### compactMap 16 | 17 | 能把数组中的 nil 过滤掉,返回一个新的数组 18 | 19 | 20 | 21 | ### Code 22 | 23 | ```swift 24 | let m1 = [1, 2, 3, 4, 5].map{ $0 * 2 } // [2, 4, 6, 8, 10] 25 | let m2 = [[1, 2], [3, 4]].flatMap{ $0 } // [1, 2, 3, 4] 26 | let m3 = [1, 2, 3, nil, 5, 6].compactMap{ $0 } // [1, 2, 3, 5, 6] 27 | ``` 28 | 29 | -------------------------------------------------------------------------------- /MD/多线程/基础/多线程隐患.md: -------------------------------------------------------------------------------- 1 | ## 多线程会有哪些安全隐患?一般有什么解决方案? 2 | 3 | ### 多线程的安全隐患 4 | 5 | 资源共享:一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源。比如多个线程访问同一个对象、同一个变量、同一个文件。 6 | 7 | 8 | 9 | ### 解决方案 10 | 11 | 使用线程同步技术:加锁 12 | 13 | - OSSpinLock 14 | - os_unfair_lock 15 | - pthread_mutex 16 | - dispatch_semaphore 17 | - dispatch_queue(DISPATCH_QUEUE_SERIAL) 18 | - NSLock 19 | - NSRecursiveLock 20 | - NSCondition 21 | - NSConditionLock 22 | - @synchronized 23 | 24 | 25 | 26 | ### Reference 27 | 28 | https://www.jianshu.com/p/79ee7ae4a929 -------------------------------------------------------------------------------- /MD/Swift底层本质/探究本质2/String.md: -------------------------------------------------------------------------------- 1 | ## String 类型占多少个字节?String 类型变量的字面量在内存中是怎样存储的? 2 | 3 | ### String 类型占多少个字节? 4 | 5 | 占 16 个字节 6 | 7 | ### String 类型变量的字面量在内存中是怎样存储的? 8 | 9 | `String` 是采用了小数据优化,大数据不优化方案,在OC中叫 `tag pointer`,其实就是小数据不用指针,大数据使用指针。 10 | 11 | ### String 字面量长度小于 16 个字节是怎样存储的?大于 16 个字节又是怎样存储的? 12 | 13 | 当长度大于 15 使用指针存储,小于 15 直接存储数据。 当长度小于 15 `String` 是存储在数据段,当进行 `append()` 操作,会将数据段数据复制到栈区,而且使用指针存储数据。 14 | 15 | 16 | 17 | ### Reference 18 | 19 | https://juejin.im/post/5d5c0010e51d456201486e4c#heading-11 20 | -------------------------------------------------------------------------------- /MD/多线程/基础/死锁.md: -------------------------------------------------------------------------------- 1 | ## 死锁产生的条件有哪些? 2 | 3 | ### 定义 4 | 5 | 所谓死锁,通常指有两个线程T1和T2都卡住了,并等待对方完成某些操作。T1不能完成是因为它在等待T2完成。但T2也不能完成,因为它在等待T1完成。于是大家都完不成,就导致了死锁(DeadLock)。 6 | 7 | 8 | 9 | ### 死锁产生的条件 10 | 11 | 产生死锁的四个必要条件: 12 | 13 | 1. 互斥条件:一个资源每次只能被一个进程使用。 14 | 2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 15 | 3. 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。 16 | 4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。 17 | 18 | 这四个条件是死锁的**必要条件**,只要系统发生死锁,这些条件必然成立,而只要上述条件之 19 | 一不满足,就不会发生死锁。 20 | 21 | 22 | 23 | ### Reference 24 | 25 | https://www.jianshu.com/p/014c291e6ee2 -------------------------------------------------------------------------------- /MD/多线程/GCD/DispatchSemaphore.md: -------------------------------------------------------------------------------- 1 | ## DispatchSemaphore 的理解?对信号量控制方法的理解?信号量底层原理是怎样? 2 | 3 | ### DispatchSemaphore 的理解? 4 | 5 | `DispatchSemaphore`,通常称作信号量,顾名思义,它可以通过计数来标识一个信号,这个信号怎么用呢,取决于任务的性质。通常用于对同一个资源访问的任务数进行限制。 6 | 7 | 例如,控制同一时间写文件的任务数量、控制端口访问数量、控制下载任务数量等。 8 | 9 | 10 | 11 | ### 信号量底层原理是怎样? 12 | 13 | 详情请看这篇[文章](https://juejin.im/post/5c761d36f265da2d84109277)。 14 | 15 | 16 | 17 | ### Reference 18 | 19 | https://juejin.im/post/5acaea17f265da239a601a01#heading-41 20 | 21 | https://juejin.im/post/5c761d36f265da2d84109277 -------------------------------------------------------------------------------- /MD/内存/自动释放池/autoreleasepool.md: -------------------------------------------------------------------------------- 1 | ## 什么是自动释放池?自动释放池的管理原理是怎样的? 2 | 3 | ### 什么是自动释放池? 4 | 5 | `NSAutoreleasePool` 类被用来支持自动引用计数内存管理系统。一个自动释放池存储的对象当自己被销毁的时会向其中的对象发送 release 消息。 6 | 7 | 8 | 9 | ### 自动释放池的管理原理是怎样的? 10 | 11 | 在一个自动引用计数的环境中(并不是垃圾回收机制),一个包含了多个对象的 NSAutoreleasePool 对象能够接收 autorelease 消息并且当销毁它的时候会对每一个池子中的对象发送 release 消息。因此,发送 autorelease 而不是 release 消息延长了对象的生命周期直到 pool 被清空的时候(当对象被保留的时候会更久)。一个对象能够被放到同一个池子中许多次,在这种情况下每放一次都会收到一个 release 消息。 12 | 13 | 14 | 15 | ### Reference 16 | 17 | https://www.jianshu.com/p/554c9fe0f041 -------------------------------------------------------------------------------- /MD/Swift底层本质/特性和优化/filter-reduce.md: -------------------------------------------------------------------------------- 1 | ## filter, reduce 的区别? 2 | 3 | ### filter 4 | 5 | 用于筛选,参数是一个用来判断是否筛除的筛选闭包,根据闭包函数返回的 Bool 值来过滤值。为 true 则加入到结果数组中。 6 | 7 | 8 | 9 | ### reduce 10 | 11 | 给定一个类型为U的初始值,把数组[T]中每一个元素传入到combine的闭包函数里面,通过计算得到最终类型为U的结果值。 12 | 13 | 14 | 15 | ### Code 16 | 17 | ```swift 18 | let list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].filter{ $0 % 2 == 0 } 19 | // list = [2, 4, 6, 8, 10] 20 | 21 | let sum = [1, 2, 3, 4].reduce(into: 0) { (result, num) in 22 | result += num 23 | } 24 | // sum = 10 25 | ``` 26 | 27 | -------------------------------------------------------------------------------- /MD/多线程/多线程同步和锁/NSLock.md: -------------------------------------------------------------------------------- 1 | ## NSLock 和 NSRecursiveLock 的理解? 2 | 3 | ### NSLock 4 | 5 | `NSLock` 只是在内部封装了一个 `pthread_mutex`,属性为 `PTHREAD_MUTEX_ERRORCHECK`,它会损失一定性能换来错误提示。 6 | 7 | `NSLock` 比 `pthread_mutex` 略慢的原因在于它需要经过方法调用,同时由于缓存的存在,多次方法调用不会对性能产生太大的影响。 8 | 9 | 10 | 11 | ### NSRecursiveLock 12 | 13 | `NSRecursiveLock` 与 `NSLock` 的区别在于内部封装的 `pthread_mutex_t` 对象的类型不同,前者的类型为 `PTHREAD_MUTEX_RECURSIVE`。 14 | 15 | 16 | 17 | ### Reference 18 | 19 | [pthread_mutex 锁的理解]() 20 | 21 | https://juejin.im/post/57f6e9f85bbb50005b126e5f#heading-8 -------------------------------------------------------------------------------- /MD/Swift底层本质/关键字/Self和self的区别.md: -------------------------------------------------------------------------------- 1 | ## Self 和 self 的区别 2 | 3 | ### self 4 | 5 | `self` 在实例方法中代指当前实例,在类方法中则代指当前类。 6 | 7 | 8 | 9 | ### Self 10 | 11 | `Self` 有两种不同的用途 12 | 13 | 1. 在 `Protocol` 中用于限制相关的类型 14 | 2. 在 `Class/static` 方法中充当方法的返回值类型 15 | 16 | 17 | 18 | ### Code 19 | 20 | ```swift 21 | extension BinaryInteger { 22 | func squared() -> Self { // Self 用于限制类型 23 | return self * self // self 获取当前实例 24 | } 25 | } 26 | ``` 27 | 28 | 29 | 30 | ### Reference 31 | 32 | https://forums.developer.apple.com/thread/5479 33 | -------------------------------------------------------------------------------- /MD/多线程/GCD/队列3.md: -------------------------------------------------------------------------------- 1 | ## GCD 队列的执行方式有什么区别?不同队列不同执行方式的区别?主队列异步执行多个任务会开启新线程吗?为什么? 2 | 3 | ### GCD 队列的执行方式有什么区别? 4 | 5 | 队列执行的方式有两种: 6 | 7 | `sync`:同步执行 8 | 9 | `async`: 异步执行 10 | 11 | 12 | 13 | ### 不同队列不同执行方式的区别? 14 | 15 | 串行队列+同步执行:队列中的任务在新线程中依次执行 16 | 17 | 串行队列+异步执行:队列中的其他任务执行完之后才会执行 18 | 19 | 并发队列+同步执行:队列中的任务在当前线程中依次执行(相当于串行队列) 20 | 21 | 并发队列+异步执行:队列中的任务在新线程中并发执行 22 | 23 | 24 | 25 | ### 主队列异步执行多个任务会开启新线程吗?为什么? 26 | 27 | 不会 28 | 29 | 只有并发队列+异步执行才会开启新线程 30 | 31 | 32 | 33 | ### Reference 34 | 35 | https://www.jianshu.com/p/f5bece2b77d8 -------------------------------------------------------------------------------- /MD/多线程/多线程同步和锁/OSSpinLock.md: -------------------------------------------------------------------------------- 1 | ## OSSpinLock 不安全的原因? 2 | 3 | 在 iOS 10 中,系统维护了 5 个不同的线程优先级/QoS: background,utility,default,user-initiated,user-interactive。高优先级线程始终会在低优先级线程前执行,一个线程不会受到比它更低优先级线程的干扰。这种线程调度算法会产生潜在的优先级反转问题,从而破坏了 spin lock。 4 | 5 | 具体来说,如果一个低优先级的线程获得锁并访问共享资源,这时一个高优先级的线程也尝试获得这个锁,它会处于 spin lock 的忙等状态从而占用大量 CPU。此时低优先级线程无法与高优先级线程争夺 CPU 时间,从而导致任务迟迟完不成、无法释放 lock。这并不只是理论上的问题,libobjc 已经遇到了很多次这个问题了,于是苹果的工程师停用了 `OSSpinLock`。 6 | 7 | 8 | 9 | ### Reference 10 | 11 | https://blog.ibireme.com/2016/01/16/spinlock_is_unsafe_in_ios/?utm_source=tuicool -------------------------------------------------------------------------------- /MD/Swift底层本质/探究本质/观察属性.md: -------------------------------------------------------------------------------- 1 | ## 观察型属性在初始化的时候会触发吗?定义的时候给定默认值会触发吗? 2 | 3 | ### 观察型属性在初始化的时候会触发吗? 4 | 5 | 不会 6 | 7 | ### 定义的时候给定默认值会触发吗? 8 | 9 | 不会 10 | 11 | 12 | 13 | ### Code 14 | 15 | ```swift 16 | struct Foo { 17 | var test: Int = 10 { 18 | willSet { 19 | print("willSet newValue:\(newValue)") 20 | } didSet { 21 | print("didSet oldValue:\(oldValue)") 22 | } 23 | } 24 | } 25 | 26 | var foo = Foo(test: 20) 27 | foo.test = 30 28 | 29 | // willSet newValue:30 30 | // didSet oldValue:20 31 | ``` 32 | 33 | -------------------------------------------------------------------------------- /MD/Swift底层本质/特性和优化/Swift编译流程.md: -------------------------------------------------------------------------------- 1 | ## 大概描述一下 Swift 的编译流程?Swift 和 OC 的区别? 2 | 3 | ### 大概描述一下 Swift 的编译流程? 4 | 5 | ![](../../../Image/Swift底层本质/swift-compilation-process.jpg) 6 | 7 | 1. Swift 代码转换成 **ATS**(抽象语法树) 8 | 2. ATS 转换成 **SIL** (Swift 中间语言) 9 | 3. 由原始 SIL 转换成 标准的 SIL 10 | 4. SIL 转换成 **LLVM IR** (底层虚拟机的中间表示层) 11 | 5. LLVM 转换成 **Assembly** (汇编) 12 | 6. Assembly 转换成 **Executable** (可执行文件) 13 | 14 | 15 | 16 | ### Swift 和 OC 的区别? 17 | 18 | 未知 19 | 20 | 21 | 22 | ### Reference 23 | 24 | https://github.com/apple/swift/blob/master/docs/CompilerPerformance.md 25 | -------------------------------------------------------------------------------- /MD/多线程/基础/常驻线程.md: -------------------------------------------------------------------------------- 1 | ## 怎样实现一个常驻线程?自定义 Runloop 的应用线程保活? 2 | 3 | ```objective-c 4 | self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(createRunloopByNormal) object:nil]; 5 | 6 | - (void)createRunloopByNormal{ 7 | @autoreleasepool { 8 | //添加port源,保证runloop正常轮询,不会创建后直接退出。 9 | [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode]; 10 | //开启runloop 11 | [[NSRunLoop currentRunLoop] run]; 12 | } 13 | } 14 | ``` 15 | 16 | 17 | 18 | ### Reference 19 | 20 | https://www.jianshu.com/p/0b082ab5a494 -------------------------------------------------------------------------------- /MD/RunLoop/常见问题/GCD.md: -------------------------------------------------------------------------------- 1 | ## GCD 和 Runloop 的关系? 2 | 3 | 实际上 RunLoop 底层也会用到 GCD 的东西,比如 RunLoop 的超时时间就是使用 GCD 中的 `dispatch_source_t`来实现的。 4 | 5 | 但同时 GCD 提供的某些接口也用到了 RunLoop, 例如 `dispatch_async()`。当调用 `dispatch_async(dispatch_get_main_queue(), block)` 时,libDispatch 会向主线程的 RunLoop 发送消息,RunLoop会被唤醒,并从消息中取得这个 block,并在回调 `__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__()` 里执行这个 block。但这个逻辑仅限于 dispatch 到主线程,dispatch 到其他线程仍然是由 libDispatch 处理的。 6 | 7 | 8 | 9 | ### Reference 10 | 11 | https://blog.ibireme.com/2015/05/18/runloop/ 12 | 13 | https://blog.csdn.net/u011619283/article/details/53783650 -------------------------------------------------------------------------------- /MD/RunLoop/基础/loop.md: -------------------------------------------------------------------------------- 1 | ## RunLoop 的内部循环逻辑是怎样的? 2 | 3 | ![](../../../Image/RunLoop/runloop-loop.jpeg) 4 | 5 | - 在 RunLoop 启动之后会发送一个通知,来告知观察者 6 | - 将要处理 Timer/Source 0 事件这样一个通知的发送 7 | - 处理 Source0 事件 8 | - 如果有 Source1 要处理,这时会通过一个 go to 语句的实现来进行代码逻辑的跳转,处理唤醒时收到的消息 9 | - 如果没有 Source1 要处理,线程就将要休眠,同时发送一个通知,告诉观察者 10 | - 然后线程进入一个用户态到内核态的切换,休眠,然后等待唤醒,唤醒的条件大约包括三种: 11 | - Source 0 12 | - Timer 13 | - 外部手动唤醒 14 | - 线程刚被唤醒之后也要发送一个通知告诉观察者,然后处理唤醒时收到的消息 15 | - 回到将要处理 Timer/Source 0 事件这样一个通知的发送 16 | - 然后再次进行上面步骤,这就是一个 RunLoop 的事件循环机制 17 | 18 | 19 | 20 | ### Reference 21 | 22 | https://www.jianshu.com/p/7db5b2775623 -------------------------------------------------------------------------------- /MD/RunLoop/应用/常驻线程.md: -------------------------------------------------------------------------------- 1 | ## 如何实现一个常驻线程? 2 | 3 | 1. 创建全局的线程,并启动线程。 4 | 5 | ```swift 6 | lazy var thread: Thread = { 7 | return Thread(target: self, selector: #selector(run), object: nil) 8 | }() 9 | thread.start() 10 | ``` 11 | 12 | 2. 启动 RunLoop。 13 | 14 | ```swift 15 | @objc func run() { 16 | RunLoop.current.add(Port(), forMode: .default) 17 | RunLoop.current.run() 18 | } 19 | ``` 20 | 21 | 3. 利用常驻线程处理事情。 22 | 23 | ```swift 24 | perform(#selector(action), on: thread, with: nil, waitUntilDone: false) 25 | ``` 26 | 27 | 28 | 29 | ### Reference 30 | 31 | https://www.jianshu.com/p/493c476d02eb -------------------------------------------------------------------------------- /MD/多线程/基础/并发.md: -------------------------------------------------------------------------------- 1 | ## 什么是并发?什么是并行?并发和并行的区别? 2 | 3 | ### 什么是并发(concurrency)? 4 | 5 | 指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。 6 | 7 | ![](../../../Image/多线程/concurrency.jpg) 8 | 9 | 10 | 11 | ### 什么是并行(parallel)? 12 | 13 | 指在同一时刻,有多条指令在多个处理器上同时执行。所以无论从微观还是从宏观来看,二者都是一起执行的。 14 | 15 | ![](../../../Image/多线程/parallel.jpg) 16 | 17 | 18 | 19 | ### 并发和并行的区别? 20 | 21 | 1. 并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生。 22 | 2. 并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。 23 | 3. 并行是在一台处理器上“同时”处理多个任务,并发是在多台处理器上同时处理多个任务。 24 | 25 | 26 | 27 | ### Reference 28 | 29 | https://www.jianshu.com/p/cbf9588b2afb -------------------------------------------------------------------------------- /MD/多线程/GCD/死锁.md: -------------------------------------------------------------------------------- 1 | ## GCD 什么情况会发生死锁?原因是什么? 2 | 3 | ### GCD 什么情况会发生死锁? 4 | 5 | ```swift 6 | print("Start") 7 | DispatchQueue.main.sync { 8 | print("1") 9 | } 10 | print("End") 11 | 12 | // 输出: 13 | // Start 14 | ``` 15 | 16 | 17 | 18 | ### 原因是什么? 19 | 20 | 在一个串行队列中,同步的向这个串行队列添加任务(`block`)。 21 | 22 | 23 | 24 | #### 分析 25 | 26 | 在执行 `sync` 方法后,将 `block` 添加到了 `DispatchQueue.main` 中,同时调用 `sync` 这个方法的线程(主线程)被阻塞,等待 `block` 执行完成才会继续向下执行,但是执行主线程队列任务的线程正是主线程,此时他处于阻塞状态,所以 `block` 永远不会被执行,主线程一直处于阻塞状态。因此**这段代码运行后,并非卡在 `block` 中无法返回,而是根本无法执行到这个` block`**。 27 | 28 | 29 | 30 | #### Reference 31 | 32 | https://www.jianshu.com/p/014c291e6ee2 -------------------------------------------------------------------------------- /MD/Swift底层本质/探究本质/枚举.md: -------------------------------------------------------------------------------- 1 | ## 枚举的原始值的本质是什么?占几个字节?它在内存中是存储在枚举里吗? 2 | 3 | ### 枚举的原始值 (rawValue) 的本质是什么? 4 | 5 | `rawValue` 是一个计算属性 6 | 7 | ### 占几个字节? 8 | 9 | 由 `rawValue` 的类型决定 10 | 11 | ### 它在内存中是存储在枚举里吗? 12 | 13 | 不是,计算属性不存储在对象之内 14 | 15 | 16 | 17 | ### Code 18 | 19 | ```swift 20 | enum TestEnum: Int { 21 | case a = 123 22 | case b = 456 23 | } 24 | 25 | let textEnum = TestEnum.a 26 | let p1 = MemoryLayout.stride(ofValue: textEnum) 27 | let p2 = MemoryLayout.size(ofValue: textEnum) 28 | let p3 = MemoryLayout.alignment(ofValue: textEnum) 29 | print("分配:\(p1) 占用:\(p2) 对齐:\(p3)") 30 | // 分配:1 占用:1 对齐:1 注: Int 占 8 个字节 31 | ``` 32 | 33 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | ## 未完成的题目 2 | 3 | ### Swift底层本质 4 | 5 | - [数组存储在栈空间还是堆空间?](https://github.com/RayJiang16/Swift-Review/blob/master/MD/Swift底层本质/探究本质2/Array.md) 6 | - [Swift 里有那几种类型的指针?有什么区别?](https://github.com/RayJiang16/Swift-Review/blob/master/MD/Swift底层本质/指针/指针.md) 7 | - [大概描述一下 Swift 的编译流程?Swift 和 OC 的区别?](https://github.com/RayJiang16/Swift-Review/blob/master/MD/Swift底层本质/特性和优化/Swift编译流程.md) 8 | 9 | 10 | 11 | ### 内存 12 | 13 | - [为什么要设计堆和栈,主要解决哪些问题?](https://github.com/RayJiang16/Swift-Review/blob/master/MD/内存/基础/堆区栈区.md) 14 | 15 | 16 | 17 | ### 多线程 18 | 19 | - [多线程间怎么通信?底层原理是什么?](https://github.com/RayJiang16/Swift-Review/blob/master/MD/多线程/基础/多线程通信.md) 20 | 21 | -------------------------------------------------------------------------------- /MD/RunLoop/基础/stop.md: -------------------------------------------------------------------------------- 1 | ## RunLoop 的退出方式有哪些? 2 | 3 | 在 [RunLoop 的启动方式有哪些?](run.md)这篇文章里说到了 3 种启动的方式,其中第一个启动方式是会一直循环下去的,其他 2 个启动方式都有超时时间的,过了超时时间就退出了。 4 | 5 | 另外系统提供了 `CFRunLoopStop()` 来结束 RunLoop,**但是** `CFRunLoopStop()` 方法只会结束当前的 `runMode:beforeDate:` 调用,而不会结束后续的调用。 6 | 7 | 所以想要控制 RunLoop 的退出时机有两个方案: 8 | 9 | 1 使用 `CFRunLoopRun()` 启动 RunLoop 10 | 11 | 2 12 | 13 | ```swift 14 | BOOL shouldKeepRunning = YES; // global 15 | NSRunLoop *theRL = [NSRunLoop currentRunLoop]; 16 | while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); 17 | ``` 18 | 19 | 20 | 21 | ### Reference 22 | 23 | https://juejin.im/post/579583ba6be3ff006613628c 24 | -------------------------------------------------------------------------------- /MD/内存/基础/retain-release.md: -------------------------------------------------------------------------------- 1 | ## retain, release 的实现机制? 2 | 3 | **retain** 4 | 5 | ```objective-c 6 | SideTable& table = SideTables()[This]; 7 | size_t& refcntStorage = table.refcnts[This]; 8 | refcntStorage += SIZE_TABLE_RC_ONE; 9 | ``` 10 | 11 | **release** 12 | 13 | ```objective-c 14 | SideTable& table = SideTables()[This]; 15 | size_t& refcntStorage = table.refcnts[This]; 16 | refcntStorage -= SIZE_TABLE_RC_ONE; 17 | ``` 18 | 19 | 二者的实现机制类似,概括讲就是通过第一层 `hash` 算法,找到 `指针变量` 所对应的 `sideTable`。然后再通过一层 `hash` 算法,找到存储 `引用计数` 的 `size_t`,然后对其进行增减操作。`retainCount` 不是固定的1,`SIZE_TABLE_RC_ONE` 是一个宏定义,实际上是一个值为 4 的偏移量。 20 | 21 | 22 | 23 | ### Reference 24 | 25 | https://www.jianshu.com/p/9147a2d92dda 26 | -------------------------------------------------------------------------------- /MD/RunLoop/常见问题/事件响应.md: -------------------------------------------------------------------------------- 1 | ## 事件响应的过程? 2 | 3 | 苹果注册了一个 Source1 (基于 mach port 的) 用来接收系统事件,其回调函数为 `__IOHIDEventSystemClientQueueCallback()`。 4 | 5 | 当一个硬件事件(触摸/锁屏/摇晃等)发生后,首先由 IOKit.framework 生成一个 IOHIDEvent 事件并由 SpringBoard 接收。这个过程的详细情况可以参考[这里](http://iphonedevwiki.net/index.php/IOHIDFamily)。SpringBoard 只接收按键(锁屏/静音等),触摸,加速,接近传感器等几种 Event,随后用 mach port 转发给需要的App进程。随后苹果注册的那个 Source1 就会触发回调,并调用 `_UIApplicationHandleEventQueue()` 进行应用内部的分发。 6 | 7 | `_UIApplicationHandleEventQueue()` 会把 IOHIDEvent 处理并包装成 UIEvent 进行处理或分发,其中包括识别 UIGesture/处理屏幕旋转/发送给 UIWindow 等。通常事件比如 UIButton 点击、touchesBegin/Move/End/Cancel 事件都是在这个回调中完成的。 8 | 9 | 10 | 11 | ### Reference 12 | 13 | https://blog.ibireme.com/2015/05/18/runloop/ -------------------------------------------------------------------------------- /MD/RunLoop/基础/RunLoop.md: -------------------------------------------------------------------------------- 1 | ## 什么是 RunLoop? 2 | 3 | `RunLoop` 是什么?`RunLoop` 还是比较顾名思义的一个东西,说白了就是一种循环,只不过它这种循环比较高级。一般的 while 循环会导致 CPU 进入忙等待状态,而 `RunLoop` 则是一种“闲”等待,这部分可以类比 Linux 下的 epoll。当没有事件时,`RunLoop` 会进入休眠状态,有事件发生时, `RunLoop` 会去找对应的 Handler 处理事件。`RunLoop` 可以让线程在需要做事的时候忙起来,不需要的话就让线程休眠。 4 | 5 | RunLoop 运行流程如下 6 | 7 | ![](../../../Image/RunLoop/runloop.jpeg) 8 | 9 | 10 | 11 | ### Reference 12 | 13 | [官方文档](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW1) 14 | 15 | [深入理解RunLoop](https://blog.ibireme.com/2015/05/18/runloop/) 【建议阅读】 16 | 17 | https://juejin.im/post/5aca2b0a6fb9a028d700e1f8#heading-1 18 | 19 | -------------------------------------------------------------------------------- /MD/内存/copy-on-write/Q1.md: -------------------------------------------------------------------------------- 1 | ## 如何为结构体手动实现 Copy on write? 2 | 3 | ```swift 4 | final class Ref { 5 | var val : T 6 | init(_ v : T) {val = v} 7 | } 8 | 9 | struct Box { 10 | var ref : Ref 11 | init(_ x : T) { ref = Ref(x) } 12 | 13 | var value: T { 14 | get { return ref.val } 15 | set { 16 | if (!isUniquelyReferencedNonObjC(&ref)) { 17 | ref = Ref(newValue) 18 | return 19 | } 20 | ref.val = newValue 21 | } 22 | } 23 | } 24 | ``` 25 | 26 | 该例子显示了如何用一个引用类型去实现一个拥有copy-on-write特性的泛型值类型T。具体逻辑就是当你进行set的时候判断是否有多个reference,如果是多个reference则进行拷贝,反之则不会。 27 | 28 | 29 | 30 | ### Reference 31 | 32 | https://juejin.im/post/5cbd31225188250a6e7e5d35 -------------------------------------------------------------------------------- /MD/RunLoop/常见问题/Timer.md: -------------------------------------------------------------------------------- 1 | ## 定时器滑动失效的原因?怎样处理?为什么 Timer 不精准?如何实现精准的定时? 2 | 3 | ### 定时器滑动失效的原因? 4 | 5 | 因为 Timer 默认会被加到主线程的 RunLoop 中,mode 为默认,但是 `UIScrollView` 滑动时 RunLoop 的 mode 会从 default 切换到 tracking,这就导致了 Timer 失效。 6 | 7 | #### 怎样处理? 8 | 9 | 在 Timer 加到 RunLoop 时,将 mode 设置为 common。common mode 会将事件分别注册到 default 和 tracking 模式中。 10 | 11 | 12 | 13 | ### 为什么 Timer 不精准? 14 | 15 | NSTimer 其实就是 CFRunLoopTimerRef,他们之间是 toll-free bridged 的。一个 NSTimer 注册到 RunLoop 后,RunLoop 会为其重复的时间点注册好事件。例如 10:00, 10:10, 10:20 这几个时间点。RunLoop为了节省资源,并不会在非常准确的时间点回调这个Timer。Timer 有个属性叫做 Tolerance (宽容度),标示了当时间点到后,容许有多少最大误差。 16 | 17 | #### 如何实现精准的定时? 18 | 19 | 使用 GCD 创建定时器。 20 | 21 | 22 | 23 | ### Reference 24 | 25 | https://blog.ibireme.com/2015/05/18/runloop/ -------------------------------------------------------------------------------- /MD/RunLoop/常见问题/AutoreleasePool.md: -------------------------------------------------------------------------------- 1 | ## RunLoop 和 AutoreleasePool 的关系? 2 | 3 | App 启动后,苹果在主线程 RunLoop 里注册了两个 Observer,其回调都是 `_wrapRunLoopWithAutoreleasePoolHandler()`。 4 | 5 | 第一个 Observer 监视的事件是 Entry (即将进入Loop),其回调内会调用 `_objc_autoreleasePoolPush()` 创建自动释放池。其 order 是 -2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。 6 | 7 | 第二个 Observer 监视了两个事件: BeforeWaiting (准备进入休眠) 时调用 `_objc_autoreleasePoolPop()` 和 `_objc_autoreleasePoolPush()` 释放旧的池并创建新池;Exit (即将退出Loop) 时调用 `_objc_autoreleasePoolPop()` 来释放自动释放池。这个 Observer 的 order 是 2147483647,优先级最低,保证其释放池子发生在其他所有回调之后。 8 | 9 | 在主线程执行的代码,通常是写在诸如事件回调、Timer回调内的。这些回调会被 RunLoop 创建好的 AutoreleasePool 环绕着,所以不会出现内存泄漏,开发者也不必显示创建 Pool 了。 10 | 11 | 12 | 13 | ### Reference 14 | 15 | https://blog.ibireme.com/2015/05/18/runloop/ -------------------------------------------------------------------------------- /MD/内存/基础/ARC.md: -------------------------------------------------------------------------------- 1 | ## ARC 在编译时和运行时分别做了哪些工作? 2 | 3 | ### 编译时 4 | 5 | 根据代码执行的上下文语境,在适当的位置插入 `retain`,`release` 6 | 7 | 8 | 9 | ### 运行时 10 | 11 | - 主要是指 `weak` 关键字。`weak` 修饰的变量能够在引用计数为`0` 时被自动设置成 `nil`,显然是有运行时逻辑在工作的。 12 | 13 | - 为了保证向后兼容性,`ARC` 在运行时检测到类函数中的 `autorelease` 后紧跟其后 `retain`,此时不直接调用对象的 `autorelease` 方法,而是改为调用 `objc_autoreleaseReturnValue`。 14 | `objc_autoreleaseReturnValue` 会检视当前方法返回之后即将要执行的那段代码,若那段代码要在返回对象上执行 `retain` 操作,则设置全局数据结构中的一个标志位,而不执行 `autorelease` 操作,与之相似,如果方法返回了一个自动释放的对象,而调用方法的代码要保留此对象,那么此时不直接执行 `retain` ,而是改为执行 `objc_retainAoutoreleasedReturnValue`函数。此函数要检测刚才提到的标志位,若已经置位,则不执行 `retain` 操作,设置并检测标志位,要比调用 `autorelease` 和`retain`更快。 15 | 16 | 17 | 18 | ### Reference 19 | 20 | https://www.jianshu.com/p/d716bda927ac 21 | 22 | -------------------------------------------------------------------------------- /MD/多线程/基础/多线程理解.md: -------------------------------------------------------------------------------- 1 | ## 对多线程的理解?多线程的底层原理?多线程的优缺点? 2 | 3 | ### 对多线程的理解? 4 | 5 | 多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。 6 | 7 | 8 | 9 | ### 多线程的底层原理? 10 | 11 | - 同一时间,CPU只能处理1条线程,只有1条线程在工作(执行) 12 | - 多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换) 13 | - 如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象 14 | 15 | 注意:多线程并发,并不是cpu在同一时刻同时执行多个任务,只是CPU调度足够快,造成的假象。 16 | 17 | 18 | 19 | ### 多线程的优缺点? 20 | 21 | #### 优点 22 | 23 | - 能适当提高程序的执行效率 24 | - 能适当提高资源利用率(CPU、内存利用率) 25 | 26 | #### 缺点 27 | 28 | - 开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能 29 | - 线程越多,CPU在调度线程上的开销就越大 30 | 31 | 主线程栈区的1M,非常非常宝贵。一个进程,至少有一个线程(主线程),不能杀掉一个线程!但是可以暂停、休眠。 32 | 33 | 34 | 35 | ### Reference 36 | 37 | https://www.jianshu.com/p/81cc15d5ae3d -------------------------------------------------------------------------------- /MD/多线程/基础/多线程通信.md: -------------------------------------------------------------------------------- 1 | ## 多线程间怎么通信?底层原理是什么? 2 | 3 | ### 多线程间怎么通信? 4 | 5 | #### NSThread 6 | 7 | ```objective-c 8 | // 返回主线程 9 | - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait; 10 | // 返回指定线程 11 | - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait; 12 | ``` 13 | 14 | #### GCD 15 | 16 | ```objective-c 17 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 18 | // 子线程 19 | dispatch_async(dispatch_get_main_queue(), ^{ 20 | // 切换到主线程 21 | }); 22 | }); 23 | ``` 24 | 25 | ### NSOperation 26 | 27 | ```objective-c 28 | [[NSOperationQueue mainQueue]addOperationWithBlock:^{ 29 | // 切换到主线程 30 | }]; 31 | ``` 32 | 33 | 34 | 35 | 36 | 37 | ### 底层原理是什么? 38 | 39 | 未知 -------------------------------------------------------------------------------- /MD/Swift底层本质/关键字/is.md: -------------------------------------------------------------------------------- 1 | ## is, isKind, isMember 的区别 2 | 3 | ### isMember 4 | 5 | 用来判断该对象是否为指定类的对象 6 | 7 | 8 | 9 | ### isKind 10 | 11 | 用来判断该对象是否为指定类或者指定类的父类的对象 12 | 13 | 14 | 15 | ### is 16 | 17 | `is` 的作用于 `isKind` 类似,区别在于 `is` 能用于 `struct` 和 `enum` 18 | 19 | 20 | 21 | ### Code 22 | 23 | ```swift 24 | struct A { } 25 | class B: NSObject { } 26 | class C: B { } 27 | 28 | let a = A() 29 | let b = B() 30 | let c = C() 31 | 32 | // isMember 33 | b.isMember(of: B.self) // true 34 | c.isMember(of: B.self) // false 35 | c.isMember(of: C.self) // true 36 | 37 | // isKind 38 | b.isKind(of: B.self) // true 39 | b.isKind(of: C.self) // false 40 | c.isKind(of: B.self) // true 41 | 42 | // is 43 | b is B // true 44 | c is B // true 45 | b is C // false 46 | a is A // true 47 | ``` 48 | 49 | -------------------------------------------------------------------------------- /MD/Swift底层本质/探究本质/计算属性.md: -------------------------------------------------------------------------------- 1 | ## 计算型属性的本质是什么?占多少个字节?是存储在当前对象里的吗?可以用 let 修饰吗? 2 | 3 | ### 计算属性介绍 4 | 5 | 计算属性不直接存储值,而是提供一个 getter 和一个可选的 setter,来间接获取和设置其他属性或变量的值。计算属性可以用于类、结构体和枚举,存储属性只能用于类和结构体。 6 | 7 | ### 占多少个字节? 8 | 9 | 由计算属性的类型决定 10 | 11 | ### 是存储在当前对象里的吗? 12 | 13 | 不是 14 | 15 | ### 可以用 let 修饰吗? 16 | 17 | 不行,计算属性只能用 var 修饰 18 | 19 | 20 | 21 | ### Code 22 | 23 | ```swift 24 | struct Square { 25 | var width: Int 26 | var area: Int { 27 | get { 28 | return width * 2 29 | } 30 | } 31 | } 32 | 33 | let square = Square(width: 5) 34 | let p1 = MemoryLayout.stride(ofValue: square) 35 | let p2 = MemoryLayout.size(ofValue: square) 36 | let p3 = MemoryLayout.alignment(ofValue: square) 37 | print("分配:\(p1) 占用:\(p2) 对齐:\(p3)") 38 | // 分配:8 占用:8 对齐:8 注: Int 占 8 个字节 39 | ``` 40 | 41 | -------------------------------------------------------------------------------- /MD/Swift底层本质/特性和优化/优化.md: -------------------------------------------------------------------------------- 1 | ## 如何优化 Swift 性能? 2 | 3 | 尽量使用 `struct` 替代 `class` 4 | 5 | 如果 `class` 不需要被继承,用 `final` 标记 6 | 7 | 如果 `class` 的方法不需要暴露给外部,用 `private` 标记 8 | 9 | ### 理由: 10 | 11 | 1. `struct` 在栈中,`class` 在堆中,栈内存分配更快,更安全,操作更快 12 | 2. `struct` 方法的派发方式是**直接派发** (Direct Dispatch),`class` 方法的派发方式是**函数表派发** (Table Dispatch) 13 | 3. 用 `final` 修饰的 `class` 派发方式会从**函数表派发**变为**直接派发** 14 | 4. 在 `class` 中用 `private` 修饰的方法会从**函数表派发**变为**直接派发** 15 | 16 | 17 | 18 | 注:**直接派发**是在编辑期间就确定好会调用哪个方法,而**函数表派发**是在运行期才知道会调用哪个方法,所以**函数表派发**方式会比**直接派发**慢。 19 | 20 | 21 | 22 | ### Reference 23 | 24 | [Swift 支持哪些方法派发方式?引用类型、值类型、协议的方法派发有什么不同?](https://github.com/RayJiang16/Swift-Review/blob/master/MD/多态/方法派发.md) 25 | 26 | https://www.jianshu.com/p/440d760a7392 【建议阅读】 27 | 28 | https://developer.apple.com/videos/play/wwdc2016/416/ 29 | -------------------------------------------------------------------------------- /MD/多线程/GCD/DispatchGroup.md: -------------------------------------------------------------------------------- 1 | ## DispatchGroup 的底层原理是什么?一般用在什么场景?有哪几种添加进组的方式? 2 | 3 | ### DispatchGroup 的底层原理是什么? 4 | 5 | `DispatchGroup` 是基于 `DispatchSemaphore` 实现的,详情请看这篇[文章](https://www.jianshu.com/p/e93fd15d93d3)。 6 | 7 | 8 | 9 | ### 一般用在什么场景? 10 | 11 | 一般用于一个任务依赖于多个耗时任务时使用,比如页面需要等待两个(及以上)接口返回后再刷新的情况。 12 | 13 | 14 | 15 | ### 有哪几种添加进组的方式? 16 | 17 | 有两种进组方式。 18 | 19 | ```swift 20 | let group = DispatchGroup() 21 | let queue = DispatchQueue.global() 22 | 23 | // 第一种方式 24 | queue.async(group: group) { 25 | print("1") 26 | } 27 | 28 | // 第二种方式,enter 和 leave 需要成对出现 29 | group.enter() 30 | queue.sync { 31 | print("2") 32 | group.leave() 33 | } 34 | ``` 35 | 36 | 37 | 38 | ### Reference 39 | 40 | https://www.jianshu.com/p/e93fd15d93d3 41 | 42 | https://juejin.im/post/5acaea17f265da239a601a01#heading-24 -------------------------------------------------------------------------------- /MD/Swift底层本质/关键字/throws.md: -------------------------------------------------------------------------------- 1 | ## throws 和 rethrows 的区别? 2 | 3 | ### throws 4 | 5 | 当一个方法可能会出错,就需要在方法后面加上 `throws` 6 | 7 | 8 | 9 | ### rethrows 10 | 11 | 当一个闭包作为参数传入到一个方法中,如果这个闭包可能会出错,就需要在方法后面加上 `rethrows` 12 | 13 | 14 | 15 | ### Code 16 | 17 | ```swift 18 | enum CustomError: Error { 19 | case error 20 | } 21 | 22 | // throws 23 | func f1(n: Int) throws -> String { 24 | guard n >= 0 else { throw CustomError.error } 25 | return String(n) 26 | } 27 | 28 | // rethrows 29 | let list = [1, 2, 3] 30 | do { 31 | try list.map(f1) 32 | } catch { 33 | // ... 34 | } 35 | 36 | // map 的声明 37 | public func map(_ transform: (Element) throws -> T) rethrows -> [T] 38 | ``` 39 | 40 | 41 | 42 | ### Reference 43 | 44 | https://stackoverflow.com/questions/43305051/what-are-the-differences-between-throws-and-rethrows-in-swift 45 | -------------------------------------------------------------------------------- /MD/Swift底层本质/关键字/权限.md: -------------------------------------------------------------------------------- 1 | ## open, public, internal, fileprivate, private 的区别 2 | 3 | ### open 4 | 5 | 可以被任何人使用和继承 6 | 7 | 8 | 9 | ### public 10 | 11 | 可以被当前模块使用和继承,其他模块只能使用,不能继承 12 | 13 | 14 | 15 | ### internal 16 | 17 | 只能被当前模块使用和继承,其他模块无法访问,权限默认为 `internal` 18 | 19 | 20 | 21 | ### fileprivate 22 | 23 | 只能在当前文件内使用和继承,可以被其他类使用 24 | 25 | 26 | 27 | ### private 28 | 29 | 只能在当前文件内使用和继承,**不能**被其他类使用 30 | 31 | 32 | 33 | ### Code 34 | 35 | ```swift 36 | class A { 37 | private var n1 = 1 38 | fileprivate var n2 = 2 39 | } 40 | 41 | class B { 42 | let a = A() 43 | 44 | func f1() { 45 | a.n1 = 10 // Error: 'n1' is inaccessible due to 'private' protection level 46 | a.n2 = 20 47 | } 48 | } 49 | ``` 50 | 51 | 52 | 53 | ### Reference 54 | 55 | https://www.avanderlee.com/swift/fileprivate-private-differences-explained/ 56 | -------------------------------------------------------------------------------- /MD/Swift底层本质/关键字/self.md: -------------------------------------------------------------------------------- 1 | ## .self 的理解 2 | 3 | ### 元类型 4 | 5 | 元类型就是类型的类型。 6 | 7 | 比如我们说 5 是 `Int` 类型,此时 5 是 `Int` 类型的一个值。但是如果我问 `Int` 类型占用多少内存空间,这个时候与具体某个值无关,而和类型的信息相关。如果要写一个函数,返回一个类型的实例内存空间大小。那么这个时候的参数是一个类型数据,这个类型数据可以是直接说明的比如是 `Int` 类型,也可以从一个值身上取,比如 5 这个值的类型。这里的类型数据,就是一个类型的类型,术语表述为元类型:metaType。 8 | 9 | 10 | 11 | ### .Type 与 .self 12 | 13 | Swift 中的元类型用 `.Type` 表示。比如 `Int.Type` 就是 `Int` 的元类型。 14 | 类型与值有着不同的形式,就像 `Int` 与 5 的关系。元类型也是类似,`.Type` 是类型,类型的 `.self` 是元类型的值。 15 | 16 | ```swift 17 | let intMetatype: Int.Type = Int.self 18 | ``` 19 | 20 | 21 | 22 | ### Code 23 | 24 | ```swift 25 | // TableView 注册 Cell 的声明 26 | open func register(_ cellClass: AnyClass?, forCellReuseIdentifier identifier: String) 27 | // 调用 28 | tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") 29 | ``` 30 | 31 | 32 | 33 | ### Reference 34 | 35 | https://www.jianshu.com/p/36083d0404b9 36 | -------------------------------------------------------------------------------- /MD/RunLoop/基础/observer.md: -------------------------------------------------------------------------------- 1 | ## RunLoop 的监听状态有哪些?怎样监听? 2 | 3 | RunLoop 一共有 6 种监听状态: 4 | 5 | ```swift 6 | let observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, CFRunLoopActivity.allActivities.rawValue, true, 0) { 7 | (observer, activity) in 8 | switch activity { 9 | case .entry: 10 | print("RunLoop 启动") 11 | case .beforeTimers: 12 | print("RunLoop 即将处理 Timer 事件") 13 | case .beforeSources: 14 | print("RunLoop 即将处理 Sources 事件") 15 | case .beforeWaiting: 16 | print("RunLoop 即将进入休眠") 17 | case .afterWaiting: 18 | print("RunLoop 被唤醒") 19 | case .exit: 20 | print("RunLoop 退出") 21 | default: 22 | break 23 | } 24 | } 25 | CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, CFRunLoopMode.defaultMode) 26 | ``` 27 | 28 | 29 | 30 | ### Reference 31 | 32 | https://www.cnblogs.com/dashengios/p/10519897.html -------------------------------------------------------------------------------- /MD/Runtime/消息机制/消息转发.md: -------------------------------------------------------------------------------- 1 | ## 消息转发的过程是怎样的? 2 | 3 | ![](../../../Image/Runtime/forwarding.png) 4 | 5 | 1. `+resolveInstanceMethod:` 或 `+resolveClassMethod:`,是动态方法解析,让你有机会提供一个函数实现。 6 | 2. `-(id)forwardingTargetForSelector:(SEL)aSelector` 会看类有没有实现这个方法,这个方法返回的是一个 `id` 类型的转发对象 `forwardingTarget`,如果其不为空,则会通过 `objc_msgSend` 函数对其直接发送消息 `objc_msgSend(forwardingTarget, sel, ...);`,也就是说让转发对象 `forwardingTarget` 去处理当前的方法 SEL。如果 `forwardingTarget` 为 `nil`,则进入下面的方法。 7 | 3. `-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector` 这个方法是让我们根据方法选择器 SEL 生成一个 `NSMethodSignature` 方法签名并返回,这个方法签名会生成一个 `NSInvocation`,将其作为参数,调用 `- (void)forwardInvocation:(NSInvocation *)anInvocation` 方法。如果我们在这里没有返回方法签名,系统则认为我们彻底不想处理这个方法了,就会调用 `doesNotRecognizeSelector:` 方法抛出经典的报错报错 `unrecognized selector sent to instance 0xXXXXXXXX`,结束消息机制的全部流程。 8 | 9 | 10 | 11 | ### Reference 12 | 13 | https://www.jianshu.com/p/198f031f44ea 14 | 15 | https://www.jianshu.com/p/45db86af7b60 -------------------------------------------------------------------------------- /MD/UI/事件传递和响应/事件.md: -------------------------------------------------------------------------------- 1 | ## 手指触摸屏幕后发生了什么?事件的传递和响应链是怎么样的? 2 | 3 | ### 手指触摸屏幕后发生了什么? 4 | 5 | ![](../../../Image/UI/flow-of-UITouch.png) 6 | 7 | 8 | 9 | ### 事件的传递和响应是怎么样的? 10 | 11 | 事件的传递是至下到上的,从最底下一直找到最上层的视图(最佳响应者)。 12 | 13 | 事件的响应是至上到下的,从最上层的视图(最佳响应者)开始一层一层往回找,直到找着能响应事件的视图为止。 14 | 15 | #### 怎么样的视图可以响应事件? 16 | 17 | 1. 允许交互:`userInteractionEnabled = true` 18 | 2. 不能隐藏:`hidden = false` 19 | 3. 透明度大于 0.01 20 | 21 | 满足上面 3 个条件的视图,才具备可以响应事件的能力。 22 | 23 | #### hitTest:withEvent: 24 | 25 | 每个 `UIView` 对象都有一个 `hitTest:withEvent:` 方法,这个方法是 Hit-Testing 过程中最核心的存在,其作用是询问事件在当前视图中的响应者,同时又是作为事件传递的桥梁。 26 | 27 | #### 响应者对于接收到的事件有 3 种操作 28 | 29 | 1. 不拦截,默认操作。事件会自动沿着默认的响应链往下传递 30 | 2. 拦截,不再往下分发事件。重写 `touchesBegan:withEvent:` 进行事件处理,不调用父类的 `touchesBegan:withEvent:` 31 | 3. 拦截,继续往下分发事件。重写 `touchesBegan:withEvent:` 进行事件处理,同时调用父类的 `touchesBegan:withEvent:` 将事件往下传递 32 | 33 | 34 | 35 | 36 | 37 | ### Reference 38 | 39 | https://www.jianshu.com/p/da46db65c3e7 【建议阅读】 40 | -------------------------------------------------------------------------------- /MD/UI/图像/卡顿掉帧.md: -------------------------------------------------------------------------------- 1 | ## 卡顿掉帧的原因?卡顿掉帧应该怎么优化? 2 | 3 | ### 卡顿掉帧的原因? 4 | 5 | ![](../../../Image/UI/screen-fps.png) 6 | 7 | 在显示器中是固定的频率,在 iOS 中是每秒 60 帧(60 FPS),即每帧 16.7 ms。 8 | 9 | 从上图中可以看出,每两个 VSync 信号之间有时间间隔(16.7 ms),在这个时间内,CPU 主线程计算布局,解码图片,创建视图,绘制文本,计算完成后将内容交给 GPU,GPU 变换,合成,渲染,放入帧缓冲区。 10 | 11 | 假如16.7ms内,CPU和GPU没有来得及生产出一帧缓冲,那么这一帧会被丢弃,显示器就会保持不变,继续显示上一帧内容,这就将导致导致画面卡顿。 12 | 13 | 14 | 15 | ### 卡顿掉帧应该怎么优化? 16 | 17 | #### CPU 18 | 19 | 1. 部分对象的创建、调整和销毁可以放到子线程去做 20 | 2. 预排版( 布局计算、文本计算),这些计算也可以放到子线程去做,这样主线程也可以有更多的时间去响应用户的交互 21 | 3. 预渲染(文本等异步绘制、图片编解码等) 22 | 23 | #### GPU 24 | 25 | 1. **纹理渲染**:假如说我们触发了**离屏渲染**,例如我们设置圆角时对maskToBounds的设置,包括一些阴影、蒙层等都会触发GPU层面的离屏渲染,对于这种情况下,GPU对于纹理渲染的工作量就会非常的大,我们可以基于此对GPU进行优化,就是**尽量减少离屏渲染**,我们也可以**通过CPU的异步绘制来减轻GPU的压力** 26 | 2. **视图混合**: 比如说我们视图层级比较复杂,视图之间层层叠加,那么GPU就要做每一个视图的合成,合成每一个像素点的像素值,如果我们可以**减少视图的层级**,也是可以减轻GPU的压力,我们也可以**通过CPU的异步绘制机制来达到一个提交的位图本身就是一个层级比较少的位图** 27 | 28 | 29 | 30 | ### Reference 31 | 32 | https://juejin.im/post/5c0931d451882531b81b20fa#heading-4 33 | -------------------------------------------------------------------------------- /MD/RunLoop/基础/run.md: -------------------------------------------------------------------------------- 1 | ## RunLoop 的启动方式有哪些? 2 | 3 | RunLoop 有 3 种启动方式 4 | 5 | ```swift 6 | open func run() 7 | open func run(until limitDate: Date) 8 | open func run(mode: RunLoop.Mode, before limitDate: Date) -> Bool 9 | ``` 10 | 11 | 12 | 13 | ### run() 14 | 15 | 该方法的作用就是:将接收器置于永久循环中,在此期间处理来自所有附加输入源的数据。其实就是开启 Runloop。 16 | 17 | 如果没有输入源或定时器连接到运行循环,则此方法立即退出;否则,它通过重复调用 `runMode:beforeDate:` 来在 `NSDefaultRunLoopMode` 中运行接收器。 换句话说,这种方法有效地开始一个无限循环,用于处理来自运行循环的输入源和定时器的数据。 18 | 19 | 但是不推荐使用,因为这会使线程进入死循环,从而不利于控制 RunLoop。 20 | 21 | 22 | 23 | ### run(until limitDate: Date) 24 | 25 | 该方法的作用就是:运行循环直到指定的日期,在此期间,它处理所有附加的输入源的数据。类同 run 方法,如果没有事件处理也立刻返回;另外增加了 limitDate 避免了无限循环。 26 | 27 | 28 | 29 | ### run(mode: RunLoop.Mode, before limitDate: Date) -> Bool 30 | 31 | 该方法的作用就是:运行循环一次,等待消息处理(好比在PC终端窗口上等待键盘输入)。一旦有合适事件被处理了,则立刻返回;类同 run 方法,如果没有事件处理也立刻返回;有否事件处理由返回 Bool 判断。同样 limitDate 为超时参数。 32 | 33 | 34 | 35 | ### Reference 36 | 37 | https://www.jianshu.com/p/f4520bdd25c4 38 | 39 | https://www.cnblogs.com/shisishao/p/6564997.html -------------------------------------------------------------------------------- /MD/内存/基础/内存对齐.md: -------------------------------------------------------------------------------- 1 | ## 内存对齐是什么?为什么要内存对齐?对齐的规则? 2 | 3 | ### 内存对齐是什么? 4 | 5 | ```swift 6 | struct Foo { 7 | let num: Int 8 | let bool: Bool 9 | } 10 | ``` 11 | 12 | 一个 `Foo` 对象占用 9 个字节,两个对象占用 18 个字节,那么这 18 个字节在内存中是如何布局的呢? 13 | 14 | ![](../../../Image/内存/stride-nopadding.png) 15 | 16 | 事实证明,这是错的,实际上是这样布局的: 17 | 18 | ![](../../../Image/内存/stride-padding.png) 19 | 20 | `Int` 占 8 个字节,`Bool` 占 1 个字节,但是实际上分配了 16 个字节给这个对象,只不过实际占用了 9 个字节,还有 7 个字节是用于对齐的。 21 | 22 | 23 | 24 | ### 为什么要内存对齐? 25 | 26 | 假设计算机一次读取 4 个字节,假设有一段数据在 2-6 内存中。那么计算机要读取两次,再把读取到的结果进行拆分,最后在拼接在一起,才能获取到这段数据,这样大大降低了计算机的处理速度,所以需要进行内存对齐。 27 | 28 | 29 | 30 | ### 对齐的规则? 31 | 32 | 按照属性中占用最大的对齐,在例子中是 `Int` 类型,占用 8 个字节,内存对齐要是 8 的倍数。 33 | 34 | 35 | 36 | ### 其他问题 37 | 38 | 如果我们把对象中的 `Int` 和 `Bool` 换个位置,会占用多少字节? 39 | 40 | ```swift 41 | struct Foo { 42 | let bool: Bool 43 | let num: Int 44 | } 45 | ``` 46 | 47 | 答案是:16 个字节,内存布局如下图 48 | 49 | ![](../../../Image/内存/alignment-internal-2.png) 50 | 51 | 52 | 53 | ### Reference 54 | 55 | https://swiftunboxed.com/internals/size-stride-alignment/ 56 | -------------------------------------------------------------------------------- /MD/多线程/多线程同步和锁/多读单写.md: -------------------------------------------------------------------------------- 1 | ## 文件读写安全方案(多读单写)有哪几种解决方案? 2 | 3 | ### dispatch_barrier_async 4 | 5 | ```swift 6 | let queue = DispatchQueue(label: "read_write_queue", attributes: .concurrent) 7 | 8 | func read() { 9 | queue.async { 10 | print("Read") 11 | } 12 | } 13 | 14 | func write() { 15 | let task = DispatchWorkItem(flags: .barrier) { 16 | print("Write") 17 | } 18 | queue.async(execute: task) 19 | } 20 | ``` 21 | 22 | 23 | 24 | ### pthread_rwlock 25 | 26 | ```swift 27 | var lock = pthread_rwlock_t() 28 | 29 | func read() { 30 | pthread_rwlock_rdlock(&lock) 31 | print("Read") 32 | pthread_rwlock_unlock(&lock) 33 | } 34 | 35 | func write() { 36 | pthread_rwlock_wrlock(&lock) 37 | print("Write") 38 | pthread_rwlock_unlock(&lock) 39 | } 40 | ``` 41 | 42 | 注意:`pthread_rwlock` 有两种 lock 方式,读的时候用 `pthread_rwlock_rdlock`,写的时候用 `pthread_rwlock_wrlock` 43 | 44 | 45 | 46 | ### Reference 47 | 48 | https://juejin.im/post/5a0a92996fb9a0451f307479#heading-15 49 | 50 | https://www.jianshu.com/p/2d25ad26680d 51 | 52 | -------------------------------------------------------------------------------- /MD/内存/布局/枚举.md: -------------------------------------------------------------------------------- 1 | ## 枚举的内存布局?有原始值的布局是怎样的?有关联值的布局又是怎样的? 2 | 3 | ### 枚举的内存布局? 4 | 5 | ```swift 6 | enum TestEnum { 7 | case a 8 | case b 9 | } 10 | // 分配:1 占用:1 对齐:1 11 | ``` 12 | 13 | 14 | 15 | ### 有原始值的布局是怎样的? 16 | 17 | ```swift 18 | enum TestEnum: String { 19 | case a = "a" 20 | case b = "b" 21 | } 22 | // 分配:1 占用:1 对齐:1 23 | ``` 24 | 25 | 因为 `rawValue` 是计算属性,计算属性不存储在对象内部,所以不会影响对象的内存。 26 | 27 | 28 | 29 | ### 有关联值的布局又是怎样的? 30 | 31 | ```swift 32 | enum TestEnum { 33 | case num(Int, Int) 34 | case other 35 | } 36 | 37 | let testEnum = TestEnum.num(10, 20) 38 | // 分配:24 占用:17 对齐:8 注: Int 占 8 个字节 39 | // 两个 Int 占 2 * 8 = 16 个字节,枚举本身占 1 个字节,一共占 17 个字节 40 | // 由于内存要进行字节对齐,所以要分配 3 * 8 = 24 个字节 41 | ``` 42 | 43 | 问:`TestEnum.other` 的内存是如何布局的?(答案在下方) 44 | 45 | 46 | 47 | ### Reference 48 | 49 | https://juejin.im/post/5d5c0010e51d456201486e4c#heading-1 50 | 51 | https://www.jianshu.com/p/7b930581f01f 52 | 53 | ``` 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | ``` 65 | 66 | 答案:还是占 24 个字节,枚举占用的空间是其中最大元素的空间 +1,就是这个枚举占用的空间。 利用最后一位来分辩是哪个类型。 -------------------------------------------------------------------------------- /MD/UI/事件传递和响应/hitTest.md: -------------------------------------------------------------------------------- 1 | ## hitTest 内部实现逻辑? 2 | 3 | ![](../../../Image/UI/hitTest.png) 4 | 5 | 注意 hitTest 里面是有判断当前的 view 是否支持点击事件,比如 userInteractionEnabled、hidden、alpha 等属性,都会影响一个 view 是否可以相应事件,如果不响应则直接返回 nil。 我们留意到还有一个 pointInside:withEvent: 方法,这个方法跟 hittest:withEvent: 一样都是 UIView 的一个方法,通过他开判断 point 是否在 view 的 frame 范围内。如果这些条件都满足了,那么遍历就可以继续往下走了,代码表现大概如下: 6 | 7 | ```objective-c 8 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { 9 | if (self.hidden || !self.userInteractionEnabled || self.alpha < 0.01 || ![self pointInside:point withEvent:event] || ![self _isAnimatedUserInteractionEnabled]) { 10 | return nil; 11 | } else { 12 | for (UIView *subview in [self.subviews reverseObjectEnumerator]) { 13 | UIView *hitView = [subview hitTest:[subview convertPoint:point fromView:self] withEvent:event]; 14 | if (hitView) { 15 | return hitView; 16 | } 17 | } 18 | return self; 19 | } 20 | } 21 | ``` 22 | 23 | 24 | 25 | ### Reference 26 | 27 | https://zhoon.github.io/ios/2015/04/12/ios-event.html 28 | -------------------------------------------------------------------------------- /MD/Runtime/消息机制/消息发送.md: -------------------------------------------------------------------------------- 1 | ## 消息发送的过程是怎样的? 2 | 3 | ![](../../../Image/Runtime/msg_send.png) 4 | 5 | 1. 当一个对象接收到消息时 `[obj message];`,首先根据 `obj` 的 `isa` 指针进入它的类对象 `cls` 里面。 6 | 2. 在 `obj` 的 `cls` 里面,首先到缓存 `cache_t` 里面查询方法 `message` 的函数实现,如果找到,就直接调用该函数。 7 | 3. 如果上一步没有找到对应函数,在对该 `cls` 的方法列表进行二分/遍历查找,如果找到了对应函数,首先会将该方法缓存到 `obj` 的类对象 `cls` 的 `cache_t` 里面,然后对函数进行调用。 8 | 4. **在每次进行缓存操作之前,首先需要检查缓存容量,如果缓存内的方法数量超过规定的临界值(设定容量的 3/4),需要先对缓存进行 2 倍扩容,原先缓存过的方法全部丢弃,然后将当前方法存入扩容后的新缓存内。** 9 | 5. 如果在 `obj` 的 `cls` 对象里面,发现缓存和方法列表都找不到 `mssage` 方法,则通过 `cls` 的 `superclass` 指针进入它的父类对象 `f_cls` 里面。 10 | 6. 进入 `f_cls` 后,首先在它的 `cache_t` 里面查找 `mssage`,如果找到了该方法,那么会首先将方法缓存到**消息接受者 **`obj` 的类对象 `cls` 的 `cache_t` 里面,然后调用方法对应的函数。 11 | 7. 如果上一步没有找到方法,将会对 `f_cls` 的方法列表进行遍历二分/遍历查找,如果找到了 `mssage` 方法,那么同样,会首先将方法缓存到**消息接受者 **`obj` 的类对象 `cls` 的 `cache_t` 里面,然后调用方法对应的函数。**需要注意的是,这里并不会将方法缓存到当前父类对象 f_cls 的 cache_t 里面。** 12 | 8. 如果还没找到方法,则会通过 `f_cls` 的 `superclass` 进入更上层的父类对象里面,按照 `(6)->(7)->(8)` 步骤流程重复。如果此时已经到了基类对象 `NSObject`,仍没有找到 `mssage`,则进入步骤 `(9)`。 13 | 9. 接下来将会转到消息机制的**动态方法解析**阶段 14 | 15 | 16 | 17 | ### Reference 18 | 19 | https://www.jianshu.com/p/198f031f44ea -------------------------------------------------------------------------------- /MD/多线程/多线程同步和锁/NSConditionLock.md: -------------------------------------------------------------------------------- 1 | ## NSConditionLock 条件锁的理解?怎样使用? 2 | 3 | ### NSConditionLock 条件锁的理解? 4 | 5 | `NSConditionLock` 是借助 `NSCondition` 来实现的,而 `NSCondition `的底层是通过条件变量(condition variable) `pthread_cond_t` 来实现的。条件变量有点像信号量,提供了线程阻塞与信号机制,因此可以用来阻塞某个线程,并等待某个数据就绪,随后唤醒线程。 6 | 7 | 8 | 9 | ### Code 10 | 11 | ```swift 12 | let lock = NSConditionLock(condition: 0) 13 | DispatchQueue.global().async { 14 | self.lock.lock(whenCondition: 0) 15 | print(1) 16 | self.lock.unlock(withCondition: 10) 17 | } 18 | DispatchQueue.global().async { 19 | self.lock.lock(whenCondition: 5) 20 | print(2) 21 | self.lock.unlock() 22 | } 23 | DispatchQueue.global().async { 24 | self.lock.lock(whenCondition: 10) 25 | print(3) 26 | self.lock.unlock(withCondition: 5) 27 | } 28 | 29 | // 1 30 | // 3 31 | // 2 32 | ``` 33 | 34 | 1. 初始化 NSConditionLock 对象时,设置了加锁条件为 0 35 | 2. 执行第一段的时候,传入的条件是 0,所以加锁成功,解锁时将条件设为 10 36 | 3. 执行第二段的时候,传入的条件是 5,不符合当前条件,线程开始等待 37 | 4. 执行第三段的时候,传入的条件是 10,符合当前条件,加锁成功,解锁是将条件设为 5 38 | 5. 第二段线程被唤醒,继续执行 39 | 40 | 41 | 42 | ### Reference 43 | 44 | https://juejin.im/post/57f6e9f85bbb50005b126e5f#heading-14 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 RayJiang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MD/RunLoop/常见问题/UI.md: -------------------------------------------------------------------------------- 1 | ## UI 绘制 setNeedsDisplay 的原理? 2 | 3 | 当在操作 UI 时,比如改变了 Frame、更新了 UIView/CALayer 的层次时,或者手动调用了 UIView/CALayer 的 `setNeedsLayout/setNeedsDisplay` 方法后,这个 UIView/CALayer 就被标记为待处理,并被提交到一个全局的容器去。 4 | 5 | 苹果注册了一个 Observer 监听 BeforeWaiting (即将进入休眠) 和 Exit (即将退出Loop) 事件,回调去执行一个很长的函数: 6 | `_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()`。这个函数里会遍历所有待处理的 UIView/CAlayer 以执行实际的绘制和调整,并更新 UI 界面。 7 | 8 | 这个函数内部的调用栈大概是这样的: 9 | 10 | ``` 11 | _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv() 12 | QuartzCore:CA::Transaction::observer_callback: 13 | CA::Transaction::commit(); 14 | CA::Context::commit_transaction(); 15 | CA::Layer::layout_and_display_if_needed(); 16 | CA::Layer::layout_if_needed(); 17 | [CALayer layoutSublayers]; 18 | [UIView layoutSubviews]; 19 | CA::Layer::display_if_needed(); 20 | [CALayer display]; 21 | [UIView drawRect]; 22 | ``` 23 | 24 | 25 | 26 | ### Reference 27 | 28 | https://blog.ibireme.com/2015/05/18/runloop/ -------------------------------------------------------------------------------- /MD/多线程/基础/进程.md: -------------------------------------------------------------------------------- 1 | ## 进程是什么?有哪几种状态?进程和线程的区别? 2 | 3 | ### 进程是什么? 4 | 5 | 进程是资源分配的基本单位,也是独立运行的基本单位。 6 | 7 | 8 | 9 | ### 有哪几种状态? 10 | 11 | 1. 就绪状态 12 | 2. 执行状态 13 | 3. 阻塞状态 14 | 15 | 就绪 → 执行 16 | 处于就绪状态的进程,当进程调度程序为止分配了处理机后,该进程就由就绪状态转变为执行状态。 17 | 18 | 执行 → 就绪 19 | 处于执行状态的进程在其执行过程中,因分配给它的一个时间片已用完而不得不让出处理机,于是进程从执行状态转变成就绪状态。 20 | 21 | 执行 → 阻塞 22 | 正在执行的进程因等待某种事件发生而无法继续执行时,便从执行状态变成阻塞状态。 23 | 24 | 阻塞 → 就绪 25 | 处于阻塞状态的进程,若其等待的事件已经发生,于是进程由阻塞状态转变为就绪状态。 26 | 27 | 28 | 29 | ### 进程和线程的区别? 30 | 31 | 进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,**一个线程死掉就等于整个进程死掉**,**所以多进程的程序要比多线程的程序健壮**,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。 32 | 33 | 1. 简而言之,一个程序至少有一个进程,一个进程至少有一个线程. 34 | 2. 线程的划分尺度小于进程,使得多线程程序的并发性高。 35 | 3. 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。 36 | 4. 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。 37 | 5. 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。 38 | 39 | 40 | 41 | ### Reference 42 | 43 | https://www.jianshu.com/p/b084fcb5e0fd 44 | 45 | https://www.jianshu.com/p/2b993a4b913e -------------------------------------------------------------------------------- /MD/多线程/多线程同步和锁/各种锁的理解.md: -------------------------------------------------------------------------------- 1 | ## 线程同步的各种锁的理解?有哪几种类型? 2 | 3 | ### 锁有哪几种类型 4 | 5 | #### 互斥锁 6 | 7 | 在编程中,引入对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问对象。 8 | 9 | #### 递归锁 10 | 11 | 同一个线程可以多次加锁,不会造成死锁。 12 | 13 | #### 信号量 14 | 15 | 信号量 (Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。 16 | 17 | #### 条件锁 18 | 19 | `NSCondition` 的对象实际上作为一个锁和一个线程检查器:锁主要为了当检测条件时保护数据源,执行条件引发的任务;线程检查器主要是根据条件决定是否继续运行线程,即线程是否被阻塞。 20 | 21 | #### 自旋锁 22 | 23 | 何谓自旋锁?它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。 24 | 25 | #### 读写锁 26 | 27 | 读写锁实际是一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。这种锁相对于自旋锁而言,能提高并发性,因为在多处理器系统中,它允许同时有多个读者来访问共享资源,最大可能的读者数为实际的逻辑CPU数。写者是排他性的,一个读写锁同时只能有一个写者或多个读者(与CPU数相关),但不能同时既有读者又有写者。 28 | 29 | #### 分布式锁 30 | 31 | 分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。 32 | 33 | 34 | 35 | ### 线程同步的各种锁的理解? 36 | 37 | 详情请看这篇[文章](https://juejin.im/post/5a0a92996fb9a0451f307479) 38 | 39 | 40 | 41 | ### Reference 42 | 43 | https://juejin.im/post/5a0a92996fb9a0451f307479#heading-9 44 | -------------------------------------------------------------------------------- /MD/RunLoop/基础/source.md: -------------------------------------------------------------------------------- 1 | ## RunLoop 的事件源有哪些?特点是什么? 2 | 3 | Run Loop Source 分为 Source、Observer、Timer 三种,他们统称为 ModeItem。 4 | 5 | 6 | 7 | ### CFRunLoopSource 8 | 9 | 根据官方的描述,CFRunLoopSource 是对 input sources 的抽象。CFRunLoopSource 分 source 0 和source 1。 10 | 11 | #### source 0 12 | 13 | 是 App 内部事件,由 App 自己管理的 UIEvent、CFSocket 都是 source 0。 14 | 15 | #### source 1 16 | 17 | 由 RunLoop 和内核管理,可以监听系统端口和通过内核和其他线程通信,接收、分发系统事件,它能够主动唤醒 RunLoop (由操作系统内核进行管理,例如 CFMessagePort 消息)。 18 | 19 | 20 | 21 | ### CFRunLoopObserver 22 | 23 | CFRunLoopObserver 是观察者,可以观察RunLoop的各种状态,并抛出回调。 24 | 25 | CFRunLoopObserver 可以观察的状态有如下 6 种: 26 | 27 | ```swift 28 | public static var entry: CFRunLoopActivity { get } // 即将进入 run loop 29 | public static var beforeTimers: CFRunLoopActivity { get } // 即将处理 timer 30 | public static var beforeSources: CFRunLoopActivity { get } // 即将处理 source 31 | public static var beforeWaiting: CFRunLoopActivity { get } // 即将进入休眠 32 | public static var afterWaiting: CFRunLoopActivity { get } // 被唤醒但是还没开始处理事件 33 | public static var exit: CFRunLoopActivity { get } // run loop 已经退出 34 | public static var allActivities: CFRunLoopActivity { get } 35 | ``` 36 | 37 | Runloop 通过监控 Source 来决定有没有任务要做,除此之外,我们还可以用 Runloop Observer 来监控 Runloop 本身的状态。 Runloop Observer 可以监控上面的 Runloop 事件。 38 | 39 | 40 | 41 | ### CFRunLoopTimer 42 | 43 | CFRunLoopTimer 是定时器,具有以下特点: 44 | 45 | - CFRunLoopTimer 是定时器,可以在设定的时间点抛出回调。 46 | - CFRunLoopTimer 和 NSTimer 是 toll-free bridged 的,可以相互转换。 47 | 48 | 49 | 50 | 51 | 52 | ### Reference 53 | 54 | https://juejin.im/post/5aca2b0a6fb9a028d700e1f8#heading-4 -------------------------------------------------------------------------------- /MD/内存/布局/协议.md: -------------------------------------------------------------------------------- 1 | ## 协议的内存布局?协议的属性存储在什么地方?VWT 是什么?PWT 又是什么? 2 | 3 | 首先考虑一个问题,现在有两个 `struct` 都遵守了一个协议。但是两个 `struct` 内的元素数量不同,这会导致两个 `struct` 的内存大小不一致。如果把两个 `struct` 的实例放到数组中,这会是个灾难,将无法定位数组内元素的位置。 4 | 5 | ```swift 6 | protocol Drawable { 7 | func draw() 8 | } 9 | struct Point : Drawable { 10 | var x, y: Double 11 | func draw() { ... } 12 | } 13 | struct Line : Drawable { 14 | var x1, y1, x2, y2: Double 15 | func draw() { ... } 16 | } 17 | 18 | let a: Drawable = Point() 19 | let b: Drawable = Line() 20 | let drawables : [Drawable] = [a, b] 21 | ``` 22 | 23 | 24 | 25 | 为了解决上述问题,Swift 引入一个叫做 `Existential Container` 的数据结构。思路是:使用一个额外的容器(Container)来放每个带有协议的值类型,而数组里面放的是一个固定大小的容器。 26 | 27 | ![](../../../Image/内存/existential-container.png) 28 | 29 | ![](../../../Image/内存/vwt-pwt.png) 30 | 31 | 前三个 word 是 Value buffer,用于存放元素的值,如果 word 数大于 3,则采用指针的方式,在堆上分配对应需要大小的内存。 32 | 33 | 第四个 word:Value Witness Table (VWT)。每个类型都对应这样一个表,用来存储值的创建,释放,拷贝等操作函数。(管理 Existential Container 生命周期) 34 | 35 | 第五个 word:Protocol Witness Table (PWT),用于存放协议(Protocol)对应的函数的实现函数地址。 36 | 37 | 如果待存放的实例对象大于 3 个 world,Swift就会在堆内存中申请一块空间,将该值保存在堆内存中,堆内存的对应的地址就会保存在 Value Buffer 的第 1 个 word 中。就像下图这样。 38 | 39 | ![](../../../Image/内存/existential-container-value-buffer.png) 40 | 41 | 最终,这种设计使得: 42 | 43 | - 数组中每个元素的大小都是固定的 5 个 word,解决了数组元素下标快速定位的问题。 44 | - 因为有 Value Buffer 的存在,我们可以将不同大小的值类型存放到 Value Buffer 中,小于等于 3 个 word 的值直接存储,更大的则通过保存引用地址的方式存储。 45 | - 通过 Value Witness Table,我们可以找到这个值类型的相关生命周期的管理函数。 46 | - 通过 Protocol Witness Table,我们可以找到协议的具体实现函数的地址。 47 | 48 | 49 | 50 | ### Reference 51 | 52 | https://www.jianshu.com/p/0ca59322c08b -------------------------------------------------------------------------------- /MD/多线程/GCD/队列.md: -------------------------------------------------------------------------------- 1 | ## 创建一个 GCD 队列?各个参数有什么作用? 2 | 3 | ### 创建一个 GCD 队列? 4 | 5 | ```swift 6 | init(label: String, qos: DispatchQoS = default, attributes: DispatchQueue.Attributes = default, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = default, target: DispatchQueue? = default) 7 | ``` 8 | 9 | 10 | 11 | ### 各个参数有什么作用? 12 | 13 | **label** 14 | 15 | 表示该队列的标签 16 | 17 | 18 | 19 | **qos** 20 | 21 | 表示该队列执行的优先级别,有六种优先级可选择。 22 | 23 | ```swift 24 | unspecified 25 | background 26 | default 27 | utility 28 | userInteractive 29 | userInitiated 30 | ``` 31 | 32 | 优先级从高到低依次为 userInteractive > userInitiated > utility > background, 而 default 与 unspecified 介于 userInteractive 与 background 之间,具体有系统决定。 33 | 34 | 35 | 36 | **attributes** 37 | 38 | 除 label 以外的参数都使用默认值时,初始化方法返回的是串行队列,如果需要返回并发队列,参数 attributes 传值为 `.concurrent` 即可。DispatchQueue.Attributes 是一个结构体类型,该结构体提供了两个静态变量:`concurrent` 和 `initiallyInactive` (注意,没有代表串行队列的静态变量)。如果attributes参数传值为 `initiallyInactive` , 任务不会自动执行,而是需要开发者手动调用 `activate()` 触发。但是代码依然是串行进行的,如果想要手动触发、并行执行任务,可以指定 attributes 参数接受一个数组: `[.concurrent, .initiallyInactive]`。 39 | 40 | 41 | 42 | **autoreleaseFrequency** 43 | 44 | DispatchQueue.AutoreleaseFrequency 有三种属性值 45 | `.inherit`:不确定,之前默认的行为也是现在的默认值 46 | `.workItem`:为每个执行的任务创建自动释放池,项目完成时清理临时对象 47 | `.never`:GCD不为您管理自动释放池 48 | 49 | 50 | 51 | **target** 52 | 53 | 用于指定即将创建的队列与队列 target 优先级相同。也可通过 `setTarget(queue: DispatchQueue?)` 函数指定与 queue 相同的优先级。 54 | 55 | 除了开发者自己创建队列,还可以通过 `DispatchQueue.main` 获取主队列(主队列也属于串行队列)、`DispatchQueue.global(qos: DispatchQoS.QoSClass)` 获取全局并发队列。 56 | 57 | 58 | 59 | ### Reference 60 | 61 | https://www.jianshu.com/p/f5bece2b77d8 -------------------------------------------------------------------------------- /MD/Runtime/isa/优化.md: -------------------------------------------------------------------------------- 1 | ## ARM 64 位之后 isa 优化原理? 2 | 3 | ARM 64 位之前 `objc_object` 的结构是: 4 | 5 | ```objective-c 6 | typedef struct objc_object { 7 | Class isa; 8 | }; 9 | ``` 10 | 11 | ARM 64 位之后 `objc_object` 的结构是: 12 | 13 | ```objective-c 14 | struct objc_object { 15 | private: 16 | isa_t isa; 17 | }; 18 | ``` 19 | 20 | `isa_t` 的结构如下: 21 | 22 | ```objective-c 23 | union isa_t 24 | { 25 | isa_t() { } 26 | isa_t(uintptr_t value) : bits(value) { } 27 | 28 | Class cls; 29 | uintptr_t bits; 30 | 31 | # if __arm64__ 32 | # define ISA_MASK 0x0000000ffffffff8ULL 33 | # define ISA_MAGIC_MASK 0x000003f000000001ULL 34 | # define ISA_MAGIC_VALUE 0x000001a000000001ULL 35 | struct { 36 | uintptr_t nonpointer : 1; 37 | uintptr_t has_assoc : 1; 38 | uintptr_t has_cxx_dtor : 1; 39 | uintptr_t shiftcls : 33; // MACH_VM_MAX_ADDRESS 0x1000000000 40 | uintptr_t magic : 6; 41 | uintptr_t weakly_referenced : 1; 42 | uintptr_t deallocating : 1; 43 | uintptr_t has_sidetable_rc : 1; 44 | uintptr_t extra_rc : 19; 45 | 46 | }; 47 | 48 | #endif 49 | ``` 50 | 51 | union 中的 struct 最重要的作用就是解释说明 `bits` 内部的成员信息,就是为了增强可读性,就是为了让人容易看懂。 52 | 53 | 这里精减掉了一些兼容性代码,只保留针对 iOS 部分的代码,我们可以将苹果对 isa 的优化进行概括。通过位运算和位域以及联合体(union)技术,更加充分的利用了 isa 的内存空间,将对象的真正的地址存放在了 isa 内存的其中 33 位上面,其余的 31 位被用来存放对象相关的其他信息。 54 | 55 | 下面是isa其他位上的作用说明: 56 | 57 | - `nonpointer`—— 0,代表普通指针,存储着 class、meta-class 对象的内存地址;1,代表优化过,使用位域存储更多信息 58 | - `has_assoc`—— 是否设置过关联对象,如果没有,施放时会速度更快 59 | - `has_cxx_dtor`—— 是否有 C++ 的稀构函数,如果没有,施放时会更快 60 | - `shiftcls`—— **这个部分存储的是真正的 Class、Meta-Class 对象的内存地址信息**,因此要通过 `isa & ISA_MASK`才能取出这里 33 位的值,得到对象的真正地址。 61 | - `magic`—— 用于在调试的时候分辨对象是否完成了初始化 62 | - `weekly_referenced`—— 是否被弱饮用指针指向过,如果没有,释放时会更快 63 | - `extra_rc`—— 里面存储的值是 **引用计数 - 1** 64 | - `deallocating`——对象是否正在被释放 65 | - `has_sidtable_rc`——引用计数器是否过大无法存储在 isa 中,若果是,这里就为 1,引用计数就会被存储在一个叫 SideTable 的类的属性中。 66 | 67 | 68 | 69 | ### Reference 70 | 71 | https://www.jianshu.com/p/30de582dbeb7 -------------------------------------------------------------------------------- /MD/多线程/PerformSelector/PerformSelector.md: -------------------------------------------------------------------------------- 1 | ## 你对 Perform Selector 几个方法的理解?哪几种方法是同步执行的?哪几种方法是异步执行的? 2 | 3 | 1. 4 | 5 | ```swift 6 | func perform(_ aSelector: Selector!) -> Unmanaged! 7 | func perform(_ aSelector: Selector!, with object: Any!) -> Unmanaged! 8 | func perform(_ aSelector: Selector!, with object1: Any!, with object2: Any!) -> Unmanaged! 9 | ``` 10 | 11 | 这三个方法来自 `NSObject`,均为同步执行,主线程和子线程中均可调用成功。等同于直接调用该方法。在需要动态的去调用方法的时候去使用。 12 | 13 | 2. 14 | 15 | ```swift 16 | open func perform(_ aSelector: Selector, with anArgument: Any?, afterDelay delay: TimeInterval, inModes modes: [RunLoop.Mode]) 17 | open func perform(_ aSelector: Selector, with anArgument: Any?, afterDelay delay: TimeInterval) 18 | ``` 19 | 20 | 这两个方法来自 `NSRunLoop`,是异步执行,即使 delay 传参为 0,仍为异步执行。主线程可以直接调用,子线程调用无效,因为这两个方法本质是使用 `Timer` 启动的,而子线程的 `RunLoop` 默认是不开启的,不会触发 `Timer`,需要在调用 `perform` 后启动 `RunLoop` 才会生效。 21 | 22 | 在方法未到执行时间之前,取消方法为: 23 | 24 | ```swift 25 | open class func cancelPreviousPerformRequests(withTarget aTarget: Any, selector aSelector: Selector, object anArgument: Any?) 26 | open class func cancelPreviousPerformRequests(withTarget aTarget: Any) 27 | ``` 28 | 29 | 3. 30 | 31 | ```swift 32 | open func performSelector(onMainThread aSelector: Selector, with arg: Any?, waitUntilDone wait: Bool, modes array: [String]?) 33 | open func performSelector(onMainThread aSelector: Selector, with arg: Any?, waitUntilDone wait: Bool) 34 | ``` 35 | 36 | 这两个方法来自 `NSThread`,在主线程和子线程中均可执行,均会调用主线程的 aSelector 方法; 37 | 38 | 如果设置 wait 为 true 将阻塞当前线程,等待 `Selector` 执行完之后,才会继续执行后续代码。 39 | 40 | 如果设置 wait 为 false 则不会阻塞当前线程。 41 | 42 | 4. 43 | 44 | ```swift 45 | open func perform(_ aSelector: Selector, on thr: Thread, with arg: Any?, waitUntilDone wait: Bool, modes array: [String]?) 46 | open func perform(_ aSelector: Selector, on thr: Thread, with arg: Any?, waitUntilDone wait: Bool) 47 | ``` 48 | 49 | 调用指定线程中的某个方法。分析效果同3。 50 | 51 | 5. 52 | 53 | ```swift 54 | open func performSelector(inBackground aSelector: Selector, with arg: Any?) 55 | ``` 56 | 57 | 开启子线程在后台运行。 58 | 59 | 60 | 61 | ### Reference 62 | 63 | https://www.jianshu.com/p/fa0933c59c63 64 | 65 | https://juejin.im/post/5bee9218e51d4520b7711fc8 66 | -------------------------------------------------------------------------------- /MD/多线程/多线程同步和锁/pthread_mutex.md: -------------------------------------------------------------------------------- 1 | ## pthread_mutex 锁的理解?有哪几种类型? 2 | 3 | ### pthread_mutex 有哪几种类型? 4 | 5 | - [互斥锁](#互斥锁) 6 | - [递归锁](#递归锁) 7 | - [条件锁](#条件锁) 8 | 9 | 10 | 11 | ### 互斥锁 12 | 13 | ```swift 14 | // 初始化锁 15 | var lock = pthread_mutex_t() 16 | // 设置属性 17 | var attr: pthread_mutexattr_t = pthread_mutexattr_t() 18 | pthread_mutexattr_init(&attr) 19 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL) 20 | pthread_mutex_init(&lock, &attr) 21 | // 销毁锁,用完之后要销毁 22 | pthread_mutex_destroy(&lock) 23 | ``` 24 | 25 | 其中锁的类型如下: 26 | 27 | ```swift 28 | /// 互斥锁,当一个线程加锁后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。 29 | public var PTHREAD_MUTEX_NORMAL: Int32 { get } 30 | /// 检错锁,如果同一个线程请求同一个锁,则抛出一个错误,否则与 PTHREAD_MUTEX_NORMAL 类型动作一致。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。 31 | public var PTHREAD_MUTEX_ERRORCHECK: Int32 { get } 32 | /// 递归锁,允许同一个线程对同一个锁成功获得多次,并通过多次 unlock 解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。 33 | public var PTHREAD_MUTEX_RECURSIVE: Int32 { get } 34 | /// PTHREAD_MUTEX_NORMAL 35 | public var PTHREAD_MUTEX_DEFAULT: Int32 { get } 36 | ``` 37 | 38 | ```swift 39 | DispatchQueue.global().async { 40 | self.test(1) 41 | } 42 | self.test(2) 43 | 44 | func test(_ n: Int) { 45 | pthread_mutex_lock(&lock) 46 | print("Num:\(n) Lock") 47 | Thread.sleep(forTimeInterval: 2) 48 | pthread_mutex_unlock(&lock) 49 | print("Num:\(n) Unlock") 50 | } 51 | 52 | // Num:2 Lock 53 | // Num:2 Unlock 54 | // Num:1 Lock 55 | // Num:1 Unlock 56 | ``` 57 | 58 | 由于现在是互斥锁,所以在两个不同的线程中使用同一把锁,第二次 lock 的时候会阻塞线程,直到另一个线程 unlock 59 | 60 | 61 | 62 | ### 递归锁 63 | 64 | ```swift 65 | // 初始化锁 66 | var lock = pthread_mutex_t() 67 | // 设置属性 68 | var attr: pthread_mutexattr_t = pthread_mutexattr_t() 69 | pthread_mutexattr_init(&attr) 70 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) 71 | pthread_mutex_init(&lock, &attr) 72 | // 销毁锁,用完之后要销毁 73 | pthread_mutex_destroy(&lock) 74 | ``` 75 | 76 | ```swift 77 | func test(_ n: Int) { 78 | if n > 3 { return } 79 | pthread_mutex_lock(&lock) 80 | print("Num:\(n) Lock") 81 | test(n+1) 82 | Thread.sleep(forTimeInterval: 2) 83 | pthread_mutex_unlock(&lock) 84 | print("Num:\(n) Unlock") 85 | } 86 | 87 | // Num:1 Lock 88 | // Num:2 Lock 89 | // Num:3 Lock 90 | // Num:3 Unlock 91 | // Num:2 Unlock 92 | // Num:1 Unlock 93 | ``` 94 | 95 | 递归锁是同一个线程可以多次获得同一个锁,其他线程如果想要获取这把锁,必须要等待,这种锁一般都是用于递归函数的情况。 96 | 97 | 98 | 99 | ### 条件锁 100 | 101 | ```swift 102 | // 创建条件 103 | var cond = pthread_cond_t() 104 | pthread_cond_init(&cond, nil) 105 | ``` 106 | 107 | ```swift 108 | /// 删除 109 | @objc func remove() { 110 | pthread_mutex_lock(&lock) 111 | 112 | if array.isEmpty { 113 | print("Wait") 114 | // 数据为空就等待(进入休眠,放开锁,被唤醒后,会再次加锁) 115 | pthread_cond_wait(&cond, &lock) 116 | } 117 | 118 | array.removeFirst() 119 | print("Remove") 120 | pthread_mutex_unlock(&lock) 121 | } 122 | /// 添加 123 | @objc func add() { 124 | pthread_mutex_lock(&lock) 125 | 126 | array.append(1) 127 | print("Add") 128 | // 激活一个等待该条件的线程 129 | pthread_cond_signal(&cond) 130 | // 激活所有等待该条件的线程 131 | //pthread_cond_broadcast(&_cond) 132 | 133 | pthread_mutex_unlock(&lock) 134 | } 135 | 136 | // 调用 137 | Thread(target: self, selector: #selector(remove), object: nil).start() 138 | Thread(target: self, selector: #selector(add), object: nil).start() 139 | 140 | // Wait 141 | // Add 142 | // Remove 143 | ``` 144 | 145 | 上面这段代码,add 和 remove 方法是在两个线程中调用的,先执行 remove 方法,发现没有数据,开始等待;add 方法添加数据后,激活了等待的线程,remove 方法继续执行。 146 | 147 | 148 | 149 | ### Reference 150 | 151 | https://juejin.im/post/5cf72a42e51d454f71439c8c#heading-13 152 | 153 | https://juejin.im/post/5d554410f265da03b21532cd#heading-15 -------------------------------------------------------------------------------- /MD/Runtime/数据结构/数据结构.md: -------------------------------------------------------------------------------- 1 | ## Runtime 基础数据结构有哪些? 2 | 3 | ```objective-c 4 | typedef struct objc_class *Class; 5 | typedef struct objc_object *id; 6 | ``` 7 | 8 | ### objc_object 9 | 10 | ```objective-c 11 | /// 对象 - 64 位之前的结构 12 | struct objc_object { 13 | Class isa; // isa 直接指向 objc_class 14 | }; 15 | 16 | /// 对象 - 64 位之后的结构 17 | struct objc_object { 18 | private: 19 | isa_t isa; 20 | }; 21 | ``` 22 | 23 | 24 | 25 | ### objc_class 26 | 27 | ```objective-c 28 | /// 类 29 | struct objc_class : objc_object { 30 | Class isa; 31 | Class superclass; // 用于获取父类,也就是元类对象,它也是一个 Class 类型 32 | cache_t cache; // 方法缓存 33 | class_data_bits_t bits; // 用于获取具体的类信息 34 | 35 | class_rw_t *data() { 36 | return bits.data(); 37 | } 38 | void setData(class_rw_t *newData) { 39 | bits.setData(newData); 40 | } 41 | }; 42 | ``` 43 | 44 | 45 | 46 | ### class_rw_t & class_ro_t 47 | 48 | 紧接着有一个 `class_rw_t *data()` 函数,该函数的作用就是获取该类的**可读写信息**,通过 `class_data_bits_t` 的 `bits.data()` 方法获得,点进该方法看一下。 49 | 50 | ```objective-c 51 | class_rw_t* data() { 52 | return (class_rw_t *)(bits & FAST_DATA_MASK); 53 | } 54 | ``` 55 | 56 | 这里是将类对象里面的`class_data_bits_t bits;`和一个`FAST_DATA_MASK`进行`&`运算取得。返回的是一个指针,类型为`class_rw_t *`,查看该类型的源码如下。 57 | 58 | ```objective-c 59 | struct class_rw_t { 60 | uint32_t flags; 61 | uint32_t version; 62 | 63 | const class_ro_t *ro; 64 | 65 | // 通过 Category 添加的方法会放到 methods 中 66 | method_array_t methods; // 方法列表 67 | property_array_t properties; // 属性列表 68 | protocol_array_t protocols; // 协议列表 69 | 70 | Class firstSubclass; 71 | Class nextSiblingClass; 72 | 73 | char *demangledName; 74 | } 75 | ``` 76 | 77 | ```objective-c 78 | struct class_ro_t { 79 | uint32_t flags; 80 | uint32_t instanceStart; 81 | uint32_t instanceSize; // instance 对象占用的内存空间 82 | #ifdef __LP64__ 83 | uint32_t reserved; 84 | #endif 85 | 86 | const uint8_t * ivarLayout; 87 | 88 | const char * name; // 类名 89 | method_list_t * baseMethodList; // 方法列表 90 | protocol_list_t * baseProtocols; // 协议列表 91 | const ivar_list_t * ivars; // 成员变量列表 92 | 93 | const uint8_t * weakIvarLayout; 94 | property_list_t *baseProperties; // 属性列表 95 | 96 | method_list_t *baseMethods() const { 97 | return baseMethodList; 98 | } 99 | }; 100 | ``` 101 | 102 | 实际上,`class_ro_t` 代表 Class 的只读信息,也就是 Class 本身的固有信息,再直接一点就是是写在它的 @interface 和 @end 之间的方法,属性,等信息,当然最重要的作用还是存放类的成员变量信息 ivars,而且是被 const 修饰说明是不可修改的,这也就是为什么 Runtime 无法动态增加成员变量,底层结构决定的。我个人将这部分理解成OC的静态信息。`class_ro_t` 中的 `method_list_t * baseMethodList;`,是一个一维数组,里面装的就是这个 Class 本身的方法。 103 | 104 | ![](../../../Image/Runtime/class_ro_t_method_list.png) 105 | 106 | 在有了 `class_rw_t` 之后,便会进行 category 的处理,将 Class 本身的方法列表和 category 里面的方法列表先后放到 `class_rw_t` 的 `method_array_t methods` 里面,Class 自身的方法列表会被最先放入其中,并且置于列表的尾部,category 方法列表的加入顺序等同与 category 文件参与编译的顺序,这部分流程的详细说明在[Objective-C之Category的底层实现原理](https://www.jianshu.com/p/6d20c40cb06e)一文里有详细介绍。因此,`method_array_t methods;` 是一个二维数组。 107 | 108 | ![](../../../Image/Runtime/class_rw_t_method_array.png) 109 | 110 | 上面 3 个类总结如图: 111 | 112 | ![](../../../Image/Runtime/objc_class_class_data_bits_t.png) 113 | 114 | 这个图可以理解成稳定状态下,Class 的内部结构。但事实上,在程序启动和初始化过程中,Class 并不是这样的结构,具体过程见这篇[文章](https://www.jianshu.com/p/af2d806a9d61),这里不展开了。 115 | 116 | 117 | 118 | ### method_t 119 | 120 | 上面我们剖析了 `class_rw_t`、`class_ro_t `这两个重要部分的结构,并且主要关注了其中的方法列表部分,而从上面的分析,可发现里面最基本也是重要的单位是 `method_t`,这个结构体包含了描述一个方法所需要的各种信息。 121 | 122 | ```objective-c 123 | struct method_t { 124 | SEL name; // 方法选择器 125 | const char *types; // 函数类型编码 126 | IMP imp; // 指向函数的指针 127 | }; 128 | ``` 129 | 130 | 这 3 个属性也不展开了,具体看这篇[文章](https://www.jianshu.com/p/af2d806a9d61)。 131 | 132 | 133 | 134 | ### cache_t 135 | 136 | 在 `objc_class` 类中有 `cache_t` 这个属性,顾名思义这就是用来缓存方法的。它的底层是通散列表(哈希表)的数据结构来实现的,用于缓存曾经调用过的方法,可以提高方法的查找速度。正常情况下方法调用的流程如下: 137 | 138 | - `obj` -> `isa` -> `obj` 的 `Class` 对象 -> `method_array_t methods` -> 对该表进行遍历查找,找到就调用,没找到继续往下走 139 | - `obj` -> `superclass` -> `obj`的父类 -> `isa` -> `method_array_t methods` -> 对父类的方法列表进行遍历查找,找到就调用,没找到就重复本步骤 140 | - 找到就调用,没找到重复流程 141 | - 找到就调用,没找到重复流程 142 | - 找到就调用,没找到重复流程 143 | - 直到 `NSObject` -> `isa` -> `NSObject` 的 `Class` 对象 -> `method_array_t methods` 144 | 145 | 如果某个方法在程序内会被频繁的调用,那么这种逐层便利查找的方式肯定是效率低下的,因此苹果设计了 `cache_t cache`,当某个第一次被调用的时候,会按照常规流程查找,找到之后,就会被加入到 `cache_t cache` 中,当再次被调用的时候,系统就会直接现到 `cache_t cache` 来查找,找到就直接调用,这样便大大提升了查找的效率。 146 | 147 | ```objective-c 148 | struct cache_t { 149 | struct bucket_t *_buckets; // 用来缓存方法的散列/哈希表 150 | mask_t _mask; // 这个值 = 散列表长度 - 1 151 | mask_t _occupied; // 表示已经缓存的方法的数量 152 | } 153 | ``` 154 | 155 | ```objective-c 156 | struct bucket_t { 157 | private: 158 | cache_key_t _key; // 方法的 SEL,也就是方法名 159 | IMP _imp; // 方法对应的函数的内存地址 160 | } 161 | ``` 162 | 163 | 关于缓存还有很多知识点,比如如何处理哈希碰撞,什么时候扩容等,具体看这篇[文章](https://www.jianshu.com/p/a1a91d91641b) 164 | 165 | 166 | 167 | 168 | 169 | ### Reference 170 | 171 | [Runtime 源码](https://github.com/opensource-apple/objc4) 172 | 173 | https://www.jianshu.com/p/30de582dbeb7 【建议阅读】 174 | 175 | https://www.jianshu.com/p/af2d806a9d61 176 | 177 | https://www.jianshu.com/p/a1a91d91641b -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swift-Review 2 | 3 | 本项目的问题来自脉脉职言区的[帖子](https://taou.cn/plVM0),看到之后感觉挺有意思的,有一些知识会用但是不知道原理。借着这次机会深入的了解一下,顺便对自己的知识点进行一次查漏补缺。问题的答案有的是自己写的,有的是摘录博客上的,如有错误欢迎指出。目前只更新了一部分问题的答案,之后会慢慢更新的,如果你有兴趣,欢迎你一起回答问题。 4 | 5 | 6 | 7 | ## 目录 8 | 9 | - [Swift 底层本质](#Swift-底层本质) 10 | - [关键字](#关键字) 11 | - [探究本质](#探究本质) 12 | - [特性和优化](#特性和优化) 13 | - [UI](#UI) 14 | - [图像显示、卡顿优化和离屏渲染相关的问题](#图像显示、卡顿优化和离屏渲染相关的问题) 15 | - [图片加载](#图片加载) 16 | - [视图绘制](#视图绘制) 17 | - [事件传递/响应机制](#事件传递/响应机制) 18 | - [TableView](#TableView) 19 | - [内存](#内存) 20 | - [内存基础](#内存基础) 21 | - [内存布局](#内存布局) 22 | - [循环引用](#循环引用) 23 | - [自动释放池](#自动释放池) 24 | - [Copy on write](#Copy-on-write) 25 | - [多线程](#多线程) 26 | - [多线程基础](#多线程基础) 27 | - [GCD](#GCD) 28 | - [多线程同步和锁](#多线程同步和锁) 29 | - [Perform Selector](#Perform-Selector) 30 | - [RunLoop](#RunLoop) 31 | - [RunLoop 基础](#RunLoop-基础) 32 | - [Runloop 的常见问题](#Runloop-的常见问题) 33 | - [Runloop 的应用](#Runloop-的应用) 34 | - [Runtime](#Runtime) 35 | - [Runtime 数据结构](#Runtime-数据结构) 36 | - [isa 指针](#isa-指针) 37 | - [Runtime 消息机制](#Runtime-消息机制) 38 | - [Runtime 的实际应用](#Runtime-的实际应用) 39 | - [Swift 中的 Runtime](#Swift-中的-Runtime) 40 | - [KVO & KVC](#KVO-&-KVC) 41 | - [KVC](#KVC) 42 | - [KVO](#KVO) 43 | - [Swift 中的 KVO 和 KVC](#Swift-中的-KVO-和-KVC) 44 | - [网络相关](#网络相关) 45 | - [网络基础](#网络基础) 46 | - [网络常见问题](#网络常见问题) 47 | - [数据结构和算法](#数据结构和算法) 48 | - [数据结构基础](#数据结构基础) 49 | - [算法](#算法) 50 | - [实际问题](#实际问题) 51 | - [其他常见算法](#其他常见算法) 52 | 53 | 54 | 55 | 56 | 57 | ## Swift 底层本质 58 | 59 | ### 关键字 60 | 61 | - [Self 和 self 的区别?](MD/Swift底层本质/关键字/Self和self的区别.md) 62 | - [.self 的理解?](MD/Swift底层本质/关键字/self.md) 63 | - [.type 和 type(of: ) 的区别?](MD/Swift底层本质/关键字/type.md) 64 | - [Any, AnyObject, Anyclass 的区别?](MD/Swift底层本质/关键字/Any.md) 65 | - [is, isKind, isMenber 的区别?](MD/Swift底层本质/关键字/is.md) 66 | - [throws 和 rethrows 的区别?](MD/Swift底层本质/关键字/throws.md) 67 | - [open, public, internal, fileprivate, private 的区别?](MD/Swift底层本质/关键字/权限.md) 68 | 69 | 70 | 71 | ### 探究本质 72 | 73 | #### Swift 各种属性的本质 74 | 75 | - [let 和 var 的区别?](MD/Swift底层本质/探究本质/let和var的区别.md) 76 | - [计算型属性的本质是什么?占多少个字节?是存储在当前对象里的吗?可以用 let 修饰吗?](MD/Swift底层本质/探究本质/计算属性.md) 77 | - [枚举的原始值的本质是什么?占几个字节?它在内存中是存储在枚举里吗?](MD/Swift底层本质/探究本质/枚举.md) 78 | - [枚举可以定义存储属性吗?枚举可以定义类型存储属性吗?](MD/Swift底层本质/探究本质/枚举2.md) 79 | - [lazy 属性可以用 let 修饰吗?lazy 属性是线程安全的吗?](MD/Swift底层本质/探究本质/lazy.md) 80 | - [观察型属性在初始化的时候会触发吗?定义的时候给定默认值会触发吗?](MD/Swift底层本质/探究本质/观察属性.md) 81 | - [inout 修饰的函数参数本质是什么?](MD/Swift底层本质/探究本质/inout.md) 82 | - [inout 参数能传递计算属性吗?传递计算属性的底层原理是什么?](MD/Swift底层本质/探究本质/inout2.md) 83 | - [inout 参数传递观察型属性会触发观察的 willSet 和 didSet 方法吗?底层原理是什么?为什么这样设计?](MD/Swift底层本质/探究本质/inout3.md) 84 | - 类型存储属性和 lazy 一样是延迟加载吗?如果一样那么是线程安全的吗?为什么? 85 | 86 | 87 | 88 | #### String, Array, Option 本质 89 | 90 | - [String 类型占多少个字节?String 类型变量的字面量在内存中是怎样存储的?](MD/Swift底层本质/探究本质2/String.md) 91 | - [数组在内存中占多少个字节?数组存储在栈空间还是堆空间?](MD/Swift底层本质/探究本质2/Array.md) 92 | - [可选类型的本质?](MD/Swift底层本质/探究本质2/Option.md) 93 | 94 | 95 | 96 | #### Swift 闭包的本质 97 | 98 | - [闭包是什么?闭包表达式和闭包是什么关系?](MD/Swift底层本质/闭包/闭包.md) 99 | - [闭包值捕获的原理是什么?捕获到的值存储在哪里?](MD/Swift底层本质/闭包/闭包捕获.md) 100 | - [捕获多个值时它们在内存中是连续存储的吗?]((MD/Swift底层本质/闭包/闭包捕获2.md)) 101 | - [一个捕获到 Int 值的闭包在内存中占几个字节?]((MD/Swift底层本质/闭包/闭包捕获3.md)) 102 | - [DispatchQueue.async 闭包体内为什么要强制加 self. 访问成员变量?](MD/Swift底层本质/闭包/DispatchQueue.md) 103 | - [逃逸闭包是什么?](MD/Swift底层本质/闭包/逃逸闭包.md) 104 | 105 | 106 | 107 | #### Swift 多态&方法派发 108 | 109 | - [Swift 里是怎样实现多态的?](MD/Swift底层本质/多态/多态.md) 110 | - [Swift 支持哪些方法派发方式?引用类型、值类型、协议的方法派发有什么不同?](MD/Swift底层本质/多态/方法派发.md) 111 | - [为什么建议使用 struct 而不使用 class?](MD/Swift底层本质/多态/Q1.md) 112 | 113 | 114 | 115 | #### Swift 里的指针 116 | 117 | - [Swift 里有那几种类型的指针?有什么区别?](MD/Swift底层本质/指针/指针.md) 118 | 119 | 120 | 121 | ### 特性和优化 122 | 123 | #### 函数和协议编程 Swift 反射机制 Swift 性能优化 124 | 125 | - [大概描述一下 Swift 的编译流程?Swift 和 OC 的区别?](MD/Swift底层本质/特性和优化/Swift编译流程.md) 126 | - 面向协议编程的理解?对函数式编程的理解? 127 | - [map, flatMap, compactMap 的区别?](MD/Swift底层本质/特性和优化/map.md) 128 | - [filter, reduce 的区别?](MD/Swift底层本质/特性和优化/filter-reduce.md) 129 | - [对反射机制的理解?](MD/Swift底层本质/特性和优化/反射.md) 130 | - [如何优化 Swift 性能?](MD/Swift底层本质/特性和优化/优化.md) 131 | 132 | 133 | 134 | 135 | 136 | ## UI 137 | 138 | ### 图像显示、卡顿优化和离屏渲染相关的问题 139 | 140 | - [图像绘制的原理和过程?](MD/UI/图像/图像绘制.md) 141 | - [卡顿掉帧的原因?卡顿掉帧应该怎么优化?](MD/UI/图像/卡顿掉帧.md) 142 | - [什么是离屏渲染?为什么会有离屏渲染机制?离屏渲染消耗性能的原因?](MD/UI/图像/离屏渲染.md) 143 | - [哪些场景会触发离屏渲染?怎么解决?](MD/UI/图像/Q1.md) 144 | 145 | 146 | 147 | ### 图片加载 148 | 149 | - 图片加载优化原理 150 | - 如何设计一个图片缓存框架?缓存清理怎样设计? 151 | - UllmageView 的 name和 contentOfFile 方法有什么区别?注意点? 152 | - iOS 图片加载的详细流程是什么?应该怎样去优化? 153 | - 简单说一下图片后台强制解压缩的流程? 154 | 155 | 156 | 157 | ### 视图绘制 158 | 159 | - 视图绘制的全流程有哪些阶段? 160 | - 什么是异步绘制,怎样进行异步绘制? 161 | - 系统绘制的流程是怎样的?视图绘制优化方案?drawRect 注意点? 162 | 163 | 164 | 165 | ### 事件传递/响应机制 166 | 167 | - [手指触摸屏幕后发生了什么?事件的传递和响应链是怎么样的?](MD/UI/事件传递和响应/事件.md) 168 | - [hitTest 内部实现逻辑?](MD/UI/事件传递和响应/hitTest.md) 169 | - 事件传递具体有哪些应用场景? 170 | 171 | 172 | 173 | ### TableView 174 | 175 | - 对 TableView 重用机制的理解? 176 | - 如何实现一个自定义的重用池? 177 | - 重用可能带来的问题,平常是怎么解决的? 178 | - 重用 Cell 的获取方式和区别? 179 | - 多线程情况下数据源同步方案? 180 | - TableView 常用方法的理解和注意点? 181 | - TableView 的一般优化思路是什么? 182 | 183 | 184 | 185 | 186 | 187 | ## 内存 188 | 189 | ### 内存基础 190 | 191 | - [iOS 内存布局结构?](MD/内存/基础/iOS内存布局.md) 192 | - [堆区和栈区的区别?为什么要设计堆和栈,主要解决哪些问题?](MD/内存/基础/堆区栈区.md) 193 | - Swift 对象堆空间申请过程? 194 | - Swift 里 let 和 var 变量的内存布局有何不同? 195 | - [内存对齐是什么?为什么要内存对齐?对齐的规则?](MD/内存/基础/内存对齐.md) 196 | - [引用计数的存储方式?](MD/内存/基础/引用计数.md) 197 | - [ARC 在编译时和运行时分别做了哪些工作?](MD/内存/基础/ARC.md) 198 | - [retain, release 的实现机制?](MD/内存/基础/retain-release.md) 199 | - 你对 iOS 内存管理的理解? 200 | 201 | 202 | 203 | ### 内存布局 204 | 205 | - [结构体的内存布局?](MD/内存/布局/结构体.md) 206 | - [类的内存布局?](MD/内存/布局/类.md) 207 | - [枚举的内存布局?有原始值的布局是怎样的?有关联值的布局又是怎样的?](MD/内存/布局/枚举.md) 208 | - [协议的内存布局?协议的属性存储在什么地方?VWT 是什么?PWT 又是什么?](MD/内存/布局/协议.md) 209 | - Swift 和 OC 类对象内存布局的区别? 210 | 211 | 212 | 213 | ### 循环引用 214 | 215 | - [对循环引用的理解?强引用和弱引用的区别?](MD/内存/循环引用/循环引用.md) 216 | - [weak 和 unowned 有什么区别?](MD/内存/循环引用/weak-unowned.md) 217 | - [weak 指针实现原理?为什么对象销毁后会被置为 nil?](MD/内存/循环引用/weak.md) 218 | - 在 SideTable 里的存取过程又是怎样的?Side Table 的组成?为什么有多张 Side Table?Side Table 为什么会有一把自旋锁? 219 | - 说说循环引用的场景和解决思路?闭包为什么会产生循环引用?手写循环引用例子 220 | 221 | 222 | 223 | ### 自动释放池 224 | 225 | - [什么是自动释放池?自动释放池的管理原理是怎样的?](MD/内存/自动释放池/autoreleasepool.md) 226 | - [AutoreleasePool 和 Runloop 的关系?](MD/内存/自动释放池/autoreleasepool-runloop.md) 227 | 228 | 229 | 230 | ### Copy on write 231 | 232 | - [什么是 Copy on write?](MD/内存/copy-on-write/copy-on-write.md) 233 | - [如何为结构体手动实现 Copy on write?](MD/内存/copy-on-write/Q1.md) 234 | - Swift 对象的深度复制(使用 Codable 协议) 235 | 236 | 237 | 238 | 239 | 240 | ## 多线程 241 | 242 | ### 多线程基础 243 | 244 | - [进程是什么?有哪几种状态?进程和线程的区别?](MD/多线程/基础/进程.md) 245 | - [什么是并发?什么是并行?并发和并行的区别?](MD/多线程/基础/并发.md) 246 | - [对多线程的理解?多线程的底层原理?多线程的优缺点?](MD/多线程/基础/多线程理解.md) 247 | - [多线程有哪些实现方案?](MD/多线程/基础/多线程方案.md) 248 | - [怎样实现一个常驻线程?自定义 Runloop 的应用线程保活?](MD/多线程/基础/常驻线程.md) 249 | - [多线程会有哪些安全隐患?一般有什么解决方案?](MD/多线程/基础/多线程隐患.md) 250 | - [死锁产生的条件有哪些?](MD/多线程/基础/死锁.md) 251 | - [多线程间怎么通信?底层原理是什么?](MD/多线程/基础/多线程通信.md) 252 | - NSThread(对应 Swift 中的 Thread)内部实现的原理是什么?启动流程又是怎样的?2 种初始化方法有什么区别? 253 | 254 | 255 | 256 | ### GCD 257 | 258 | - [创建一个 GCD 队列?各个参数有什么作用?](MD/多线程/GCD/队列.md) 259 | - [GCD 有哪几种队列?主队列和全局队列分别是什么队列?](MD/多线程/GCD/队列2.md) 260 | - [GCD 队列的执行方式有什么区别?不同队列不同执行方式的区别?主队列异步执行多个任务会开启新线程吗?为什么?](MD/多线程/GCD/队列3.md) 261 | - [GCD 什么情况会发生死锁?原因是什么?](MD/多线程/GCD/死锁.md) 262 | - [GCD 任务提交方式有哪些?DispatchWorkItem 提交有什么好处?](MD/多线程/GCD/任务提交.md) 263 | - [GCD 延迟执行 DispatchTime 和 DispatchWillTime 有什么区别?](MD/多线程/GCD/延迟执行.md) 264 | - [DispatchSource 的理解?](MD/多线程/GCD/DispatchSource.md) 265 | - [DispatchSourceTimer 和 Timer 比哪个更精准的?](MD/多线程/GCD/DispatchSourceTimer.md) 266 | - [DispatchGroup 的底层原理是什么?一般用在什么场景?有哪几种添加进组的方式?](MD/多线程/GCD/DispatchGroup.md) 267 | - [barrier 的理解?](MD/多线程/GCD/栅栏.md) 268 | - [DispatchSemaphore 的理解?对信号量控制方法的理解?信号量底层原理是怎样?](MD/多线程/GCD/DispatchSemaphore.md) 269 | 270 | 271 | 272 | ### 多线程同步和锁 273 | 274 | - [多线程同步方案有哪些?哪些锁的性能最好?](MD/多线程/多线程同步和锁/锁.md) 275 | - [线程同步的各种锁的理解?有哪几种类型?](MD/多线程/多线程同步和锁/各种锁的理解.md) 276 | - [OSSpinLock 不安全的原因?](MD/多线程/多线程同步和锁/OSSpinLock.md) 277 | - [os_unfair_lock 怎样使用?](MD/多线程/多线程同步和锁/os_unfair_lock.md) 278 | - [pthread_mutex 锁的理解?有哪几种类型?](MD/多线程/多线程同步和锁/pthread_mutex.md) 279 | - [NSLock 和 NSRecursiveLock 的理解?](MD/多线程/多线程同步和锁/NSLock.md) 280 | - [NSConditionLock 条件锁的理解?怎样使用?](MD/多线程/多线程同步和锁/NSConditionLock.md) 281 | - [文件读写安全方案(多读单写)有哪几种解决方案?](MD/多线程/多线程同步和锁/多读单写.md) 282 | 283 | 284 | 285 | ### Perform Selector 286 | 287 | - [你对 Perform Selector 几个方法的理解?哪几种方法是同步执行的?哪几种方法是异步执行的?](MD/多线程/PerformSelector/PerformSelector.md) 288 | 289 | 290 | 291 | 292 | 293 | ## RunLoop 294 | 295 | ### RunLoop 基础 296 | 297 | - [什么是 RunLoop?](MD/RunLoop/基础/RunLoop.md) 298 | - [RunLoop 的启动方式有哪些?](MD/RunLoop/基础/run.md) 299 | - [RunLoop 的退出方式有哪些?](MD/RunLoop/基础/stop.md) 300 | - [RunLoop 和线程有什么关系?线程间如何通信?](MD/RunLoop/基础/thread.md) 301 | - [RunLoop 有哪几种 mode?对常见 mode 的理解?](MD/RunLoop/基础/mode.md) 302 | - [RunLoop 的事件源有哪些?特点是什么?](MD/RunLoop/基础/source.md) 303 | - [RunLoop 的监听状态有哪些?怎样监听?](MD/RunLoop/基础/observer.md) 304 | - [RunLoop 的内部循环逻辑是怎样的?](MD/RunLoop/基础/loop.md) 305 | 306 | 307 | 308 | ### Runloop 的常见问题 309 | 310 | - [RunLoop 和 AutoreleasePool 的关系?](MD/RunLoop/常见问题/AutoreleasePool.md) 311 | - [事件响应的过程?](MD/RunLoop/常见问题/事件响应.md) 312 | - [手势识别的过程?](MD/RunLoop/常见问题/手势识别.md) 313 | - [UI 绘制 setNeedsDisplay 的原理?](MD/RunLoop/常见问题/UI.md) 314 | - [定时器滑动失效的原因?怎样处理?为什么 Timer 不精准?如何实现精准的定时?](MD/RunLoop/常见问题/Timer.md) 315 | - [Perform Selector after Delay 的实现原理?](MD/RunLoop/常见问题/PerformSelector.md) 316 | - [GCD 和 Runloop 的关系?](MD/RunLoop/常见问题/GCD.md) 317 | 318 | 319 | 320 | ### Runloop 的应用 321 | 322 | - [如何实现一个常驻线程?](MD/RunLoop/应用/常驻线程.md) 323 | 324 | 325 | 326 | 327 | 328 | ## Runtime 329 | 330 | Runtime 相关的知识点一环扣一环,如果你对 Runtime 还不了解,建议先阅读[这个系列](https://www.jianshu.com/p/30de582dbeb7)的文章,读完之后下面这些问题就都迎刃而解了。 331 | 332 | ### Runtime 数据结构 333 | 334 | - [Runtime 中的数据结构有哪些?](MD/Runtime/数据结构/数据结构.md) 335 | 336 | 337 | 338 | ### isa 指针 339 | 340 | - [isa 的指向关系?](MD/Runtime/isa/isa.md) 341 | - [实例对象、类对象和元类对象的联系和区别有哪些?](MD/Runtime/isa/实例对象、类对象和元类.md) 342 | - [ARM 64 位之后 isa 优化原理?](MD/Runtime/isa/优化.md) 343 | 344 | 345 | 346 | ### Runtime 消息机制 347 | 348 | - [消息发送的过程是怎样的?](MD/Runtime/消息机制/消息发送.md) 349 | - [动态方法解析阶段的过程?](MD/Runtime/消息机制/动态方法解析.md) 350 | - [消息转发的过程是怎样的?](MD/Runtime/消息机制/消息转发.md) 351 | 352 | 353 | 354 | ### Runtime 的实际应用 355 | 356 | - Runtime 常用的 API 有哪些? 357 | - 平常有用过 Runtime?一般来干什么?怎样实现? 358 | 359 | 360 | 361 | ### Swift 中的 Runtime 362 | 363 | - 你对 Swit 中 Runtime 的理解? 364 | 365 | 366 | 367 | 368 | 369 | ## KVO & KVC 370 | 371 | ### KVC 372 | 373 | - 什么是 KVC? 374 | - KVC 的本质? 375 | - KVC 的实现机制是怎样的? 376 | - KVC 设/取值流程是怎样的? 377 | - KVC 修改属性时如果该属性被 KVO 观察的话会触发 KVO 吗?为什么? 378 | 379 | 380 | 381 | ### KVO 382 | 383 | - 什么是 KVO? 384 | - KVO 的本质是什么? 385 | - KVO 的实现机制是怎样的? 386 | - KVO 设取值观察原理是怎样的?派生类的内部实现逻辑又是怎样的? 387 | 388 | 389 | 390 | ### Swift 中的 KVO 和 KVC 391 | 392 | - Swift 中有没有 KVC?原理是什么? 393 | - Swift 中如何使用 KVO?需要注意什么? 394 | 395 | 396 | 397 | 398 | 399 | ## 网络相关 400 | 401 | ### 网络基础 402 | #### HTTP 403 | 404 | - 你是怎样理解 HTTP?具体包含哪些内容?报文结构? 405 | - HTTP 请求方案?状态码的含义? 406 | - POST 请求体常见格式? 407 | - GET/POST 的区别?从语义角度?常规角度? 408 | - GET 安全性的理解?幂等性的理解?可缓存的理解? 409 | - HTTP 连接和断开流程?三次握手流程?为什么3次而不是2次?四次挥手流程?为什么4次? 410 | - HTTP 的特点?对无连接的理解?为什么HTTP要持久连接? 411 | - 持久连接涉及到的头部字段?怎样判断一个持久连接的请求是否结束? 412 | 413 | 414 | 415 | #### TCP/UDP 416 | 417 | - 简单说一下 TCP/UDP 首部格式? 418 | - TCP/UDP 的特点? 419 | - UDP 无连接的理解?面向报文的理解? 420 | - TCP 面向连接的理解?TCP 为什么是可靠的?原理是什么?可靠传输有什么特点? 421 | - TCP 面向字节流的理解? 422 | - TCP 流量控制的理解?原理是什么? 423 | - TCP 拥塞控制的理解?有哪几个阶段?过程是什么?什么是快速重传机制? 424 | - TCP 建立连接的过程?为什么3次握手而不是2次?为什么要4次挥手? 425 | 426 | 427 | 428 | #### DNS 解析 429 | 430 | - DNS 的理解? 431 | - 查询方式? 432 | - 如何防劫持? 433 | - 和HTTP有关系吗? 434 | 435 | 436 | 437 | #### Session 和 Cookie 438 | 439 | - Session 和 Cookie 的理解? 440 | - 交互流程? 441 | - 有什么区别? 442 | 443 | 444 | 445 | ### 网络常见问题 446 | 447 | - 怎样实现文件的断点下载?基本原理是什么? 448 | - 如何处理大文件的上传下载?边下边写基本原理?分段读取基本原理? 449 | - Alamofire 的理解?有哪几个模块?请求的过程? 450 | - Moya 的理解?主要解决什么问题? 451 | 452 | 453 | 454 | 455 | 456 | ## 数据结构和算法 457 | 458 | ### 数据结构基础 459 | 460 | - 你对数据结构的理解,什么是逻辑结构?什么是物理结构?常见数据结构有哪些?有什么特点? 461 | - 线性表的特点是什么?说一下线性链表和顺序表的优缺点对比?各自适用什么场景? 462 | - 栈的概念?有哪些基本操作?特点?什么是假溢出? 463 | - 队列的概念?有啷些基本操作?特点? 464 | - 什么是树?树的度?树的深度又是什么? 465 | - 什么是二叉树?满二叉树的概念?完全二叉树的概念? 466 | - 二叉树的先序遍历、中序遍历、后序遍历方式是怎样的? 467 | 468 | 469 | 470 | ### 算法 471 | 472 | #### 排序 473 | 474 | - 冒泡排序 475 | - 选择排序 476 | - 插入排序 477 | - 快速排序 478 | 479 | 480 | 481 | #### 链表 482 | 483 | - 寻找单链表的中间元素? 484 | - 判断一个链表是否有环?有环则找出入口节点?有环则找出环上节点数? 485 | - 判断 2 个无环单链表是否相交?相交则找出交点? 486 | - 反转单链表? 487 | - 合并 2 个有序单链表? 488 | - 找到链表的倒数第 n 个节点? 489 | - 删除链表内倒数第 n 个节点? 490 | - 旋转单链表? 491 | - 倒序打印链表节点值? 492 | - 删除有序链表中等于给定值的所有节点? 493 | - 删除有序链表中值重复的节点(去重和重复的都删除 2 种情况)? 494 | - 划分链表相关问题?奇偶链表? 495 | 496 | 497 | 498 | #### 二叉树 499 | 500 | - 求二叉树深度? 501 | - 反转一颗二又树? 502 | - 平衡二叉树判断?对称二叉树判断?相同二叉树判断? 503 | - 二叉搜索树的查找? 504 | 505 | 506 | 507 | ### 实际问题 508 | 509 | - 寻找两个 View 共同父视图? 510 | - 查找 View 上的所有 Button 控件(包含子 View)? 511 | - 查找 View 所在的视图控制器? 512 | 513 | 514 | 515 | ### 其他常见算法 516 | 517 | - 字符串反转 518 | - 只出现过一次的字符 519 | - 有序数组合并 520 | - 寻找数组中只出现一次的数(除了一个出现一次,其他都出现 2 次) 521 | --------------------------------------------------------------------------------