- Effective Objective-C 2.0 读书笔记
- Effective Objective-C 2.0笔记(二)
- Effective Objective-C 2.0笔记(一)
- Effective Objective-C 2.0笔记(三)
- Effective Objective-C 2.0笔记(五)
- 《Effective Objective-C 2.0 》 阅读笔
- Effective Objective-C 2.0 无废话精简篇
- Effective Objective-C 2.0 Tips 总
- Effective Objective-C 2.0 脑图- [O
- iOS开发读书笔记:Effective Objective-C
第六章 块与大中枢([shū])派发
第三十七条:理解“块”这一概念
“块”可以实现闭包。
(1)块的语法结构
return_type (^block_name)(parameters)
int (^addBlock)(int a,int b) = ^(int a, int b){
return a + b;
}
-
块的强大之处:在声明它的范围内,所有变量都可以为其所捕获。
-
默认情况下,为块所捕获的变量,是不可以在块里 修改的。若要修改,需要在声明变量时加上__block
__block NSInterger count = 0;
-
内联块 把所有业务逻辑都放到一起。
-
块本身可视为 对象
块所捕获的变量时对象类型,那么就会自动保留它,块回收时,也会释放块所捕获的变量。
(2) 块的内部结构
块对象的内存布局
- invoke变量 :函数指针,指向块的实现代码。void*型的参数代表块。
- descriptor变量:指向结构体的指针,每个块都包含此结构体。其中声明了块对象的总体大小,还声明了copy和dispose这两个辅助函数所对应的函数指针。
块会把它所捕获的所有变量都拷贝一份,拷贝的不是对象本身,而是指向这些对象的指针变量。
(3)块的分类
- 栈块
定义块的时候,其所占的内存区域是分配在栈中的。块只在定义它的那个范围内有效。 - 堆块
给块对象 发送copy消息,复制到推中。块就变成了带引用计数的对象了, - 全局块
不会捕获任何状态,运行时也无须有状态来参与。
全局块所使用的整个内存区域,在编译期已经完全确定了,因此全局块可以声明在全局内存里。而不在每次用的时候在栈中创建。
第三十八条:为常用的块类型创建typedef
- typedef 关键字用于给类型起个易读的别名
// 新增了一个名为EOCSomeBlock的类型
typedef int (^EOCSomeBlock)(BOOL flag, int value);
// 使用新类型创建block
EOCSomeBlock block = ^(BOOL flag, int value){
};
第三十九条:用handler块降低代码分散程度
与使用委托模式的代码相比,用块写出来的代码更简洁。
第四十条:用块引用其所属对象时 不要出现保留环
- 如果块 所捕获的对象直接 或者间接地保留块本身,那么就要当心保留环问题。
- 一定要找到适当的时机解除保留环,不能把责任推给API调用者。
第四十一条:多用派发队列,少用同步锁
如果有多个线程要执行同一份代码,通常要使用锁 来实现某种同步机制。
(1)同步块
@synchronized(self){
// 自动创建一个锁,并等待块中的代码执行完毕。
// 共用同一个锁的那些同步块,都必须按顺序执行。
}
(2)NSLock对象
_lock = [[NSLock alloc]init];
-(void)synchronizedMethod{
[_lock lock];
//
[_lock unlock];
}
(3)GCD
-
串行同步队列
将读取操作及写入操作都安排在同一队列里,即可保证数据同步。 -
并发队列
-
栅栏(barrier)
写入操作单独执行,读取操作并发执行。
// 异步栅栏
void dispatch_barrier_async(dispatch_queue_t queue,disapatch_block_t block);
// 同步栅栏
void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block);
在队列中,栅栏块必须单独执行,不能与其他块并行。
并发队列如果发现接下来要处理的块是个栅栏,那么就一直要等当前所有并发块都执行完毕,才会单独执行这个栅栏块。
第四十二条:多用GCD,少用performSelector系列方法
- -(id)performSelector:(SEL)selector
该方法与直接调用选择子等效,
[object performSelector:@selector(selectorName)];
[object selectorName];
用途:简化复杂代码 在动态绑定之上再次使用动态绑定
image.png image.png image.png
- performSelector系列方法在内存管理方面容易有疏失。它无法确定将要执行的选择子具体是什么,因而ARC编译器也就无法插入适当的内存管理方法。
- performSelector 系列方法所能处理的选择子太过局限了,选择子的返回类型以及发送给方法的参数个数都受到限制。
第四十三条:掌握GCD及操作队列的使用时机
- NSOperationQueue(操作队列)
把操作以NSOperation子类的形式放到队列中,并发操作。
区别
- GCD是纯C的API,操作队列是Objective-C的对象。
- GCD是轻量级数据结构,操作队列是重量级的Objective-C对象。
好处- NSOperation 可以取消某个操作
- NSOperation 可以指定操作间的依赖关系
- 通过键值观察机制监控NSOperation对象属性
- NSOperation 可以指定操作的优先级
- 重用NSOperation对象
第四十四条:通过Dispatch Group机制,根据系统资源状况来执行任务
-
dispatch group 是GCD的一项特性,能够把任务分组。
把将要并发执行的多个任务合为一组,就可以知道这些任务何时才能全部执行完毕。 -
创建dispatch group
dispatch_group_t dispatch_group_create();
- 表示待执行的块 所属的组
void dispatch_group_async(dispatch_group_t group,dispatch_queue_t queue, dispatch_block_t block);
- 保留操作
void dispatch_group_enter(dispatch_group_t group);
- 释放操作
void dispatch_group_leave(dispatch_group_t group);
保留操作与 释放操作彼此对应,以防内存泄漏
- 用于等待dispatch group执行完毕
long dispatch_group_wait(dispatch_group_t froup,dispatch_time_t timeout);// timeout表示在等待执行完毕时,应该阻塞多久
- 组内任务全部完成时调用
void dispatch_group_notify(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);
- 此函数会将块反复执行一定次数,每次传给块的参数值都会递增,
dispatch_apply(10, queue,^(size_t i) ){
// dispatch_apply 所用的队列可以是并发队列,会持续阻塞直到所有任务都执行完毕为止。
});
第四十五条:使用dispatch_once 来执行只需要运行一次的线程安全代码
- 单利模式
// 为了保证线程安全,将创建单利实例代码 放到同步块里
+ (id)sharedInstance{
static EOCClass * sharedInstance = nil;
@synchronized(self){
if(!sharedInstance){
sharedInstance = [[self alloc] init];
}
}
return sharedInstance;
}
dispatch_once更高效,没有使用重量级的同步机制
// 该函数保证相关的块必会执行,且仅执行一次 完全线程安全
+ (id)sharedInstance{
static EOCClass * sharedInstance = nil;// 由于每次调用时都必须使用完全相同的标记所以要用static,不会创建新变量;
static dispatch_once_t onceToken ;
dispatch_once(&onceToken,^{
sharedInstance = [[self alloc] init];
});
}
第四十六条:不要使用dispatch_get_current_queue
-
此函数返回当前正在执行代码的队列。
因为在回调块里调用dispatch_get_current_queue所返回的“当前队列”,不是其调用api时指定的哪一个,而是api内部的那个同步队列。
image.png -
解决方法:通过GCD所提供的功能来设定"队列特有数据",此功能可以把任意数据以键值对的形式关联到队列里。如果找不到关联数据就会 沿着层级体系向上查找,直到找到数据或者达到根队列。
void dispatch_queue_set_specific(dispatch_queue_t queue,const void *key,void *context, dispatch_function_t destructor);
// 函数是按照指针值来比较键的
image.png
网友评论