[关闭]
@myron-lee 2016-04-19T01:37:17.000000Z 字数 4904 阅读 1490

effective object c 总结

IOS


delegate

  1. 要解决的问题
    两个类之间通信的问题
  2. 定义
    定义一个接口,一个对象可以实现这个接口,成为另外一个对象的代理。与监听注入类似。
  3. 优势
    • 解耦
      要解决两个类之间通信的问题,最直接的办法是把对方作为一个成员变量,相互调用方法。但是,这种办法使得两个类紧密耦合,降低了代码的可扩展性。而使用 delegate 模式,可以降低耦合度,使得被代理的类相对独立。在示例中,类XYZClassB可以独立出来,不再依赖类XYZClassA,意味着如果将来 类XYZClassA有变动,可以不用修改类 XYZClassB,这在设计 API 时非常有用,类 XYZClassB作为提供服务的类肯定不能依赖要使用它的类。
  1. #import <Foundation/Foundation.h>
  2. @class EOCClassA;
  3. @class EOCClassB;
  4. @interface EOCClassA : NSObject
  5. @property (nonatomic, strong) EOCClassB *other;
  6. @end
  7. @interface EOCClassB : NSObject
  8. @property (nonatomic, strong) EOCClassA *other;
  9. @end

retain cycle

  1. 说明
    oc 中没有 gc,ARC 解决不了 retain cycle 问题,retain cycle 会造成内存泄露。

  2. 可能发生的地方

    • associated object
      associated object可能是 block,在block中使用一些对象很容易产生 retain cycle。并且因为两个对象之间的 associate 关系在方法中实现,并没有在 interface 中声明,使得隐藏的 retain cycle 难以 debug。
    • delegate
      假设被代理的类是 Utility,代理接口是UtilityDelegate,类 Consumer 实现了 UtilityDelegate 接口。那么,Consumer 一定要持有 Utility 的引用直至工作完成才能释放它,而 Utility 也需要持有 Consumer 的引用,当一些事件发生时通知它。这就形成了 retain cycle,通常把 Utility 对 Consumer 的引用设为 weak 解决这个问题。
    • block
      A block automatically retains all objects it captures.如果 block 使用了持有 block 的对象,retain cycle 就产生了。block 会自动retain 它使用的对象。比如,我们在一个对象方法中定义了一个 block,这个 block 可以访问所有这个对象方法能访问的变量,其中就包括 self,如果在 block 中不经意的使用到了 self,那么 block 将 retain self 这个对象。如果恰好,self 对象也 retain 这个 block 对象(可能是间接的 retain),那么将会形成一个 retain cycle。比如,下面一个例子中,_fetchedData = data;使得 block retain 了 EOCClass 对象,而 EOCClass 对象持有 EOCNetworkFetcher 对象的引用,EOCNetworkFetcher 持有 block 的引用,也就是说 EOCClass 间接的 retain block,从而形成 retain cycle,这个问题可以通过在EOCNetworkFetcher 工作完成后释放它的引用解决。
  1. // EOCNetworkFetcher.h
  2. #import <Foundation/Foundation.h>
  3. typedef void(^EOCNetworkFetcherCompletionHandler)(NSData *data);
  4. @interface EOCNetworkFetcher : NSObject
  5. @property (nonatomic, strong, readonly) NSURL *url;
  6. - (id)initWithURL:(NSURL*)url;
  7. - (void)startWithCompletionHandler: (EOCNetworkFetcherCompletionHandler)completion;
  8. @end
  9. // EOCNetworkFetcher.m
  10. #import "EOCNetworkFetcher.h"
  11. @interface EOCNetworkFetcher ()
  12. @property (nonatomic, strong, readwrite) NSURL *url;
  13. @property (nonatomic, copy) EOCNetworkFetcherCompletionHandler completionHandler;
  14. @property (nonatomic, strong) NSData *downloadedData;
  15. 
  16. @end
  17. @implementation EOCNetworkFetcher
  18. - (id)initWithURL:(NSURL*)url {
  19. if ((self = [super init])) {
  20. _url = url;
  21. }
  22. return self;
  23. }
  24. - (void)startWithCompletionHandler: (EOCNetworkFetcherCompletionHandler)completion
  25. {
  26. self.completionHandler = completion;
  27. // Start the request
  28. // Request sets downloadedData property
  29. // When request is finished, p_requestCompleted is called
  30. }
  31. - (void)p_requestCompleted {
  32. if (_completionHandler) {
  33. _completionHandler(_downloadedData);
  34. }
  35. }
  36. @end
  37. @implementation EOCClass {
  38. EOCNetworkFetcher *_networkFetcher;
  39. NSData *_fetchedData;
  40. }
  41. - (void)downloadData {
  42. NSURL *url = [[NSURL alloc] initWithString:
  43. @"http://www.example.com/something.dat"];
  44. _networkFetcher =
  45. [[EOCNetworkFetcher alloc] initWithURL:url];
  46. [_networkFetcher startWithCompletionHandler:^(NSData *data){ NSLog(@"Request URL %@ finished", _networkFetcher.url);
  47. _fetchedData = data; }];
  48. }
  49. @end

