本月聚焦了 block 与 GCD 的一些用法,同时阅读了《Effective Objective-C 2.0》一书的后面几个章节。特别需要注意的是 block 何时在栈内何时在堆中。
Week 24
June 11 to June 17
Algorithm:
[Leetcode - easy] 344. Reverse String
1 | // takes a string as input and returns the string reversed. |
Review:
When declaring Blocks based API, always declare functions and methods to take only one block and always make that block the last argument.
声明bolck作为API时,总是声明方法和函数的最后一个参数为block,并保持只有一个block。
Blocks are created on the stack. Careful.
注意:blcok在栈中创建。
__block is a distinct storage type__block是一个显式存储类型。
Blocks retain object references (but not all)
block retain对象引用(但不总是)。
When should you copy or retain a block? In general, you shouldn’t need to copy or retain a block.
什么时候需要copy或者retain一个blcok?一般来说,不需要copy或者retain一个block。
Blocks are Objective-C Objects
block是Objective-C对象
Blocks can be recursive
block可以递归
First copy of a Block moves it to heap, the rest are cheap
block第一次copy时会移动到堆
Tip:
Swift
1 | func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result |
Returns the result of combining the elements of the sequence using the given closure.
Use the reduce(_:_:) method to produce a single value from the elements of an entire sequence.
1 | let numbers = [1, 2, 3, 4] |
Share:
we should copy blocks rather than retain.
1 | typedef void (^SuccessCallBack)(NSDictionary *dataDic); |
Week 25
June 18 to June 24
Algorithm:
[Leetcode - easy] 412. Fizz Buzz
1 | func fizzBuzz(_ n: Int) -> [String] { |
Review:
KVO Considered Harmful – Soroush Khanlou
作者认为KVO是一个糟糕的API。
理由如下:
KVO的所有的监听都通过一个方法 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context ,这会导致多个被监听的属性都会在这个方法内处理
KVO是字符串形式的,监听的属性依赖于字符串,无法知道属性是什么类型,甚至不知道属性是否存在。
KVO需要自己控制父类的监听
取消一个监听两次可能导致APP崩溃,这会发生在父类也在观察同一个属性时,当第二次取消监听时会发生crash
KVO是含蓄的
KVO可能导致无限循环,必须确保观测的属性在响应时没有改变
KVO是一个陈旧的方法,一些其他模式可以替代,比如代理、通知中心、blcok回调等
总结:
两种情况下可以用,
第一种是Apple需要的时候。比如AVPlayer类中,文档需要观测status属性。
第二种是设计API给别人用时。
Tip:
《Effective Objective-C 2.0》
Chapter 6: Blocks and Grand Central Dispatch
Item 37: Understand Blocks
block可以分配在栈或者堆上,也可以是全局的。分配在栈上的block可以拷贝到堆上,堆上的block成为对象,具有引用计数。
Item 38: Create typedefs for Common Block Types
为常用的block类型创建typedef
Item 39: Use Handler Blocks to Reduce Code Separation
用Handle Blocks降低代码分散程度
Item 40: Avoid Retain Cycles Introduced by Blocks Referencing the Object Owning Them
如果block所捕获的对象直接或间接保留了block本身,那么就要当心循环引用问题
Item 41: Prefer Dispatch Queues to Locks for Synchronization
多用派发队列,少用同步锁
Item 42: Prefer GCD to performSelector and Friends
多用GCD,少用performSelector系列方法
Item 43: Know When to Use GCD and When to Use Operation Queues
何时用GCD,何时用 NSOperationQueue
Item 44: Use Dispatch Groups to Take Advantage of Platform Scaling
通过Dispatch Group机制,根据系统资源状况来执行任务
Item 45: Use dispatch_once for Thread-Safe Single-Time Code Execution
使用dispatch_once来执行只需要运行一次的线程安全代码
Item 46: Avoid dispatch_get_current_queue
避免使用dispatch_get_current_queue
Share:
通过GCD中的 dispatch_barrier_(a)sync 加强对sync中所谓等待的理解
dispatch_barrier_sync 和 dispatch_barrier_async 的共同点:
- 都会等待在它前面插入队列的任务(1、2、3)先执行完
- 都会等待他们自己的任务(0)执行完再执行后面的任务(4、5、6)
dispatch_barrier_sync 和 dispatch_barrier_async 的不共同点:
在将任务插入到queue的时候,dispatch_barrier_sync 需要等待自己的任务(0)结束之后才会继续程序,然后插入被写在它后面的任务(4、5、6),然后执行后面的任务;而 dispatch_barrier_async 将自己的任务(0)插入到queue之后,不会等待自己的任务结束,它会继续把后面的任务(4、5、6)插入到queue
Week 26
June 25 to July 01
Algorithm:
[Leetcode - easy] 263. Ugly Number
1 | func isUgly(_ num: Int) -> Bool { |
Review:
在一次用户的touch交互中,是hit-test决定了Application的整个view层次结构中,到底该由哪个view去接收并处理该事件。
- touch事件发生,创建UIEvent对象
- 按照Application的view层次结构,逐层调用每个view的
hitTest:withEvent:方法,并传入该event对象,view根据hitTest:withEvent:方法和来决定touch点是否包含在自己的bounds中; - 如果view的bounds包含了touch点,该view会遍历自己的subview,并调用每个subview的
pointInside:withEvent:方法来进一步决定touch事件是发生在自己本身,还是自己的subview上。 - 重复第二,三步,并筛选出最终接受touch事件的view对象
enabled属性会忽略touch events,并可能改变绘制,比如button.enabled = NO意味着button灰色并且不能响应touches(继承自UIControl);userInteractionEnabled表示user events是否会被忽略并从event queue中移除,比如button.userInteractionEnabled = NO意味着显示正常,但是不能响应touches(继承自UIView)。
Tip:
《Effective Objective-C 2.0》
Chapter 7: The System Frameworks
系统框架
Item 47: Familiarize Yourself with the System Frameworks
熟悉系统框架,最重要的是Foundation和CoreFoundation,此外还有:
CFNetwork,提供C语言级别的网络通信能力。CoreAudio,提供C语言API可用来操作设备上的音频硬件。AVFoundation,提供Objective-C对象用来回放并录制音频及视频。CoreData,提供Objective-C接口可将对象放入数据库,便于持久保存。CoreTest,提供C语言接口可以高效执行文字排版及渲染操作。
Item 48: Prefer Block Enumeration to for Loops
多用块枚举,少用for循环
- 遍历collection有四种方式。最基本的办法是
for循环,其次是NSEnumerator遍历法及for in快速遍历法,最新、最先进的方式则是块枚举法 Block Enumeration。 - 块枚举法本身就是通过GCD来并发执行遍历操作,无须另行编写代码。而采用其他遍历方式则无法轻易实现。
- 若提前知道待遍历的collection含有何种对象,则应修改块签名,指出对象的具体类型。
Item 49: Use Toll-Free Bridging for Collections with Custom Memory-Management Semantics
对自定义其内存管理语义的collection使用无缝桥接toll-free bridging。
- 通过无缝桥接技术,可以在Foundation框架中的Objective-C对象与CoreFoundation框架中的C语言数据结构之间来回转换。
- 在CoreFoundation层面创建colletion时,可以指定许多回调函数,这些函数表示此colletion应如何处理其元。素。然后,可运用无缝桥接技术,将其转换成具备特殊内存管理语义的
Objective-C collection。
Item 50: Use NSCache Instead of NSDictionary for Caches
构建缓存时选用NSCache而非NSDictionary
- 实现缓存时应选用
NSCache而非NSDictionary对象。因为NSCache可以提供优雅的自动删减功能,而且是“线程安全的”,此外,与NSDictionary不同,NSCache不会拷贝key。 - 可以给NSCache对象设置上限,用以限制缓存中的对象总个数及“总成本”,而这些尺度则定义了缓存删减其中对象的时机。但是绝对不要把这些尺度当成可靠的
hard limit,它们仅对NSCache起指导作用。 - 将
NSPurgeableData与NSCache搭配使用,可实现自动清除数据的功能,也就是说,当NSPurgeableData对象所占内存为系统所丢弃时,该对象自身也会从缓存中移除。 - 如果缓存使用得当,那么应用程序的响应速度就能提高。只有那种“重新计算起来很费事的”数据才值得缓存,比如那些需要从网络获取或从磁盘读取的数据。
Item 51: Keep initialize and load Implementations Lean
精简initialize与load的实现代码
- 在加载阶段,如果类实现了load方法,那么系统就会调用它。分类里也可以定义此方法,
class的load方法要比category中的先调用。与其他方法不同,load方法不参与覆写机制。 - 首次使用某个类之前,系统会向其发送i
nitialize消息。由于此方法遵从普通的覆写规则,所以通常应该在里面判断当前要初始化的是哪个类。 load与initialize方法都应该实现的精简一些,- 无法在编译期设定的全局常量,可以放在
initialize方法里初识化。
Item 52: Remember that NSTimer Retains Its Target
记住NSTimer会保留其目标对象
- NSTimer对象会保留其目标,直到计时器本身失效为止,调用
invalidate方法可令计时器失效,另外,一次性的计时器在触发完任务之后也会失效。 - 反复执行任务的计时器(
repeating timer),很容易引入保留环,如果这种计时器的目标对象又保留了计时器本身,那肯定会导致保留环。这种环状保留关系,可能是直接发生的,也可能是通过对象图里的其他对象间接发生的。 - 可以扩充NSTimer的功能,用Block来打破保留环。不过,除非NSTimer将来在公共接口里提供此功能,否则必须创建分类,将相关实现代码加入其中。
Share:
__weak与__block区别
__weak 本身是可以避免循环引用的问题的,但是其会导致外部对象释放了之后,block 内部也访问不到这个对象的问题,我们可以通过在 block 内部声明一个 __strong 的变量来指向 weakObj,使外部对象既能在 block 内部保持住,又能避免循环引用的问题。
__block 本身无法避免循环引用的问题,但是我们可以通过在 block 内部手动把 blockObj 赋值为 nil 的方式来避免循环引用的问题。另外一点就是 __block 修饰的变量在 block 内外都是唯一的,要注意这个特性可能带来的隐患。