effective OC2.0 阅读笔记(四 协议与分类)

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

这条讲的也比较基础,就是基本的delegate,protocol使用。

要点

  1. 委托模式的主旨是定义一套接口,某对象若想接受另一个对象的委托,则需遵从此接口,以便成为其“委托对象(delegate)”。将委托对象应该支持的接口定义成协议,在协议中把可能需要处理的事件定义成方法。

  2. 委托模式可将数据与业务逻辑解偶。视图对象的属性中,可以包含负责数据和事件处理的对象。这两种对象分别称为“数据源”(data source) 和 委托"delegate"。又例如:当某对象需要从另外一个对象中获取数据时,可以使用委托模式,这种用法经常被称为“数据源协议”(Data source Protocol)类似 UITableview的UITableViewDataSource

  3. 信息从数据源(Data Source )流向类(Class);而在常规的委托模式中,信息则从类流向受委托者(Delegate)。在信息源模式中,信息从数据源流向类,而在普通的委托模式中,信息则从类流向受委托者
    effective OC2.0 阅读笔记(四 协议与分类)_第1张图片

  4. 有时候一个可选择方法可能在一个生命期中多次调用,如果每次都检查委托对象是否能响应选择子,那就显得多余了。可以使用位段(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;

在方法要调用很多次时,值得进行这种优化,如果要频繁通过数据源协议从数据源中获取多份相互独立的数据,那么这项优化技术会提高程序效率。

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

要点:

这条主要说的是

  1. 通过分类机制,可以把类分成很多歌易于管理的小块。也是有一些前提的吧,可能是这个类业务比较复杂,需要瘦身,需要解耦等等。
  2. 作者还推荐把私有方法统一放在Private分类中,以隐藏实现细节。
    具体的分类的实现就是
    分类的实现
    比如说我们一个人信息建模为类,那么这个类包括下面几个方法:
#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)

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

要点:

分类机制通常用于向无源码的既有类中新增功能。如果一个分类里有同名的方法,其代码与你所添加的大同小异,但却不能正确实现你所需的功能,那个分类的加载时机如果晚于你所写的这个分类,那么其代码就会把你的那一份覆盖掉。所以我们需要以命名空间来区别各个分类的名称与其中所定义的方法。
所以,向第三方类中添加分类时,总应该给其中的方法名加上你专用的前缀。
从技术角度来讲,并不是非得用命名空间把各个分类的名称区隔开不可。即使两个分类重名了,也不会出错,但这样会发出一种类似的警告:

warning:duplicate definition of category 'HTTP' on interface
'NSString'

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

要点:

  1. 把封装数据所用的全部属性都定义在主接口中
  2. 不要在分类中声明属性,除了“class-continuation”分类中
#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

则会有警告
effective OC2.0 阅读笔记(四 协议与分类)_第2张图片

意思是说此分类无法合成与friends属性相关的实例变量,所以开发者需要在分类中为该属性实现存取方法。
当我们为其实现存取方法后,我们发现不仅在内存管理上容易出错,因为我们在为属性实现存取方法时,经常会忘记遵从其内存管理语义。作者觉得这个方法并不坏,但是太麻烦,不推荐。正常的话就把所有属性都定义在主接口中就好了

第27条:使用class-continuation分类(其实是类扩展)隐藏实现细节

要点:

这条跟之前的也有点重复,最终目的还是要尽量在公共接口中向外暴露的内容最小化,隐藏实现细节,只告诉怎么调用,怎么使用即可。具体实现以及属性的可修改权限尽可能的隐藏掉。其实就是将需要被暴露的方法露在外面, 将私有变量和私有方法放在class-continuation

  1. 通过“class-continuation分类”向类中新增实例变量。
  2. 如果某属性在主接口中声明为“只读”,而类的内部又要用设置方法修改此属性,那么就在“class-continuation分类”中将其扩展为“可读写”。
  3. 把私有方法的原型声明在"class-continuation分类"里面
  4. 若想使类所遵循的协议不为人所知,则可与“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 的四种使用情形

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

匿名对象是什么?

不用指明具体类型的对象,比如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的人仅仅要求所返回的对象能用来连接、断开并查询数据库即可。

要点:

  1. 协议可在某种程度上提供匿名类型。具体的对象类型可以淡化成遵从某协议的id类型,协议里规定了对象所应实现的方法
  2. 使用匿名对象来隐藏类型名称(或类名)
  3. 如果具体类型不重要,重要的是对象能够相应(定义在协议里的)特定方法,那么可使用匿名对象来表示。

你可能感兴趣的:(ios,objective-c)