Objective C分类(Category)和协议(Protocol)


Objective C分类(Category)和协议(Protocol)

内容概述

  • 分类(Category)的概念
  • 分类(Category)的用法
  • 协议(Protocol)的概念
  • 协议(Protocol)的用法

8.1 分类(Category)的概念

Objective C中有一个新的语法特性分类(Category),分类可以在不创建子类的情况下,向已经存在的类添加新的方法。在很多情形下Category也是比创建子类更优的选择。新添加的方法同样也会被被扩展的类的所有子类自动继承。

和子类不同的是,Category不能用于向被扩展类添加实例变量。Category通常作为一种组织框架代码的工具来使用。

分类的用途可以归结为以下几点:

  1. 在不创建继承类的情况下实现对已有类的扩展。

  2. 简化类的开发工作,例如,当一个类需要多个程序员协同开发的时候,Category可以将同一个类根据用途分别放在不同的源文件中,从而便于程序员独立开发相应的方法集合。

  3. 将常用的相关的方法分组。

  4. 在没有源代码的情况下可以用来修复BUG。

8.2 分类(Category)的用法

分类大概分为两种,第一种是在原有类的基础上添加新的方法,第二种是将相同方法归类,使用更方便。下面通过一个案例来演示第一种分类的用法。

  1. 定义一个类MyClass其中有两个方法method1和method2,这两个方法简单输出方法名称。
#import <Foundation/Foundation.h>

@interface MyClass : NSObject

-(void)method1;

-(void)method2;

@end

#import "MyClass.h"

@implementation MyClass

-(void)method1{

    NSLog(@"method1...");

}

-(void)method2{

    NSLog(@"method2...");

}

@end
  1. 创建一个分类类,该分类的文件命名格式是:原有类名称+分类名称,例如,MyClass+MyCategory.h,类名称格式是:原有类(分类名称)例如,@interface MyClass (MyCategory)。
#import "MyClass.h"

@interface MyClass (MyCategory)

// 添加两个新方法

-(void)method3;

-(void)method4;

@end

#import "MyClass+MyCategory.h"

@implementation MyClass (MyCategory)

-(void)method3{

    NSLog(@"method3...");

}

-(void)method4{

    NSLog(@"method4...");

}

@end
  1. 测试分类运行结果。
#import <Foundation/Foundation.h>

#import "MyClass+MyCategory.h"

int main(int argc, const char * argv[])

{

    @autoreleasepool {

        // 实例化

        MyClass *mc = [[MyClass alloc]init];

        // 原有方法

        [mc method1];

        [mc method2];

        // 新添加方法

        [mc method3];

        [mc method4];

    }

    return 0;

}

第二种分类的用途就是归类,一般是在同一个文件中定义所有方法。在系统类中也有大量的案例,例如,NSArray数组类的定义。

@interface NSArray : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>

- (NSUInteger)cout;

- (id)objectAtIndex:(NSUInteger)index;

@end

@interface NSArray (NSExtendedArray)

- (NSArray *)arrayByAddingObject:(id)anObject;

- (NSArray *)arrayByAddingObjectsFromArray:(NSArray *)otherArray;

@end

@interface NSArray (NSArrayCreation)

+ (id)array;

+ (id)arrayWithObject:(id)anObject;

@end

@interface NSArray (NSDeprecated)

- (void)getObjects:(id \_\_unsafe\_unretained [])objects;

@end

/**************** Mutable Array  ****************/

@interface NSMutableArray : NSArray

- (void)addObject:(id)anObject;

- (void)insertObject:(id)anObject atIndex:(NSUInteger)index;

@end

@interface NSMutableArray (NSExtendedMutableArray)

- (void)addObjectsFromArray:(NSArray *)otherArray;

- (void)exchangeObjectAtIndex:(NSUInteger)idx1 withObjectAtIndex:(NSUInteger)idx2;

@end

@interface NSMutableArray (NSMutableArrayCreation)

+ (id)arrayWithCapacity:(NSUInteger)numItems;

- (id)initWithCapacity:(NSUInteger)numItems;

@end

下面我们自己定义一个分类,实现步骤如下:

  1. 在同一个文件中定义一个类,两个分类,将方法分为3组。
#import <Foundation/Foundation.h>

// 原有方法

@interface MyClass2 : NSObject

-(void)method1;

-(void)method2;

@end

// 第一组分类

@interface MyClass2(MyCategory2)

-(void)method3;

-(void)method4;

@end

// 第二组分类

@interface MyClass2(MyCategory3)

-(void)method5;

-(void)method6;

@end
  1. 方法实现。
#import "MyClass2.h"

@implementation MyClass2

// 原有类方法实现

-(void)method1{

    NSLog(@"method1...");

}

-(void)method2{

    NSLog(@"method2...");

}

// 第一分组实现

-(void)method3{

    NSLog(@"method3...");

}

-(void)method4{

    NSLog(@"method4...");

}

// 第二分组实现

-(void)method5{

    NSLog(@"method5...");

}

-(void)method6{

    NSLog(@"method6...");

}

@end
  1. 测试程序。
MyClass2 *mc2 = [[MyClass2 alloc]init];

// 原有方法

[mc2 method1];

[mc2 method2];

// 第一组分类

[mc2 method3];

[mc2 method4];

// 第二组分类

[mc2 method5];

[mc2 method6];

