Effective Objective-C 2.0 总结与笔记(第四章)—— 协议与分类

第四章:协议与分类

​ Objective-C语言有一项特性叫做“协议”,类似与Java的“接口”。Objective-C不支持多重继承,所以我们把某个类应该实现的一系列方法定义在协议里面。最为常见的用途就是实现委托模式了。

​ “分类”也是Objective-C的一项重要特性,利用分类机制,无须继承子类即可直接为当前类添加方法。

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

  • 委托模式的编程设计模式来实现对象间的通信,该模式的主旨是:
    • 定义一套接口。
    • 如果对象想接受另一个对象的委托,那么就要遵循这个接口,以便其称为其“委托对象”。

example:EOCDataModel请求EOCNetworkFetcher以异步的方式执行一项任务,而EOCNetworkFetcher执行完这项任务之后,就会通知其委托对象,也就是EOCDataModel

//EOCNetworkFetcher.h
@class EOCNetworkFetcher;
//定义一个协议
@protocol EOCNetworkFetcherDelegate <NSObject>

-(void)networkFetcher:(EOCNetworkFetcher *)fetcher didReceiveData:(NSData *)data;
-(void)networkFetcher:(EOCNetworkFetcher *)fetcher didFailWithError:(NSError *)error;

@end

//有了协议之后就可以使用一个属性来存放其委托对象了。这个属性需要使用weak,而不是strong,因为DataModel和NetworkFetcher必须为非拥有关系,通常情况下,扮演delegate的对象也要持有本对象。使用weak能防止保留环的产生。    
@interface EOCNetworkFetcher : NSObject

@property (nonatomic , weak) id<EOCNetworkFetcherDelegate> delegate;//利用属相来存放委托对象
@end

//EOCNetworkFetcher.m
#import "EOCNetworkFetcher.h"

@implementation EOCNetworkFetcher
-(void)didSomeSthing{

    if ([_delegate respondsToSelector:@selector(networkFetcher:didReceiveData:)]){
        [_delegate networkFetcher:nil didReceiveData:nil];
    }
}
@end

//EOCDataModel 实现EOCNetworkFetcherDelegate
//EOCDataModel.h
#import <Foundation/Foundation.h>
#import "EOCNetworkFetcher.h"
@interface EOCDataModel : NSObject<EOCNetworkFetcherDelegate>

@end

//EOCDataModel.m
#import "EOCDataModel.h"

@implementation EOCDataModel
-(void)networkFetcher:(EOCNetworkFetcher *)fetcher didReceiveData:(NSData *)data
{
//do some things
}
-(void)networkFetcher:(EOCNetworkFetcher *)fetcher didFailWithError:(NSError *)error
{

}
@end

  • 委托协议一般使用@optional关键字来表示可选的,一般情况下都说使用这个方式来实现。

  • 也可以使用协议定义一套接口,令某类经由该接口获取其所需的数据。委托模式的这一用法又称数据源模式,因为信息从数据源流向类。

  • 若有必要,可以使用含有位段的结构体,将委托对象是否能响应相关协议方法这一信息缓存至其中。

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

  • 如果一个类的代码全部放在一个巨大的实现文件里,可能可阅读性会差一点,那么可以通过Objective-C的分类机制,把类代码按逻辑划入几个分区中。
@interface EOCPerson : NSObject<NSCopying>

@property(nonatomic, copy) NSString *firstName;
@property(nonatomic, copy) NSString *lastName;

- (instancetype)initWithFirstName:(NSString *)firstName
                      andlastName:(NSString *)lastName;

//friend method
- (void)addFriend:(EOCPerson *)person;
- (void)removeFriend:(EOCPerson *)person;
- (void)isFriendWith:(EOCPerson *)person;

//work method
- (void)performDaysWork;
- (void)takevacationFromWork;

//play method
- (void)goToTheCinema;
- (void)goToSportGame;

@end
    
//如果利用分类的方式,可以拆成3个分类
@interface EOCPerson (Friendship)
- (void)addFriend:(EOCPerson *)person;
- (void)removeFriend:(EOCPerson *)person;
- (void)isFriendWith:(EOCPerson *)person;
@end
    
@interface EOCPerson (Work)
- (void)performDaysWork;
- (void)takevacationFromWork;
@end
    
@interface EOCPerson (Play)
- (void)goToTheCinema;
- (void)goToSportGame;
@end
  • 如果编写准备分享给其他开发者使用的程序库的时候,可以创建Private分类,把私有方法归入到Private分类中。

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

  • 向第三方类中加分类的时候,应该添加上前缀,而对应的方法名也应该加上前缀。这样做的目的是防止因为没有加前缀而导致重复的分类被创建,或者分类下的方法命名重复,因为Objective-C没有命名空间,所以可能会出现意想不到的问题。

