通过委托与数据源协议进行对象间通信
原因
业务应该和数据解耦合,酱紫业务的改变不需要数据也接着改变;同样,数据存储机制的调整也不需要更改业务。
处理
如下图所示,FSJNetworkFetcher定义了一套协议接口,如FSJNetworkFetcherDelegate中得方法,FSJDataModel继承了Delegate并实现了方法,接着FSJDataModel发起了FSJNetworkFetcher请求,当FSJNetworkFetcher获得数据后回调委托方法将数据传递给委托对象。
这里FSJDataModel作为委托对象实现了Delegate方法
//FSJNetworkFetcherDelegate.h
@protocol FSJNetworkFetcherDelegate
-(void)networkFetcher:(FSJNetworkFetcher*)fetcher didReceiveData:(NSData*)data;
-(void)networkFetcher:(FSJNetworkFetcher*)fetcher didFailWithError:(NSError*)error;
@end
//FSJNetworkFetcher.h
@interface FSJNetworkFetcher: NSObject
@property (nonatomic, weak)id <FSJNetworkFetcherDelegate> delegate;
@end
@implementation
...
NSData *data = /*data from fetcher*/
if([_delegate respondsToSelector:@selector(networkFetcher:didReceiveData:)])
{
[_delegate networkFetcher:self didReceiveData:data];
}
...
@end
//FSJDataModel.m
@interface FSJDataModel()<FSJNetworkFetcherDelegate>
@end
@implementation
-(id)init{
if(self = [super init])
{
FSJNetworkFetcher *fetcher = [[FSJNetworkFetcher alloc] init];
fetcher.delegate = self;
...
}
return self;
}
-(void)networkFetcher:(FSJNetworkFetcher*)fetcher didReceiveData:(NSData*)data
{
/*Handle Data*/
}
-(void)networkFetcher:(FSJNetworkFetcher*)fetcher didFailWithError:(NSError*)error
{
/*Handle Data*/
}
@end
上面的代码需要注意属性delegate对象必须是weak特质,原因如下图,DataModel声明了对象NetworkFetcher,并将NetworkFetcher的delegate 赋值为self,也就是NetworkFetcher对象持有DataModel,同时发起了NetworkFetcher请求,NetworkFetcher获得数据检验self是否实现delegate方法,如果实现,执行。
假如delegate特质为strong,会造成DataModel 与NetworkFetcher循环引用,导致内存泄露。
委托协议可以用@optional或@required来声明接口是可选还是必要,可选的接口需要用respondsToSelector:方法来检查是否实现
假如委托方法需要调用多次,那么将respondsToSelector:的检查结果缓存起来能够优化执行速度
//声明了缓存结构体
@interface FSJNetworkFetcher(){
struct {
BOOL didReceiveData = 1;
BOOL didFailWithError = 1;
} _delegateFlag;
}
//在设置delegate的时候就进行相关方法实现的检查
-(void)setDelegate:(id<FSJNetworkFetcherDelegate>)delegate{
_delegate = delegate;
_delegateFlag.didReceiveData = [_delegate respondsToSelector:@selector(networkFetcher:didReceiveData:)];
_delegateFlag.didFailWithError = [_delegate respondsToSelector:@selector(networkFetcher:didFailWithError:)];
}
//在代码实现的时候就进行设置
_delegateFlag.didReceiveData = 1;
//调用之前检查缓存结构体
if(_delegateFlag.didReceiveData){
//Yes, flag set
}
分类机制
详见:
http://blog.csdn.net/shawjan/article/details/48847239
通过协议提供匿名对象
假如你不想让外部知道你实现的类,你可以通过协议来隐藏类名。之所以能够这么实现,主要是遵循了FSJDelegate协议的对象,都可以赋值给在另外一个类中声明的delegate,并且通过这个delegate来响应指定的方法。如下面所示,这样外部就不知道调用类的名字。
@property (nonatomic, weak) id <FSJDelegate> delegate;
用“僵尸对象”调试内存管理问题
僵尸对象
僵尸对象是Xcode提供的一项非常方便的调试功能,启动这项功能后,运行期系统会把所有已经回收的实例转化成特殊的“僵尸对象”,而不会真正的回收他们。这种对象所在的核心内存无法重用,因此不可能遭到覆写。
僵尸对象收到消息后,会抛出异常,其中准确说明了发送过来的消息,并描述了回收之前的那个对象。
这项功能在非ARC环境会特别有用,在ARC更复杂的代码环境中也是很有用的。
启动方法:Edit Scheme → Run → 选择Diagnostics → 勾选Enable Zombie Objects
原理
- 开启了僵尸对象调试功能,当某类的对象声明周期结束时,系统会从_NSZombie_模板类中复制出来创建一个新类_NSZombie_OriginalClass,系统并不会把对象的内存释放掉,但是这块内存无法复用。同时系统修改对象的isa指针指向_NSZombie_OriginalClass僵尸类,至此,原对象转变为僵尸对象。
- 当僵尸对象收到消息时,僵尸类能够响应所有的选择子,响应方式为:打印一条包含消息内容及其接受者的消息,然后终止程序。