前言

Effective Objective-C 读书笔记 - 协议与分类

协议(protocol)与Java的接口(interface)类似,由于OC不支持多重继承。通过协议,可以实现类似多重继承的效果。若指定某个类实现了某个协议,那么就表示,该类实现了协议中规定了一些方法。

分类(category)是OC的重要的一个语言特性。利用分类机制,无须继承子类即可为当前类添加方法。这也是由于动态runtime系统,才得以实现。

笔记

通过委托与数据源协议进行对象间通信

  1. 注意一点,声明delegate的时候,要声明成weak类型的。避免循环引用。

  2. 通常将:委托对象是否可以响应协议中的某个方法 这一信息缓存起来。之后就不需要一直查询该对象是否响应该方法了。缓存可以使用位段来实现。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @interface Example() {
    struct {
    unsigned int didReceiveData : 1;
    unsigned int didFailWithError : 1;
    } _delegateFlags;
    }
    @end

    @implementation Example
    - (void)setDelegate:(id<ExampleDelegate>)delegate {
    _delegate = delegate;
    _delegateFlags.didReceiveData = [delegate respondsToSelector:@selector(didReceiveData:)];
    _delegateFlags.didFailWithError = [delegate respondsToSelector:@selector(didFailWithError:)];
    }
    @end

    在调用delegate时,就不需要判断delegate是否响应某方法了,只需要看_delegateFlags里面的字段就可以了

将类的实现代码分散到便于管理的数个分类之中

  1. 将类的方法分散到几个分类中
    1. EOCPerson+Friendship(.h/.m)
    2. EOCPerson+Work(.h/.m)
    3. EOCPerson+Play(.h/.m)
  2. 可以创建一个Private的分类,将所有的‘私有方法’写在里面,这个private的头文件可以被其他文件所引用,但是发布的时候,不会公布出去。所以只能在程序内部使用,第三方使用者无法使用private分类中的方法。

总是为第三方类的分类名称加前缀

  1. 分类机制通常用于向五元吗的既有类中新增功能。

  2. 分类的方法相当于直接添加在既有类中的,他们好比这个类的固有方法

  3. 将分类中的方法加入既有类的方法列表是在runtime加载分类的时候。这就有一个问题了,如果分类中的方法名和既有类原始方法同名,那么会覆盖原始方法的实现

  4. 基于上述问题,就要加前缀

    1
    2
    3
    @interface NSString (ABC_HTTP)
    - (NSString *)abc_method;
    @end

勿在分类中声明属性

  1. 属性包含实例变量,分类无法自动合成属性的实例变量。
  2. 要想硬添加属性也是可以的。手动添加合成实例变量的方法,也就是实现该属性的存取方法,也就是调用 objc_getAssociatedObjectobjc_setAssociatedObject 方法

使用class-continuation分类隐藏实现细节

  1. 这种分类与普通的分类不同。其实我们一直在使用这个分类。。。

  2. 这个分类是唯一能正常的声明实例变量的分类,他必须写在实现文件中

    1
    2
    3
    4
    5
    6
    @interface EOCPerson() {
    NSString *_instanceVariable; // 实例变量
    }
    @property (nonatomic, strong) NSString *pro; // 属性
    - (void)p_someMethod; // 声明私有方法
    @end
  3. 虽然不要求声明私有方法,但是最好这样做

  4. 如果只是私有的遵循某协议的话,也需放在该分类中

    1
    2
    @interface EOCPerson () <EOCSecretDelegate>
    @end

通过协议提供匿名对象

  1. id<EOCPersonDelegate> delegate; 这个delegate就是匿名对象,不需要关心对象是什么类型,只要他遵循该协议就可以了

  2. 使用这个还可以返回多个基类不是相同的类,如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @protocal EOCDBConnection
    - (void)connect;
    - (void)disconnect;
    - (BOOL)isConnect;
    - (NSArray *)performQuery:(NSString *)query;
    @end

    @interface EOCManager : NSObject
    + (id)sharedInstance;
    - (id<EOCDBConnection>)connectionWithIdentifier:(NSString *)identifier;
    @end

    上面这个例子用来实现数据库的连接,不管是连接何种类型的数据库(Mysql,SQLite,PostreSQL)等等,都可以使用connectionWithIdentifier:方法。

    这个方法底层的实现:根据不同的identifier,选择性的返回与连接 Mysql或是SQLite或是PostreSQL 相关的对象,可能返回的对象叫做 MySQLManager, SQLiteManager, PostreSQLManager。 这些对象因为可能是来自3种第三方库的,所以不会有相同的基类,但是只要他们实现了delegate的方法,都可以这样返回。

    这样做:既保证了接口的简介,又隐藏了底层的实现