读书笔记

2017/02/03

Objective-C 高效编程

ARC

在ARC中,虽然不用手动管理对象的引用计数,但是还是要明确每个变量的所有权修饰符,才能让ARC正常工作。

  1. __strong 是 对象类型和id类型的默认所有权修饰符
  2. __strong 修饰的变量 ,ARC 在作用域内,未结束之前,假如让变量指空nil,编译也会马上插入release ,让他立即释放。不然的话 ARC会在这个变量的作用域结束时,大括号结束时,插入这个变量的 release操作立刻释放。
  3. __autoreleasing 修饰的变量,那么他们会在运行时 beforewaiting事件的 autoreleasPool 释放时,自动释放。
  4. 如果仅有__strong将无法避免引用循环问题,所以引入 __weak__unsafe_unretained 两个弱引用所有权修饰符。
  5. 作为alloc/new/copy/mutableCopy方法返回值取得的对象,可以直接返回,除此之外的方法,编译器都会在返回时插入 objc_autorelaseReturnValue(result) 将对象放入AutoReleasPool 和接受时插入 objc_retainAutoreleasedReturnValue(result),retain一次这个返回值,这里有一个优化的策略,一旦这两个方法挨在一起成对儿出现 ,他们通过函数栈上的操作,直接将要放回的对象传递给函数的调用方,而省去放入AutoReleasPool的步骤。编译器是根据调用方法的名字来判定如果处理返回值的引用计数,所以遇见 按条件执行 performSetelctor:copy/new/doSomething 时编译器无法插入正确的维护引用计数的代码。会造成内存泄露。
  6. __autoreleasing 当使用对象指针的指针时,id * obj 或者 NSObject **Obj 都等同于 NSObject *__autoreleasing * obj,使用例子就是NSError .

         NSError *error;
         BOOL OK = [myObject performOperationWithError:&error];
    		
         -(BOOL)performOperationWithError:(NSError * __autoreleasing *)error;
    
    
7. __weak 修饰的变量,并不增加引用计数,但是每当使用__weak变量时,会用生成一个 __strong tmp 临时变量,在使用完成后立即将tmp release释放。iOS和OSX的中说那个tmp修饰符应该是__autoreleasing,被标记成为 autoreleasing 对象,其实是老版的clang对 __weak 的实现方法,现在已经改为,用完及时释放的实现版本了。假如,tmp当做一个参数传入到一个函数中,是可以打印引用计数 ,发现引用计数+1的。
```

{
    NSObject* sp = [NSObject new];
    NSObject* __weak wp = sp;
    NSLog(@"%@", wp);
}

//转换为

id sp = objc_msgSend(NSObject, "new");
id wp;
objc_initWeak(&wp, sp);//初始化weak变量,并将变量放入weak表中,以对象地址为Key,变量地址为Value
id tmp = objc_loadWeakRetained(wp); // 拿出weak变量的真是对象,并retain,引用计数 +1;
NSLog(@"%@", wp); //使用weak变量
objc_release(tmp); //编译器发现使用完成就立刻释放掉临时指针tmp
objc_destroyWeak(&wp); //从weak表中销毁weak变量
objc_storeStrong(&sp, 0); //相当于objc_release(sp)
```

OC中52个有效方法

多用类型常量,少用#define预处理

  • 定义常量
    • 编译单元(.h.m)可见 : 需要用 static 和 const 一起用。static表示仅编译单元(.h.m)可见. const 表示不能修改。而且添加k前缀
    • eg : static const NSTimeInterval kAnimationDuration = 0.3
    • 全局可见 :
      • eg1
        • 在 .m 中 const NSTimeInterval EOCAnimationDuration = 0.3 ;
        • 在 .h 中 extern const EOCAnimationDuration ;
      • eg2
        • 在 .m 中 NSString * const EOCStringConstant = @“Value” ;
        • 在 .h 中 extern NSString *const EOCStringConstant ;
      • 添加模块类名前缀

用枚举型表示状态、选项、状态码

  • typedef NS_OPTIONS
typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
    SDWebImageRetryFailed = 1 << 0,
    SDWebImageLowPriority = 1 << 1,
    SDWebImageCacheMemoryOnly = 1 << 2,
    SDWebImageProgressiveDownload = 1 << 3,
	};

  • typedef NS_ENUM
	typedef NS_ENUM(NSInteger, SDImageFormat) {
	    SDImageFormatUndefined = -1,
	    SDImageFormatJPEG = 0,
	    SDImageFormatPNG,
	    SDImageFormatGIF,
	    SDImageFormatTIFF,
	    SDImageFormatWebP
	};

使用GCD可以比锁更高效,在保证多线程安全过程中

  • dispatch_barrier_async 和并行队列 来处理 读写锁
  • 将所有操作都放在一个串行队列里。可以很好滴保护这段代码线程安全。而却还可以异步,不会阻塞代码

少用performSelector 多用 GCD

  • performSelector 在内存管理上容易有疏忽,他无法确定究竟要执行哪一个选择子,ARC无法插入适当的内存管理
  • performSelector 函数传参合返回值都有局限性
  • performSelector 大部分api都能用GCD替代

