EffectiveObjective-C2.0 笔记 - 第四部分

4 协议与分类

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

1. 协议(protocol)类似 java 的接口(interface)。Objective-C 不支持多重继承,但我们可以把某个类应该实现的方法定义在一系列的协议里面。

  1. Objective-C 可以使用 “委托模式”(Delegate pattern)的编程设计模式来实现对象间的通信:

定义一套接口,某对象若想接受另一个对象的委托,则需遵从此接口,以便成为其 “委托对象”(delegate)。Objective-C 一般利用 “协议” 机制来实现此模式。

定义协议:

//采用 “驼峰法” 命名,最后加上 Delegate 一词
@protocol EOCNetworkingFetcherDelegate<NSObject>

// 可选实现的方法
@optional
- (void)newworkingFetcher:(EOCNetworkingFetcher *)fetcher
            didRecevieData:(NSData *)data;

// 必须实现的方法
@required
- (void)newworkingFetcher:(EOCNetworkingFetcher *)fetcher
         didFailWithError:(NSError *)error;

@end

@interface EOCNetworkingFetcher : NSObject

// weak,避免循环引用
@property (nonatomic,weak) id delegate;

@end

3. 如果要在委托对象上调用可选方法,那么必须提前使用类型信息查询方法,判断这个委托对象能否响应相关的选择子。

NSData *data;
if([_delegate respondsToSelector:@selector(networkFetcher:didRecevieData:)]){
    [_delegate networkFetcher:self didRecevieData:data];
}

4. delegate 里的方法也可以用于从委托对象中获取信息(数据源模式)。

5. 在实现委托模式和数据源模式的时,协议中的方法是可选的,我们就会写出大量这种判断代码:

if([_delegate respondsToSelector:@selector(networkFetcher:didRecevieData:)]){
    [_delegate networkFetcher:self didRecevieData:data];
}

每次调用方法都会判断一次,其实除了第一次检测的结构有用,后续的检测很有可能都是多余的,因为委托对象本身没变,不太可能会一下子不响应,一下子响应的,所以我们这里可以把这个委托对象能否响应某个协议方法记录下来,以优化程序效率。
将方法响应能力缓存起来的最佳途径是使用 “位段”(bitfield)数据类型。我们可以把结构体中某个字段所占用的二进制位个数设为特定的值。

// 协议
@protocol EOCNetworkingFetcherDelegate<NSObject>

@optional
- (void) didReceiveData:(NSData *)data;

@end

@interface EOCNetworkingFetcher ()
// 定义位段
struct {
    unsigned int didReceiveData : 1;
} _delegateFlags
@end

// 设置
-(void)setDelegate:(id<EOCNetworkingFetcherDelegate>)delegate{
    _delegate = delegate;
    _delegateFlags.didReceiveData = [_delegate respondsToSelector:@selector(didRecevieData:)]
}

//使用
if(_delageteFlags.didReceiveData){
    [_delegate didRecevieData:data];
}

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

1. 使用分类机制把类的实现代码划分成易于管理的小块。

2. 将应该视为 “私有” 的方法归入为叫Private 的分类中,以隐藏实现细节。

3. 分类原则上不能定义属性,当可以通过【关联对象】实现,参见《2.5-关联对象存放自定义数据》

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

1. 分类机制常用于向无源码的既有类中新增新功能,但是在使用的时候要十分小心,不然很容易产生Bug。因为这个机制时在运行期系统加载分类时,将其方法直接加到原类中,这里要注意方法重名的问题,不然会覆盖原类中的同名方法。

2. 一般用前缀来区分各个分类的名称与其中所定义的方法。

3. 不要轻易去利用分类来覆盖方法,这里需要慎重考虑。

4.4 勿在分类中声明属性

1. 可以利用运行期的关联对象机制,为分类声明属性,但是这种做法要尽量避免。

因为除了 "class-continuation 分类" 之外,其他分类都无法向类中新增实例变量,因此,他们无法把实现属性所需的实例变量合成出来。

2. 利用关联对象机制可以解决分类中不能合成实例变量的问题。

自己实现存取方法,但是要注意该属性的内存管理语义(属性特质)。

@property (nonatomic,copy) NSString *name;
static const void *kViewControllerName = &kViewControllerName;
- (void)setName:(NSString *)name {
    objc_setAssociatedObject(self, kViewControllerName, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
    NSString *myName = objc_getAssociatedObject(self, kViewControllerName);
    return myName;
}

3. 在可以修改源代码的情况下,尽量把属性定义在主接口中。

这里是唯一能够定义实例变量的地方,属性只是定义实例变量及相关存取方法所用的 “语法糖”。

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

“class-continuation 分类”必须定义在本身类的实现文件中,而且这里是唯一可以声明实例变量的分类(除关联对象机制)。

而且此分类没有特定的实现文件,这个分类也没有名字。这里可以定义实例变量的原因是 “ 稳固的ABI” 机制,我们无须知道对象的大小就可以直接使用它。

@interface EOCPerson ()
@end

私有的方法、协议、变量等,都可以定义在“class-continuation 分类”

1. 可以将不需要要暴露给外界知道的实例变量及方法写在 “class-continuation 分类” 中。

2. 可以利用 “class-continuation 分类” 把引用C++ 类的细节写到实现文件中,这样子别的类引用这个类就不会受到影响,甚至都不知道这个类底层实现混有C++ 代码。

编写Objective-C++ 代码时候,使用 “class-continuation 分类” 会十分方便。因为对于引用了C++的文件的实现文件需要用.mm 为扩展名,表示编译器应该将此文件按照Objective-C++ 来编译。C++ 类必须完全引入,编译器要完整地解析其定义才能得知这个C++ 对象的实例变量大小。如果把对C++ 类的引用写在头文件的话,其他引用到这个类也会引用到这个C++ 类,就也需要编译成Objective-C++ 才行,这样子很容易失控。

3. 使用 “class-continuation 分类” 还可以将头文件声明 “只读” 的属性扩展成 “可读写”,以便在类的内部可以设置其值。

我们通常不直接访问实例变量,而是通过设置方法来做,因为这样子可以触发 “键值观测” (Key-Value Observing,KVO)通知。

4. 若对象所遵循的协议只应视为私有,也可以同过“class-continuation 分类” 来隐藏。

4.6 通过协议提供匿名对象

@property (nonatomic,weak) id delegate;

该属性类型是id的,所以实际上任何类的都能充当这一属性,即便该类不继承NSObject 也可以,只要遵循EOCDelegae 协议就可以了,对于具备此属性的类来说,delegate 就是 “匿名的”。

1. 使用匿名对象来隐藏类型名称(或类名)。

2. 如果具体类型不重要,重要的是对象能够响应(定义在协议里的)特定方法,那么可使用匿名对象来表示。

?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容