首页 > 代码库 > 《Effective Objective-C 2.0》—(第23-28条)—类别、协议,代理,匿名对象、delegate
《Effective Objective-C 2.0》—(第23-28条)—类别、协议,代理,匿名对象、delegate
第23条:通过委托与数据源协议进行对象间通信
对象之间经常需要相互通信,而通信方式有很多。OC开发者广泛使用一种名叫“委托模式”(Delegate Pattern)的编程设计模式来实现对象间的通信,该模式的主旨是:定义一套接口,某对象若想接收另一个对象的委托,则需遵从此接口,以便称为“委托对象”(delegate)。而这“另一个对象”则可以给其委托对象回传一些信息,也可以在发生相关联时间时通知委托对象。
此模式可以将数据与业务逻辑解耦。
在Objective-C中,一般通过“协议”这项语言特性来实现此模式,整个Coco系统框架都是这么做的。如果你的代买也这样写,那么就能和系统框架很好地融合在一起了。
举例:
回调委托对象的流程。请注意,“委托对象”未必非得由EOCDataModel实例来担任不可,也可以由另外一个对象扮演此角色
利用协议机制,很容易就以Objective-C代码实现此模式,代码如下:
@protocol EOCNetworkFetcherDelegate -(void)netWorkFetcher:(EOCNetworkFetcher*)fetcher didReceiveData:(NSData*)data;//这个EOCNetworkFetcher*参数可以高速委托对象,是谁调用它的 -(void)netWorkFetcher:(EOCNetworkFetcher*)fetcher didFailWithData:(NSError*)error; @end
有了这个协议之后,类就可以用一个属性来存储委托对象了。在本例中,这个类就是EOCNetworkFetcher类:
@interface EOCNetworkFetcher :NSObject @property (nonatomic,weak) id<EOCNetworkFetcherDelegate> delegate; @end
一定要注意这个属性定义成weak,而非strong。因为两者之间必须为“非拥有关系”。如下图所示:
为了避免循环引用,NetWorkFetcher不保留delegate属性.
看一下EOCDataModel的实现:
@interface EOCDataModel()<EOCNetworkFetcherDelegate> @end @implement EOCDataModel -(void)netWorkFetcher:(EOCNetworkFetcher*)fetcher didReceiveData:(NSData*)data{ if(fetcher == _myFetherA){ /* handle data*/ }else if(fetcher == _myFetherB){ /* handle data*/ } } -(void)netWorkFetcher:(EOCNetworkFetcher*)fetcher didFailWithData:(NSError*)error{ /*handle error*/ } @end
这个EOCNetworkFetcher*参数可以高速委托对象,是谁调用它的.
下面是EOCNetworkFetcher调用情况
NSData *data = /*data obtained from network*/ if([_delegate respondsToSelector:@selector(networkFetcher:didReceiveData:)]){ [_delegate networkFetcher:self didReceiveData:data]; }通过这个例子,大家应该很容易理解此模式为何叫做“委托模式”:因为对象把应对某个行为的责任委托给另外一个类了。
关于运行时刻,每次都判断respondsToSelector:@selector(xxxx)是多余的,只有第一次判断是有用的。参考优化办法
【本节要点】
● 委托模式为对象提供了一套接口,使其可由此相关事件告知其他对象。
● 将委托对象应该支持的接口定义成协议,在协议中把可能需要处理的事情定义成方法
● 当某对象需从另一个对象中获取数据时,可以使用委托模式。这种情境下,该模式亦称为“数据源协议”(data source protocal)
● 若有必要,可实现还有位段的结构体,将委托对象是否能够相应相关协议方法这一信息缓存至其中。
第24条:将类的实现代码分散到便于管理的数个分类之中
类中经常容易填满各种方法,而这些方法的代码则全部堆在一个巨大的实现文件里。在此情况下,可以通过Objective-C的“分类”机制,把类代码按照逻辑划入几个分区中。
Cocoa中的NSURLRequest类以及可变版本NSMutableURLRequest类就是这么做的。
另外:在编写准备分享给其他开发者使用的程序库时,可以考虑建立“Private”分类,如果程序中的某个地方要用到这些方法,那就引入此分类的头文件。而分类头文件并不会随程序库一并公开,于是该库的使用者也就不知道库里还有这些私有方法了。
第25条:总是为第三方类的分类名称前加前缀
分类机制通常用于向无源码的既有类中添加新功能。这个特性极为强大,但是使用时候很容易忽视产生的问题。分类中的方法是直接添加到类里面的,如果分类中的方法跟类中本来就有的方法重名了,那么就会覆盖掉固有的方法了。
你可能这么写分类
@interface NSString (HTTP) //Encode a string with URL encoding -(NSString*)urlEncodeString; //Decode a URL encoded string -(NSString*)urlDecodedString; @end
如果NSString类本身就有urlEncodeString方法,或者还有另外一个分类也叫urlEncodeString,如果别人写的分类加载时间比你晚的时候,你的将被覆盖。想要解决此问题,以命名空间来区分各个分类的名称
@interface NSString (ABC_HTTP) //Encode a string with URL encoding -(NSString*)abc_urlEncodeString; //Decode a URL encoded string -(NSString*)abc_urlDecodedString; @end
第26条:勿在分类中生命属性
属性是封装数据的方式。尽管从技术上说,分类里可以生命属性,但这种做法还是要尽量避免。
第27条:使用“class-continuation”隐藏实现细节
第28条:通过协议提供匿名对象
-(void) setObject:(id)object forKey:(id<NSCopying>)key;
@protocol EOCDatabaseConnection -(void) connect; -(void)disconnect; -(void)isConnected; -(NSArray*)performQuery:(NSString*)query; @end;
@protocol EOCDatabaseConnection @interface EOCDatabaseManger:NSObject +(id)sharedInstance; -(id<EOCDatabaseConnection>) connectionWithIdentifier:(NSString*)identifier; @end;这样的话,处理数据库连接所用的类名称几UI不会泄露了。
NSFetchedResultsController * controller = /*some controller*/; NSUInteger section = /*section index to query*/ NSArray *sections = controller.sections; id<NSFetchedResultsSectionInfo> sectionInfo = sections[section]; NSUInteger numberOfObjects = sectionInfo.numberOfObjects;