zoukankan      html  css  js  c++  java
  • Effective Objective-C 2.0读书笔记(七)

    第37条,理解“块”这一概念(重点)

    • 多线程的核心是块(block)和GCD(Grand Central Dispatch)。
    • 块是一种可以在C、C++、及OC代码中使用的“词法闭包”。
    • 块的语法结构:return_type (^block_name)(paramerers)
    • 块的强大之处在于:声明它的范围内,所有变量都可以为其所捕获。
    • 默认情况下,为块所捕获的变量,是不可以在块里修改的,如果在声明变量的时候加上__block 修饰符,就可以在块内修改了。
    • 块本身可视为对象,有引用计数。
    • 由于self本事也是对象,如果self本事指向的对象包含了块,那么在块中使用self就会导致“保留环”。
    • 块的内部结构:isa,指向Class对象的指针;flag;reserved;invoke,函数指针,指向块的实现代码;descriptor,指向结构体的指针。
    • 全局块、栈块、堆块:编译器会给每个块分配栈内存,等离开相应的范围(块的花括号内)后,编译器可能把分配给块的内存覆写掉。再另外调用块方法的时候,没覆写,不报错,覆写了,就报错。为了防止这种覆写导致的程序出错,可以给块发送copy消息,这样能把块从栈复制到堆了,这时候的堆块可以在定义它的范围外调用了。
    • 在堆块里,块是带引用计数的对象,堆块复制属于浅拷贝,只会递增引用计数。
    • 全局块不会捕捉任何状态(外围变量),运行时无须有状态参与,块使用的整个内存区域,在编译器已经完全确定的,全局块可以声明在全局内存里,不需要在每次的栈中创建,全局块的拷贝是空操作。

    关键词:block、栈块、堆块、全局块

    第38条,为常用的块类型创建 typedef(重点)

    • 为了隐藏复杂的块类型,需要用到C语言中的“类型定义”(type definition)的特性。
    • block类型定义样式:typedef int(^block_class_name)(parameters);
    • block类型定义后的使用: block_class_name name = ^(parameters){};
    • 以typedef重新定义的块类型,可令块变量用起来更加简单。
    • 定义新类型时应遵循现有命名习惯,勿使其名称与别的类型相冲突。
    • 不妨为同一个块签名定义多个类型别名。如果要重构的代码使用了块类型的某个别名,那么只需要修改相应typedef中的块签名即可,无须改动typedef。

    关键词:typedef、block类型定义

    第39条,用 handler块降低代码分散程度

    • API的回调用block(块)来实现比用委托实现更紧凑,代码更完整。
    • 在创建对象时,可以使用内联的handler块将相关业务逻辑一并声明。
    • 在有多个实例需要监控时,如果采用委托模式,那么经常需要根据传入的对象来切换,而若改用handler块来实现,则可直接将块与相关对象放在一起。
    • 设计API时如果用到了handler块,那么可以增加一个参数,使调用者可以通过此参数来决定应该把块安排在哪个队列上执行。

    关键词:handler

    第40条,用块引用其所属对象时不要出现保留环

    • 如果块所捕获的对象直接或间接地保留了块本身,那么就得当心保留环问题。
    • 一定要找个适当的时机解除保留环,而不能把责任推给API的调用者。

    关键词:块引用保留环

    第41条,多用派发队列,少用同步锁(重点)

    • OC中多个线程使用同一份代码,需要使用到同步机制,GCD之前使用“同步块”( synchronization block)。
    • 滥用@synchronzed(self)会降低代码效率。
    • 各种锁:@synchronized 加锁的对象需要是同一个对象;NSLock 对象锁,多次lock死锁;NSRecursiveLock 递归锁,场景限制;NSConditionLock 条件锁;pthread_mutex (C语言)互斥锁 linux 底层;dispatch_semaphore (GCD),信号量;OSSpinLock (不建议使用)。
    • 死锁(deadlock):多个进程因竞争资源而造成的一种僵局(互相等待)。
    • 死锁的四个必要条件:互斥条件:一个资源每次只能被一个进程使用,即在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
    • 不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。循环等待条件: 若干进程间形成首尾相接循环等待资源的关系。
    • GCD是一种更简单、更高效的锁。
    • 属性的原子性(atomic)本质就是在set/get方法里加@synchronized,虽然能保证操作属性的“原子性”,但是并不能保证线程安全,同一个线程上多次调用get方法,获取的结果不一定相同。因为两次get间,其他线程可能会写入新的属性值。
    • 解决属性读写操作数据同步,线程安全的办法就是“串行同步队列”,即把设置和获取操作都安排在序列化的队列里执行。
    • 执行异步派发时,需要拷贝块,会影响执行性能。
    • GCD中的栅栏:在并发队列中,栅栏块必须单独执行,不与其他块并行。
    • 派发队列可用来表述同步语义,这种做法要比使用@synchronized块或者NSLock对象更简单。
    • 将同步与异步派发结合起来,可以实现与普通加锁机制一样的同步行为,而这么做却不会阻塞执行异步派发的线程。
    • 使用同步队列及栅栏块,可以令同步行为更加高效。
    • 关键词:互斥锁、递归锁、死锁、GCD、原子性、串行队列、并发队列、同步、异步、栅栏块、进程、线程

    第42条,多用 GCD,少用 performSelector 系列方法

    • performSelector系列方法在内存管理方面容易有疏失。它无法确定将要执行的选择子具体指什么,因而ARC编译器也就无法插入适当的内存管理方法。
    • performSelector系列的方法所能处理的选择子太过局限性了,选择子的返回值类型及发送给方法的参数个数都受到限制。
    • 如果想把任务放在另一个线程上执行,那么最好不要用performSelector系列方法,而是应该把任务封装到块里,然后调用GCD派发机制的相关方法来实现。

    关键词:performSelector

  • 相关阅读:
    Codeforces Round #547 F1&F2. Same Sum Blocks(贪心)
    Codeforces Round #547 D. Colored Boots(贪心)
    Codeforces Round #547 C. Polycarp Restores Permutation(二分枚举/数学+模拟)
    CCF 201812-4 数据中心(最小生成树)
    CCF【小明放学&小明上学】
    TIME_WAIT状态
    ping的详细过程
    两段不相邻子段和之和最大
    神水一题之“Who's in the Middle”
    日进一步之“A Knight's Journey”
  • 原文地址:https://www.cnblogs.com/akiha/p/13267670.html
Copyright © 2011-2022 走看看