第26条:勿在分类中声明属性

  • 除了"class-continuation分类“(拓展),其他分类都无法向类中新增实例变量。他们无法把实现属性所需的实例变量合成出来,如果使用关联对象可以解决分类不能合成实例变量的问题,但是这样代码冗余度会提高,同时可能出现内存管理的问题。
  • 尽量把全部属性都定义在主接口里,而在"class-continuation分类“以外的分类,可以定义存取方法,但是尽量不要声明属性。

第27条:使用"class-continuation分类“隐藏实现细节

  • Objective-C动态消息系统的工作方式决定了不可能实现真正的私有方法或私有实例变量。如果确实是不需要对外公布确应该具有的方法和实例变量应该使用"class-continuation分类“来实现。
  • "class-continuation分类“和普通的分类不同,他必须定义在所拓展的那个类的实现文件里,这是唯一能声明实例变量的分类,而且此分类没有特定的实现文件。
//EOCPerson.m
@interface EOCPerson () {
    //实例变量
}
//方法和属性
@end

实例变量也可以声明到实现块里,语法上和"class-continuation分类“里定义一样。

  • 编写Objective-C++代码的时候,使用"class-continuation分类“也更为有效。
//EOCClass.h
#import <Foundation/Foundation.h>
#include "SomeCppClass.h"

@interface EOCClass : NSObject {
@private
    SomeCppClass _cppDemo;
}

@end

假如一个类的头文件声明如上,那么其实现文件应该是EOCClass.mm,表示告诉编译器应该将此文件使用Objective-C++的方式进行编译,但是由于头文件里包含了CPP文件,那么如果其他引入该头文件的类,都需要使用.mm的拓展名,这样并不是非常合适。

最好的解决方案就是使用"class-continuation分类“。

//EOCClass.h
#import <Foundation/Foundation.h>

@interface EOCClass : NSObject 

@end
    
//EOCClass.m
#import "EOCClass.h"
#include "SomeCppClass.h"

@interface EOCClass () {
    SomeCppClass _cppDemo;
}

@end

@implementation EOCClass

@end

这样其头文件就不会出现CPP文件了,使用头文件的人也不会意识到这个底层的实现使用了CPP的代码,很好的隐藏了实现细节。

  • "class-continuation分类“还有一种用法就是将在public接口中声明为“只读”的属性扩展为“可读写”,以便在类内部设置其值。因为这样可以不用直接访问实例变量,而是通过设置访问方法来做,就可以触发KVO通知。
@interface EOCPerson : NSObject<NSCopying>

@property(nonatomic, copy, readonly) NSString *firstName;
@property(nonatomic, copy, readonly) NSString *lastName;

- (instancetype)initWithFirstName:(NSString *)firstName
                      andlastName:(NSString *)lastName;
@end
    
@interface EOCPerson ()
    
@property(nonatomic, copy, readwrite) NSString *firstName;
@property(nonatomic, copy, readwrite) NSString *lastName;

@end
  • "class-continuation分类“还可以定义私有的协议。有时由于对象所遵从的某个协议在某个私有API中,所以可能不太想在公共接口中泄露这一信息。

第28条:通过协议提供匿名对象

  • 协议定义了一系列方法,遵从此协议的对象应该实现它们,于是可以用协议把自己所写的API之中的实现细节隐藏起来,将返回的对象设计为遵从此协议的纯id类型,这样想要隐藏的类名就不会出现在API之中了。这一概念常常被称为“匿名对象”。在委托里,经常定义匿名对象,例如:
@property (nonatomic, weak) id<EOCDelegate> delegate;

该属性是id<EOCDelegate的,实际上任何类的对象都能充当这一属性,只需要遵循EOCDelegate即可。

  • NSDictionary也有应用这一概念,在可变版本的字典里,设置键值对所用的方法的签名是:
- (void)setObject:(id)object forKey:(id<NSCopying>)key;

表示键值对的参数都可以是任意类型,key参数可以视为匿名对象,字典不关心key对象的所属的具体类,字典对象只需要确定能给此实例发送拷贝消息就可以。

  • 匿名对象的作用主要是:

    • 隐藏类型名称。

    • 对于不关心具体类型,只是关心是否能响应特定方法的情况,可以使用匿名对象。

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

推荐阅读更多精彩内容