《Effective Objective-C 2.0编写高质量i
2019-01-28 本文已影响0人
Mr_WangZz
OC中两项重要的语言特性:
-
协议:与Java中的“接口”类似,但不支持多重继承,最常见的用途是实现委托模式。
多重继承指的是一个类可以同时继承多个类,比如A类继承自B类和C类,这就是多重继承。
-
分类:利用分类机制,我们无须继承子类即可直接为当前类添加方法。因为OC运行期系统是高度动态的,但也隐藏着一些陷阱。
23. 通过委托与数据源协议进行对象间通信
@property (nonatomic, weak) id <xxxxDelegate> delegate;
如果是方法是可选的(optional),在委托类中,需要先执行判断(responseToSelector)操作。
if ([_delegate responseToSelector:@selector(xxxxx)]) {
[_delegate xxxxx];
}
完整的例子:
//
// EOCNetworkFetcher.h
//
#import <Foundation/Foundation.h>
@class EOCNetworkFetcher;
@protocol EOCNetworkFetcherDelegate <NSObject>
@optional
- (void)networkFetcher:(EOCNetworkFetcher *)fetcher didReceiveData:(NSData *)data;
- (void)networkFetcher:(EOCNetworkFetcher *)fetcher didFailWithError:(NSError *)error;
- (void)networkFetcher:(EOCNetworkFetcher *)fetcher didUpdateProgressTo:(float)progress;
@end
@interface EOCNetworkFetcher : NSObject
@property (nonatomic, weak) id <EOCNetworkFetcherDelegate> delegate;
- (void)requestWithParams:(id)params url:(NSURL *)url;
@end
/*******************************************/
/*******************************************/
//
// EOCNetworkFetcher.m
//
#import "EOCNetworkFetcher.h"
@interface EOCNetworkFetcher (){
/**
这个结构体,用来缓存委托对象是否能响应特定的选择子
2^1 = 2,表示0 和 1 两个数。
*/
struct {
unsigned int didReceiveData: 1;
unsigned int didFailWithError: 1;
unsigned int didUpdateProgressTo: 1;
} _delegateFlags;
}
@end
@implementation EOCNetworkFetcher {}
- (void)setDelegate:(id<EOCNetworkFetcherDelegate>)delegate {
_delegate = delegate;
/* 优化代码执行速度
这样就不用每次在实现要被代理的方法前,每次执行检查了。只要检查一次就好。因为在设置代理时就已经确定了。
*/
_delegateFlags.didReceiveData = [delegate respondsToSelector:@selector(networkFetcher:didReceiveData:)];
_delegateFlags.didFailWithError = [delegate respondsToSelector:@selector(networkFetcher:didFailWithError:)];
_delegateFlags.didUpdateProgressTo = [delegate respondsToSelector:@selector(networkFetcher:didUpdateProgressTo:)];
}
- (void)requestWithParams:(id)params url:(NSURL *)url {
if (_delegateFlags.didReceiveData) {
[_delegate networkFetcher:self didReceiveData:[NSData new]];
}
if (_delegateFlags.didFailWithError) {
[_delegate networkFetcher:self didFailWithError:[NSError new]];
}
if (_delegateFlags.didUpdateProgressTo) {
[_delegate networkFetcher:self didUpdateProgressTo:1.0f];
}
}
@end
要点
- 委托模式为对象提供了一套接口,使其可由此将相关事件告知其他对象。
- 将委托的对象应该支持的接口定义成协议,在协议中将可能需要的事件定义成方法。
- 当某一对象需要从另一对象获取数据时,可以使用委托模式。在这种情况下,该协议被称为 “数据源协议”。
- 若有必要,可实现含有位段的结构体,将委托对象是否响应相关代理方法 这一信息缓存至其中。
在结构体中,
fileA 位段将占用8个二进制位,0~255;
fileB 位段将占用4个二进制位,0~16;
fileC 位段将占用2个二进制位,0~4;
fileD 位段将占用1个二进制位,0~1;
struct file {
unsigned int fileA: 8;
unsigned int fileB: 4;
unsigned int fileC: 2;
unsigned int fileD: 1;
} ;
24. 将类的实现代码分散到便于管理的数个分类之中
要点
- 使用 分类机制 把类的实现代码划分为易于管理的小块。
- 将应该视为 “私有” 的方法归入名为 “private” 的分类中,用以隐藏实现细节。
将类代码打散到分类中,便于调试
- 调试会出现分类名称,如-[EOCPerson(FriendShip) addFriend:]
- 方便查看,可以给类进行“瘦身”。
25. 总是为第三方类的分类名称加上前缀
@interface NSString (WZZ_HTTP)
- (NSString *)wzz_doSomeThing1;
- (NSString *)wzz_doSomeThing2;
@end
要点总结
- 向第三方类中添加分类时,总应给其 名称 和其中的 方法 加上你专用的前缀。----避免多次覆盖
26. 勿在分类中声明属性
属性,是用来封装数据的。
要点
- 把封装数据所用的全部属性都定义在 主接口 里;
- 在 “class-continuation 分类” 之外的所有分类里,可以定义存取 方法 ,但不应声明 属性 。
27. 使用 “class-continuation” 分类实现隐藏细节
@interface EOCPerson ()
@end
//或
@implementation EOCPerson
@end
要点
- 通过 “class—continuation分类” ,向类中增加实例变量。
- 如果属性在主接口声明中为“只读” ,而类的内容又要用设置方法修改此属性,那么就在 “class-continuation分类” 中将其扩展为可读写。
//.h 中
@property (nonatomic, copy, readOnly) NSString *name;
//.m 中
@property (nonatomic, copy, readWrite) NSString *name;
- 把私有方法的原型声明在 “class-continuation分类” 里面。
//.m
@interface EOCPerson ()
- (void)p_privateMethod;//私有方法
@end
- 若想隐藏此类所实现的协议,则可在该类的 “class-continuation分类” 中声明。
//.m
@interface EOCPerson() <EOCSecretDelegate>
@end
28. 通过协议提供匿名对象
要点
- (id<EOCSecretProtocol>)doSomeThing;
- 协议在某种程度上可以提供匿名类型。具体的类型可以淡化成遵从某协议的id类型,协议里规定了对象所应实现的代理方法。
- 使用匿名对象来隐藏类型名称(或类名)。
- 如果类型不重要,重要的是对象能够响应(某个协议的)特定方法,那么可以使用你匿名对象来表示。