这是 EOCClass 对象与 block 之间形成的 retain cycle。事实上,一些网络访问库,consumer 并不需要持有 networkFetcher 去 keep it alive,可以通过将 networkFetcher 放到 global 的 set 中实现。这样 EOCClass 对象与 block 之间的 retain cycle 就不那么容易实现。但是如果在 block 中使用了 networkFetcher 将会形成 networkFetcher 与 block 之间的 retain cycle,如下面的例子所示。block 会持有它使用到的对象,而 networkFetcher 通过 property 持有 block,retain cycle 形成。所以,在 block 中使用任何对象的时候都要小心

  1. (void)downloadData {
  2. NSURL *url = [[NSURL alloc] initWithString:
  3. @"http://www.example.com/something.dat"];
  4. EOCNetworkFetcher *networkFetcher =
  5. [[EOCNetworkFetcher alloc] initWithURL:url];
  6. [networkFetcher startWithCompletionHandler:^(NSData *data){ NSLog(@"Request URL %@ finished", networkFetcher.url);
  7. _fetchedData = data; }];
  8. }
  1. 解决办法
    • weak reference
      The general rule is that if you don’t own an object, you should not retain it.weak reference 不 retain 对象,并且在对象被deallocate时被设为 nil。
    • 手动释放引用

block

  1. 说明
    block 类似于函数指针,但实际上是一个对象,它的存储空间默认分配在 stack 上,所以只在定义的 scope 里面有效,但是可以通过给它发送 copy 消息,将存储空间转移到 heap 上前。
    它的存储空间保存了 isa 指针,指向一个 Class 对象;保存了 invoke 指针,是指向 block 实现的函数指针;captured variables 保存了所有 block 使用到的对象的引用。
    block 内可以访问其定义的 scope 中所有可以访问的变量,默认只能读取,使用_block 修饰才能修改(成员变量不需要显式使用_block修饰)。

  2. 使用场景
    Handler,比如 CompletionHandler、ErrorHandler,在调用服务的时候直接注入 Handler,将启动代码与结束处理(响应)代码放在一起,提高代码的可读性。并且,每次调用服务都当场定义响应block,不需要像 delegate 模式中的响应函数对不同的服务进行 switch,分别响应,也就不需要保存各个服务。

GCD

  1. CD VS Lock、synchronize
    GCD 有 serial queue、concurrent queue; dispatch_sync、dispatch_async; dispatch_barrier_sync、dispatch_barrier_async 等等方法可以使得互斥同步操作更加高效的实现,比如将属性的读操作 dispatch_sync和属性的写操作dispatch_barrier_async 到一个 concurrent queue中去,可以实现多线程高效读写。

  2. GCD VS performSelector
    performSelector限制方法参数必须是对象,并且最多两个,有些场景下无法使用。使用GCD则没有此限制,并且同样支持延迟执行、支持指定在某一线程执行。

  3. GCD VS NSBlockOperation
    GCD 中使用 block,比较轻量;NSBlockOperation 是封装过的,更重,但是功能更强大,支持 Cancel、相互依赖、KVO(比如isCancelled)、优先级、重用等等。

  4. Dispatch group

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注