这条讲的也比较基础,就是基本的delegate,protocol使用。
委托模式的主旨是定义一套接口,某对象若想接受另一个对象的委托,则需遵从此接口,以便成为其“委托对象(delegate)”。将委托对象应该支持的接口定义成协议,在协议中把可能需要处理的事件定义成方法。
委托模式可将数据与业务逻辑解偶。视图对象的属性中,可以包含负责数据和事件处理的对象。这两种对象分别称为“数据源”(data source) 和 委托"delegate"。又例如:当某对象需要从另外一个对象中获取数据时,可以使用委托模式,这种用法经常被称为“数据源协议”(Data source Protocol)类似 UITableview的UITableViewDataSource
信息从数据源(Data Source )流向类(Class);而在常规的委托模式中,信息则从类流向受委托者(Delegate)。在信息源模式中,信息从数据源流向类,而在普通的委托模式中,信息则从类流向受委托者
有时候一个可选择方法可能在一个生命期中多次调用,如果每次都检查委托对象是否能响应选择子,那就显得多余了。可以使用位段(bitfield)数据类型将方法响应能力缓存起来。
位段是一种c语言特性:我们可以把结构体中的某个字段所占用的二进制位数设为特定的值。
struct data{
unsigned int fieldA : 8;
unsigned int fieldB : 4;
unsigned int fieldC : 2;
unsigned int fieldD : 1;
};
这里fieldA位段占用8个二进制位,可以表示0~255之间的值,fieldB占用4个,fieldC占用2个,fieldD占用1个,可以表示0、1。
如果创建的结构体中只有大小为1的位段,那么就能把许多布尔值塞入一小块数据里面,如下: 如果是1 就表示为yes,0表示为no
struct{
unsigned int didReceiveData :1;
unsigned int didFailWithError :1;
unsigned int didUpdateProgressTo :1;
}_delegateFalags;
在方法要调用很多次时,值得进行这种优化,如果要频繁通过数据源协议从数据源中获取多份相互独立的数据,那么这项优化技术会提高程序效率。
这条主要说的是
#import
@interface EOCPerson:NSObject
@property (nonatomic,copy,readonly)NSString *firstName;
@property (nonatomic,copy,readonly)NSString *lastName;
@property (nonatomic,strong,readonly)NSArray *friends;
- (id)initWithFirstName:(NSString *)firstName andLastName:(NSString *)lastName;
/*Friendship methods*/
- (void)addFriend:(EOCPerson *)person;
- (void)removeFriend:(EOCPerson *)person;
- (void)isFriendsWith:(EOCPerson *)person;
/*Work methods*/
- (void)performDaysWork;
- (void)takeVacationFromWork;
/*Play methods*/
- (void)goToTheCinema;
- (void)goToSportsGame;
@end
我们可以按照其分类拆分为下列几个文件:
EOCPerson+Friendship(.h/.m)
EOCPerson+Work(.h/.m)
EOCPerson+Play(.h/.m)
分类机制通常用于向无源码的既有类中新增功能。如果一个分类里有同名的方法,其代码与你所添加的大同小异,但却不能正确实现你所需的功能,那个分类的加载时机如果晚于你所写的这个分类,那么其代码就会把你的那一份覆盖掉。所以我们需要以命名空间来区别各个分类的名称与其中所定义的方法。
所以,向第三方类中添加分类时,总应该给其中的方法名加上你专用的前缀。
从技术角度来讲,并不是非得用命名空间把各个分类的名称区隔开不可。即使两个分类重名了,也不会出错,但这样会发出一种类似的警告:
warning:duplicate definition of category 'HTTP' on interface
'NSString'
#import
NS_ASSUME_NONNULL_BEGIN
@interface EOCPerson : NSObject
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
- (id)initWithFirstName:(NSString *)firstName andLastName:(NSString *)lastName;
@end
@implementation EOCPerson
//methods
@end
@interface EOCPerson(Friendship)
@property (nonatomic,strong)NSArray *friends;
- (BOOL)isFriendsWith:(EOCPerson *)person;
@end
@implementation EOCPerson(Friendship)
//methods
@end
NS_ASSUME_NONNULL_END
意思是说此分类无法合成与friends属性相关的实例变量,所以开发者需要在分类中为该属性实现存取方法。
当我们为其实现存取方法后,我们发现不仅在内存管理上容易出错,因为我们在为属性实现存取方法时,经常会忘记遵从其内存管理语义。作者觉得这个方法并不坏,但是太麻烦,不推荐。正常的话就把所有属性都定义在主接口中就好了
这条跟之前的也有点重复,最终目的还是要尽量在公共接口中向外暴露的内容最小化,隐藏实现细节,只告诉怎么调用,怎么使用即可。具体实现以及属性的可修改权限尽可能的隐藏掉。其实就是将需要被暴露的方法露在外面, 将私有变量和私有方法放在class-continuation
"class-continuation分类"和普通的分类不同,它必须定义在其所接续的那个类的实现文件里。
此分类没有特定的实现文件,其中的方法都应该定义在类的主实现文件里。与其他分类不同,"class-continuation分类"没有名字。比如,有个类叫做EOCPerson,其"class-continuation分类"写法如下:
@inrtface EOCPerson ()
{
//可以声明实例变量
}
@end
比如,可以将一个秘密类的实例声明在"class-continuation分类"中:
//EOCClass.h
#import
@interface EOCClass : NSObject
@end
//EOCClass.m
#import "EOCClass.h"
#import "EOCSecretClass.h"
@interface EOCClass ()
{
EOCSecretClass *_secretInsrance;
}
@end
@implementation EOCClass
//Methods here
@end
我们可以对比一下:
通常 我们会这样写:
#import
@class EOCSuperSecretClass;
@interface EOCClass : NSObject
//@property (nonatomic,strong)NSArray *friends;
{
// 即使声明为 private 放在公共 接口中的EOCClass 依然会让外部知道有
// 有EOCSuperSecretClass 这样的类存在 而这不是我们想要的
@private EOCSuperSecretClass *_secretInstance;
}
@end
对比:
#import "EOCClass.h"
#import "EOCSuperSecretClass.h"
@interface EOCClass ()
{
// 使用这种方式来 隐藏 实现的细节 和我们不想给外部知道的 一些类
EOCSuperSecretClass *_superSecretClass;
}
@end
@implementation EOCClass
@end
iOS 中 Class-continuation 的使用
这一篇博客介绍了Class-continuation 的四种使用情形
不用指明具体类型的对象,比如delegate对象就是匿名的,因为它是id类型的。
1、接口背后有多个不同的实现类,而你又不想指明具体使用哪个类,因为有时候这些类可能会变,有时候他们又无法容纳于标准的类继承体系中,因而不能以某个公共基类来统一表示。
比如处理数据库连接的程序库,对于处理连接所用的那个类,你也许不想叫外人知道其名字,因为不同的数据库可能要用不同的类来处理,没有办法令其都继承自同一基类,那么就得返回id类型的东西了。
在开发后续版本时,无须改变公共API,即可切换后端的实现类。
2、有时对象类型不重要,重要的是对象有没有实现某些方法。
因为协议已经定义了一系列的方法,那么遵守了协议的匿名对象,就可以使用这些方法,我们就可以用匿名对象通过调用相应的方法,来实现我们的目的。
比如处理数据库连接的代码:
协议可以这样写
@protocol EOCDatabaseConnection:
-(void)connect;
-(void)disconnect;
-(BOOL)isConnected;
-(NSArray *)performQuery:(NSString *)query;
@end
数据库处理程序可以这样写:
#import
@protocol EOCDatabaseConnection;
@interface EOCDatabaseManager : NSObject
+ (id)sharedInstance;
- (id<EOCDatabaseConnection>)connectionWithIdentifier:(NSString *)identifier;
@end
这样的话,处理数据库连接所用的类的名称就不会泄漏了,有可能来自不同框架的那些类现在均可以经由同一个方法来返回了。使用API的人仅仅要求所返回的对象能用来连接、断开并查询数据库即可。