使用分类(Category)注意事项:

  1. 分类的好处是, Category用于大型类有效分解。通常一个大型类的方法可以根据某种逻辑或是相关性分解为不同的组,一个类的代码量越大,将这个类分解到不同的文件中就显得越有用,每个文件中分别是这个类的某些相关方法的集合。

当有多个开发者共同完成一个项目时,每个人所承担的是单独的cagegory的开发和维护。这样就版本控制就更加简单了,因为开发人员之间的工作冲突更少了。

  1. 如何在分类和子类之间选择。并没有什么界限分明的判定标准来作为何时用Category何时用添加子类的方法的指导。但是有以下几个指导性的建议:

2.1 如果需要添加一个新的变量,则需添加子类。

2.2 如果只是添加一个新的方法,用Category是比较好的选择。

8.3 协议(Protocol)的概念

Objective C中有一个概念叫协议(Protocol),协议和类的定义类似,但是协议里面只定义了方法的声明,而没有实现。这很像Java语言中的接口,只定义做什么,而没有实现怎么做。这更有利于接口和实现的分离,程序的耦合性更低。

任何类都可以实现一个协议,实现该协议必须实现协议必须实现的方法。协议定义的关键字是protocol。结构和类定义相似。例如,如下代码定义了一个协议。

#import <Foundation/Foundation.h>

// usb协议

@protocol USB <NSObject>

// 读数据方法

-(void)read;

// 协议数据方法

-(void)write;

@end

我们也可以使用预编译指令:@required或@optional来指定该方法是必须实现的方法还是可选实现方法。

8.4 协议(Protocol)的用法

本节我们通过几个案例来讲述如何定义协议,如何实现协议。一个类实现多个协议的用法以及协议实现代理的用法。

下面我们定义一个协议,并定义一个实现类来实现该协议。该案例中我们定义了一个USB协议,该接口中定义两个方法,分别是读写数据。

  1. USB协议的定义。
#import <Foundation/Foundation.h>

// usb协议

@protocol USB <NSObject>

// 读数据方法

-(void)read;

// 协议数据方法

-(void)write;

@end
  1. 定义一个移动设备类Mobile实现该协议,可以实现读写数据,实现协议的语法格式是继承类后面加<协议名称>。
#import <Foundation/Foundation.h>

#import "USB.h"

@interface Mobile : NSObject<USB>

@end

#import "Mobile.h"

@implementation Mobile

// 读数据方法实现

-(void)read{

    NSLog(@"from mobile read...");

}

// 写数据方法实现

-(void)write{

    NSLog(@"write to mobile...");

}

@end

一个类可以实现多个协议,在后续章节中我们使用到的UITableView经常要实现两个以上协议。例如,UITableViewDataSource和UITableViewDelegate。我们自己定义的类也可以实现多个协议,下面这个案例我们定义了两个协议USB协议和Media协议,Mobile类同时实现这两个协议,分别来实现读写数据和播放多媒体文件。

  1. 定义USB协议和Media协议。
#import <Foundation/Foundation.h>

@protocol Media <NSObject>

// 播放音乐

-(void)playMusic;

// 播放视频

-(void)playVideo;

@end
  1. Mobile类同时实现USB和Media协议。
#import <Foundation/Foundation.h>

#import "USB.h"

#import "Media.h"

// 同时实现USB协议和Media协议

@interface Mobile : NSObject<USB,Media>

@end
  1. Mobile类的实现文件。
#import "Mobile.h"

@implementation Mobile

// 读数据方法实现

-(void)read{

    NSLog(@"from mobile read...");

}

// 写数据方法实现

-(void)write{

    NSLog(@"write to mobile...");

}

// 播放音乐

-(void)playMusic{

    NSLog(@"playMusic...");

}

// 播放视频

-(void)playVideo{

    NSLog(@"playVideo...");

}

@end

另外,代理常用来实现代理设计模式,协议经常用来实现委托对象。一个委托对象是一种用来协同或者代表其他对象的特殊对象。

下面通过一个现实生活中的例子,来理解代理。在IBM笔记本风靡的年代,全世界有大量的IBM代理商为IBM代理销售笔记本电脑。这个例子可以很好的理解代理设计模式。该案例的实现步骤如下:

  1. 创建一个IBM代理类,IBMDelegate,并添加一个销售方法sale。
@protocol IBMDelegate <NSObject>

// 销售电脑

-(void)sale;

@end
  1. 创建一个IBM类,该类实现IBMDelegate协议,并添加该协议属性。
// 实现代理协议

@interface IBM : NSObject<IBMDelegate>

// 生产电脑方法

-(void)produce;

// 代理属性

@property(nonatomic,strong)id<IBMDelegate> delegate;

@end
  1. 实现IBM类,在初始化方法中指定代理,并实现produce和sale。
@implementation IBM

// 初始化方法

- (id)init

{

    self = [super init];

    if (self) {

        // 指定代理

        self.delegate = self;

    }

    return self;

}

// 生产电脑方法

-(void)produce{

    NSLog(@"生产电脑...");

}

// 销售电脑

-(void)sale{

    NSLog(@"销售电脑...");

}
  1. 在main方法中测试。
#import <Foundation/Foundation.h>

#import "IBM.h"

int main(int argc, const char * argv[])

{

    @autoreleasepool {

        // 实例化

        IBM *ibm = [[IBM alloc]init];

        // 生产方法

        [ibm produce];

        // ibm的代理的sale方法

        [ibm.delegate sale];

    }

    return 0;

}

上述代理模式就实现了IBM生产电脑,IBM代理来销售电脑的代码分离。降低了程序的耦合性。