Chapter 6. Blocks and Grand Central Dispatch
<br />
Item 46: Avoid dispatch_get_current_queue
<br />
这一节讲dispatch_get_current_queue可能会引起的死锁问题。这是一个已经废弃的方法。
主要原因就是不同的queue之间可以以嵌套的形式构成一个树状的结构。比如如果存在A->B这样的父子关系,在B中采用同步操作的话,会把A锁死,就和在主线程进行一个向主队列同步派发的操作是一样的。而dispatch_get_current_queue只能判断当前的queue,不能判断这种队列嵌套关系,所以无法避免这种死锁。书中给的例子:
dispatch_sync(queueA, ^{
dispatch_sync(queueB, ^{
dispatch_block_t block = ^{/*…*/};
if (dispatch_get_current_queue() == queueA) {
block();
}
else {
dispatch_sync(queueA, block);
}
});
});
这里是想做一个判断,想让内部操作不运行在队列A上以免死锁,但是没法做到,因为dispatch_get_current_queue()
只能返回队列B。我觉得有点像class cluster的结构,但是class cluster有isKindOfClass:来判断是不是属于某个父类的分支,这里没有这种很直接的方法。
有一个解决方法是使用queue-specific data,给队列关联了一个键值对,根据键来判断是哪个队列,当没有找到关联的值时,会自动沿着树的分支向上查找直到根节点。方法名是dispatch_queue_set_specific
。在上面这个例子中,如果给A关联了键值对,在队列B的block中,判断这个键对应的值是否存在,只有不存在时(也就是B不在A的子树中)才把同步操作派发到A,此时就不会死锁了。
Chapter 7. The System Frameworks
<br />
Item 47: Familiarize Yourself with the System Frameworks
<br />
这一节简介系统框架。
一开始提到了动态库与静态库。查了一段解释:
When a C program is compiled, the compiler generates object code. After generating the object code, the compiler also invokes linker. One of the main tasks for linker is to make code of library functions (eg printf(), scanf(), sqrt(), ..etc) available to your program. A linker can accomplish this task in two ways, by copying the code of library function to your object code, or by making some arrangements so that the complete code of library functions is not copied, but made available at run-time.
Static Linking and Static Libraries is the result of the linker making copy of all used library functions to the executable file. Static Linking creates larger binary files, and need more space on disk and main memory.
Dynamic linking and Dynamic Libraries Dynamic Linking doesn’t require the code to be copied, it is done by just placing name of the library in the binary file. The actual linking happens when the program is run, when both the binary file and the library are in memory.
iOS的系统框架都是采用的动态库。常见的比如Foundation和CoreFoundation,都是提供基础核心功能的框架,其中后者是C语言框架,两个框架中的数据结构可以toll-free bridging。C语言框架的优点是速度更快,但是需要注意内存管理的问题,因为ARC是只对OC对象起作用。
其他提到的系统框架有CFNetwork, CoreAudio, AVFoundation, CoreData, CoreText, AppKit, UIKit等,基本都听说过吧,然而大多没怎么用过_(:з」∠)_
<br />
Item 48: Prefer Block Enumeration to ‘for’ Loops
<br />
这一节讲对collection对象做enumeration的各种方法。
第一个是for循环。for循环做的是有序遍历。我之前不知道的是,对于无序的collection对象,比如set和dictionary,做for循环时需要先把对象复制到一个有序数组上,然后进行有序遍历。所以这里存在额外空间开销。
快速遍历是我之前最常用的,只要不是一定要用到下标的遍历,都是采用的快速遍历。如果要使自定义的类可以实现快速遍历,需要实现NSFastEnumeration协议。
还可以使用NSEnumerator。这是一个抽象类,只有一个属性allObjects,只有一个方法nextObject。写法很统一,像这样:
NSArray *anArray = /*…*/;
NSEnumerator *enumerator = [anArray objectEnumerator];
id object;
while ((object = [enumerator nextObject] != nil)) {
// Do something with ‘object’
}
这里第二行,不同的collection定义了返回不同enumerator对象的方法。比如NSDictionary可以返回keyEnumerator和objectEnumerator,也可以返回反向的enumerator。
最后是采用block进行遍历。以NSArray的API为例,提供了三个方法:
- (void)enumerateObjectsUsingBlock:(void (^)(ObjectType obj,
NSUInteger idx,
BOOL *stop))block
- (void)enumerateObjectsWithOptions:(NSEnumerationOptions)opts
usingBlock:(void (^)(ObjectType obj,
NSUInteger idx,
BOOL *stop))block
- (void)enumerateObjectsAtIndexes:(NSIndexSet *)indexSet
options:(NSEnumerationOptions)opts
usingBlock:(void (^)(ObjectType obj,
NSUInteger idx,
BOOL *stop))block
参数名字已经自解释得很清楚了。block的优势在于,可以在遍历中提供更多的参数供使用,比如idx,比如何时stop。另外如果确定collection里遍历的对象是什么类型,这里的ObjectType可以进行修改,这样编译器就可以知道具体的类型,就会有方法自动补全,当发现调用了本类不存在的方法时可以及时报错。opt这个参数的解释:"A bit mask that specifies the options for the enumeration (whether it should be performed concurrently and whether it should be performed in reverse order)." 就是说可以方便地进行逆向循环和并发循环。