gcd 与 NSOperationQueue

  • NSOperation
    • NSOperation 添加依赖Operation
    • NSOperation 有queuePriority,可以在NSOperation之间使用,让哪一个NSOperation先执行,而GCD只能针对一个队列,里面的block都是一个优先级。
    • 设置最大并行数
    • kvo监听isFinished 或者 isCanceled
    • NSOperation 有threadPriority,可以更方便的设置线程优先级
  • GCD
    • 支持的方法更多
    • 队列类型
      • 主队列 : dispatch_get_main_queue
      • 自定义队列 : dispatch_queue_create : DISPATCH_QUEUE_SERIAL / DISPATCH_QUEUE_CONCURRENT
      • 全局队列 : dispatch_get_global_queue
        • 优先级
          • 高 QOS_CLASS_USER_INTERACTIVE : 及时交互 UI相关,交互等
          • 高 QOS_CLASS_USER_INITIATED : 用户发起需要马上得到结果进行后续任务
          • 中 QOS_CLASS_DEFAULT : 默认的不应该使用这个设置任务
          • 低 QOS_CLASS_UTILITY : 花费时间稍多比如下载,需要几秒或几分钟的
          • 低 QOS_CLASS_BACKGROUND : 不可见在后台的操作可能需要好几分钟甚至几小时的
    • dispatch_apply 比 dispatch_async 有避免线程爆照的优势。
    • dispatch_block_notify 可以检测某个block完成的通知
    • dispatch_block_cancel 取消未执行的block,假如已经在执行,则不能取消,可以用dispatch_block_testcancel测试
    • dispatch_io_create/dispatch_io_set_low_water/dispatch_io_read 多线程去去读取大数据切片数据。比单线程要快很多

    • Dispatch Source 用GCD监视进程
      • Dispatch Source 类型
        • DISPATCH_SOURCE_TYPE_DATA_ADD 数据增加
        • DISPATCH_SOURCE_TYPE_DATA_OR 数据OR
        • DISPATCH_SOURCE_TYPE_MACH_SEND Mach端口发送
        • DISPATCH_SOURCE_TYPE_MACH_RECV Mach端口接收
        • DISPATCH_SOURCE_TYPE_MEMORYPRESSURE 内存情况
        • DISPATCH_SOURCE_TYPE_PROC 进程事件
        • DISPATCH_SOURCE_TYPE_READ 读数据
        • DISPATCH_SOURCE_TYPE_SIGNAL 信号
        • DISPATCH_SOURCE_TYPE_TIMER 定时器
        • DISPATCH_SOURCE_TYPE_VNODE 文件系统变化
        • DISPATCH_SOURCE_TYPE_WRITE 文件写入
    • Dispatch Source 用法 + dispatch_source_create:创建dispatch source,创建后会处于挂起状态进行事件接收,需要设置事件处理handler进行事件处理。
      • dispatch_source_set_event_handler:设置事件处理handler
      • dispatch_source_set_cancel_handler:事件取消handler,就是在dispatch source释放前做些清理的事。
      • dispatch_source_cancel:关闭dispatch source,设置的事件处理handler不会被执行,已经执行的事件handler不会取消。
    • Dispatch Source 几个作用
      • 监听进程的退出 利用 DISPATCH_SOURCE_TYPE_PROC
      • 监听文件夹内文件的变化 利用 DISPATCH_SOURCE_TYPE_VNODE
      • 生成不依赖Runloop的Timer 利用 DISPATCH_SOURCE_TYPE_TIMER
  • NSOperationQueue
    • api更简洁
  1. 遍历
    • for int i = 0 ;i < array.count ; i = i + 1;
    • for object in array
    • while ([NSEnumerator nextObject]) { } //set dict array方法统一
    • [array enumerateObjectsUsingBlock:^(id _ Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {}]; //set dict array方法统一,可以直接修改block参数签名,可以设置并行方式利用的是gcd。
  2. 精简 initialize和load 的实现代码
    • load
      • 唯一不遵守继承规则的。子类没实现这个方法,父类不会的load也不会被调用 + ClassA的load 方法和 他的分类load 方法,分别会被调用,而且 ClassA的load方法先被调用
      • 最好不要用锁在load里,和大数据量操作。会阻塞app的载入。真有必要的话可以放到initialize里面。
      • load里面使用其他类,要十分小心。可能其他类没有被load。但是我怎么没测出来呢
    • initialize
      • initialize 和 load 都是线程安全的。在里面不用加锁使用。因为运行时在调用load和initialize时前后都加了锁。
      • initialize 方法遵循继承规则,即使initialize里面没有调用 [super initialize],子类调用initialize,父类也会被调用,因为初始化子类,需要先初始化父类,从runtime源码上能开出,是再强制循环调用父类的initialize。如果子类真的不需要父类的初始化,可以在initialize 方法中添加 eg: SuperClass.m if(self == [SuperClass class]){ // do }
      • 无法在编译期设定的全局变量可以放在,initialize里。

Post Directory