Objective-C的协议(Protocol)——协议的实现

协议就是定义了一组方法,然后要求其他类去实现。

以下类的复制来举例说明,遵守NSCopying协议的类是如何实现复制的。

0x01 NSCopying协议

NSCopying是对象拷贝的协议。

类的对象如果支持拷贝,则该类应遵守并实现NSCopying协议。

用NSCopying协议来举例,是因为该协议中的方法只有一个,比较容易理解:

- (id)copyWithZone:(NSZone *)zone {    
    Person *model = [[[self class] allocWithZone:zone] init];
    model.firstName = self.firstName;
    model.lastName  = self.lastName;
    //未公开的成员
    model->_nickName = _nickName;
    return model;
}

0x02 复制Engine类

为了能够复制engine对象,Engine类需要采用NSCopying协议。

所以Engine类的接口更新如下:

//Engine类的接口
@interface Engine : NSObject <NSCopying>
...
@end // Engine

因为Engine类采用了NSCopying协议,所以必须实现协议中的copyWithZone:方法。

zone是NSZone类的一个对象,指向一块可供分配的内存区域。

当你向一个对象发送copy消息时,该copy消息实际上将会转换成copyWithZone:方法。

Engine类的copyWithZone:方法实现如下:

//Engine类的实现
@implementation Engine
- (id) copyWithZone: (NSZone *) zone
{
    Engine *engineCopy;
    engineCopy = [[[self class] allocWithZone: zone] init];
    return (engineCopy);
} // copyWithZone

...

copyWithZone

copyWithZone:方法首先获得self参数所属的类,然后向self对象所属的类发送allocWithZone:消息以分配内存并创建一个该类的新对象。最后,copyWithZone:方法给这个对象发送init:消息使其初始化。

这里也可以使用指定初始化函数来执行初始化工作。

allocWithZone

alloc是类方法,allocWithZone也是类方法。

allocWithZone:方法的最后一行会返回新创建的对象。 

+ (id) allocWithZone: (NSZone *) zone;

为什么这里使用[[self class] allocWithZone: zone]而不是[Engine allocWithZone: zone]?

假如Engine类有个子类Slant6。

如果我们直接给Engine类发送allocWithZone:消息,创建的将是一个新的Engine类对象,而不是Slant6类对象。

通过使用[self class],allocWithZone:消息会被正确地发送给正在接收copy消息的对象所属的类。

0x03 复制Tire系列类

复制超类Tire

Tire类的复制工作比Engine类复杂些,因为Tire类有两个实例变量,其子类AllWeatherRadial又引入了两个实例变量。

既然是复制,那么实例变量也是必须被复制的。

Tire类采用协议的新接口代码如下:

//Tire类的接口
@interface Tire : NSObject <NSCopying>
@property float pressure;
@property float treadDepth;
...
@end // Tire

Tire类的copyWithZone:方法实现如下:

//Tire类的实现
- (id) copyWithZone: (NSZone *) zone
{
 Tire *tireCopy;
 tireCopy = [[[self class] allocWithZone: zone] initWithPressure:pressure 
                                                      treadDepth:treadDepth];
 return (tireCopy);
} // copyWithZone

...

这里因为Tire类需要复制两个实例变量,所以使用指定初始化函数来完成初始化工作。

复制子类AllWeatherRadial

子类AllWeatherRadial的接口代码不需要变化:

//AllWeatherRadial类的接口
@interface AllWeatherRadial : Tire
// ... properties
// ... methods
@end // AllWeatherRadial

为什么子类的接口不用写<NSCopying>协议名?

因为子类继承超类的时候,也包括获得超类的所有属性,遵守超类遵守的协议。

但是必须重写copyWithZone:方法,因为AllWeatherRadial类中有两个实例变量是新增的,必须同时被复制:

//子类AllWeatherRadial的实现
- (id) copyWithZone: (NSZone *) zone
{
 AllWeatherRadial *tireCopy;
 tireCopy = [super copyWithZone: zone];
 //复制子类新增的实例变量
 tireCopy.rainHandling = rainHandling;
 tireCopy.snowHandling = snowHandling;
 return (tireCopy);
} // copyWithZone

...

为什么这里用super而不用self?

因为AllWeatherRadial类继承自Tire类,所以子类只需直接请求超类执行copy操作,并期望超类正确地复制以及在分配对象时使用[self class]。所以,这里用super直接访问超类。

关于self和super的用法,可以参考另一篇笔记:《Objective-C中的self和super详解》

剩下的工作就是把rainHandling和snowHandling两个实例变量复制到新的对象tirecopy中去。

0x04 复制Car类

Car类没有什么特殊的,一样要遵守NSCopying协议。接口代码修改如下:

//Car类的接口
@interface Car : NSObject <NSCopying>
// properties
// ... methods
@end // Car

修改后的Car类实现如下:

//Car类的实现
- (id) copyWithZone: (NSZone *) zone
{
 Car *carCopy;
 carCopy = [[[self class] allocWithZone: zone] init];
 //复制车名
 carCopy.name = self.name;
 //复制Engine
 Engine *engineCopy;
 engineCopy = [engine copy];
 carCopy.engine = engineCopy;
 //复制Tire
 for (int i = 0; i < 4; i++)
 {
   Tire *tireCopy;
   tireCopy = [[self tireAtIndex: i] copy];
   [carCopy setTire: tireCopy atIndex: i];
}
 return (carCopy);
} // copyWithZone

...

为什么这里并没有使用NSArray代替for循环?

因为NSArray的copy方法只会创建一个浅复制(shalldow copy)而不是深复制(deep copy)。

关于浅复制和深复制(deep copy)见另一篇笔记分析。

猜你喜欢

转载自blog.csdn.net/qq_33737036/article/details/81462548