美文网首页
Effective Objective-C 2.0读书笔记(6)

Effective Objective-C 2.0读书笔记(6)

作者: _桃夭大人_ | 来源:发表于2019-02-21 10:01 被阅读7次

第六章 块与大中枢([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);

在队列中,栅栏块必须单独执行,不能与其他块并行。
并发队列如果发现接下来要处理的块是个栅栏,那么就一直要等当前所有并发块都执行完毕,才会单独执行这个栅栏块。

image.png

第四十二条:多用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

相关文章

网友评论

      本文标题:Effective Objective-C 2.0读书笔记(6)

      本文链接:https://www.haomeiwen.com/subject/zenxyqtx.html