Objective-C中的类目(Category),延展(Extension),协议(Protocol)这些名词看起来挺牛的,瞬间感觉OC好高大上。在其他OOP语言中就没见过这些名词,刚看到这三个名词的时候,有种感觉这是不是学习的坎?这东西难不难?能不能学会?经过本人亲自验证,这三个东西理解起来还是蛮简单的,学过C++或者Java的小伙伴对比理解还是蛮轻松的。类目(Category)就是给已有的类扩充相应的方法,扩充的方法是公有的,类目还可以起到分模块的功能,下面会详细说到。 延展(Extension)这个名词就是是匿名类目的别称,匿名类目就叫做延展,延展可以实现类方法的私有化,具体如何实现,下面有源码。协议我个人感觉和Java中的接口极为相似,在定义对象时使用协议,个人感觉和Java中得泛型有着异曲同工之妙,看下文的详细介绍吧。(本文为笔者个人总结,欢迎批评指正)。
一.Objective-C中的类目(Category)
在Objective-C比其他OOP的编程语言多了个类目,在OC中除了用继承来扩充类的功能函数外我们还可以用类目来实现。学过C++的小伙伴们是否还记得友元这个概念呢?友元就是非本类的方法可以使用本类中得变量,这也是对类方法的一个扩充,个人感觉在OC中得类目和C++中的友元有着异曲同工之妙(仅代表个人观点,欢迎批评指正),下面我们就来详细的学习一下OC中得类目吧。
提到类目呢,首先我们会问我们具体能拿类目做些什么事情呢下面做一下总结:
1.可以用类目给已有的类扩充方法
2.可以用类目把类的实现按功能模块分为不同的文件
3.可以用来扩展NSObject类的方法,也叫做非正式协议
编译环境说明: iMac OS X 10.9 (13A603) 编译器:XCode 5.0.2版本
1.给已有的类扩充方法
在Xcode中新建CategoryTest类,在新建类中声明两个实例变量,在实现类中重写description方法,打印输出两个实例变量的值
代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
//CategoryTest.h #import <Foundation/Foundation.h> @interface CategoryTest : NSObject //定义两个私有的属性 { @ private int ludashi1; int ludashi2; } @end //CategoryTest.m #import "CategoryTest.h" @implementation CategoryTest //重写description方法 -(NSString *) description { return [NSString stringWithFormat:@ "ludashi1 = %d, ludashi2 = %d" , ludashi1,ludashi2]; } @end |
新建一个CategoryTest的类目,来进行对类方法的扩充,
代码如下:
1
2
3
4
5
6
7
8
9
|
// CategoryTest+CategoryExtendFunction.h // Memory // Created by ludashi on 14-8-4. // Copyright (c) 2014年 Mr.li. All rights reserved. #import "CategoryTest.h" @interface CategoryTest (CategoryExtendFunction) //利用类目扩展新的方法 -( void ) extendFunction; @end |
实现文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// // CategoryTest+CategoryExtendFunction.m // Memory // Created by ludashi on 14-8-4. // Copyright (c) 2014年 Mr.li. All rights reserved. #import "CategoryTest+CategoryExtendFunction.h" @implementation CategoryTest (CategoryExtendFunction) //实现扩展的方法 -( void )extendFunction { NSLog(@ "鲁大师,你好!我是通过类目扩展的方法!" ); } @end |
测试运行结果:
1
|
2014-08-04 17:08:46.187 Memory[1621:303] 鲁大师,你好!我是通过类目扩展的方法! |
2.对把类中不同的功能模块分成不同的文件
1.给上面的类创建两个类目,类目中分别存放实例变量的getter和setter方法,为了节省篇幅下面给出其中一个类目的事例;
接口的声明:
1
2
3
4
5
6
7
8
9
10
11
|
// CategoryTest+Categgory1.h // Memory // Created by ludashi on 14-8-4. // Copyright (c) 2014年 Mr.li. All rights reserved. #import "CategoryTest.h" @interface CategoryTest (Categgory1) //声明Category中实例变量ludashi1的getter和setter方法 -( void ) setLudashi1:( int ) vLudashi; -( int ) ludashi1; @end |
类目的实现文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// CategoryTest+Categgory1.m // Memory // Created by ludashi on 14-8-4. // Copyright (c) 2014年 Mr.li. All rights reserved. #import "CategoryTest+Category1.h" @implementation CategoryTest (Categgory1) //实现ludashi1的getter和setter方法 -( void )setLudashi1:( int )vLudashi { ludashi1 = vLudashi; } //getter方法 -( int ) ludashi1 { return ludashi1; } @end |
对代码测试的结果:
1
|
2014-08-04 17:08:46.188 Memory[1621:303] ludashi1 = 10, ludashi2 = 20 |
3.非正式协议
非正式协议就是给NSObject类创建的类目又叫做非正式协议, 非正式协议一般不需要进行实现,一般在子类中进行方法的重写。代码在这就不赘述啦!
类目的优缺点分析(下面有些是个人观点,不对之处请批评指正)
优点:上面的功能也是类目存在的重要原因之所在,在这就不重复了
局限性: 在类目中只可以为类添加方法,不能添加实例变量; 类目中得方法的优先级要高。
二.Objective-C中的延展(Extension)
简单的说匿名类目就是延展,在延展中定义的方法是类私有的方法只能在类的内部调用,定义延展的方式就是把类目中括号中得名字省略掉,括号保留这就是延展。其实在延展中定义的方法不是真正的私有方法和C++, Java中得方法还有所区别,在类初始化的文件中引入相应延展的头文件,其延展对应的方法也是可以访问的。是通过隐藏延展的头文件来达到方法私有 的。
定义私有方法有以下三种方式:
1.通过延展来实现方法的私有,延展的头文件独立。这种方法不能实现真正的方法私有,当在别的文件中引入延展的头文件,那么在这个文件中定义的类的对象就可以直接调用在延展中定义所谓私有的方法。demo如下:
代码如下:
延展相应的头文件,延展方法的实现在类对应的.m中给出实现方法:
1
2
3
4
5
6
|
#import "ExtensionTest.h" @interface ExtensionTest () -( void )privateFunction1; @end |
2.第二种实现延展的方式是延展没有独立的头文件,在类的实现文件.m中声明和实现延展,这种方法可以很好的实现方法的私有,因为在OC中是不能引入.m的文件的
3.第三种实现方法私有的方式是在.m文件中得@implementation中直接实现在@interface中没有声明的方法,这样也可以很好的实现方法的私有。
Extension.m中的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
#import "ExtensionTest.h" #import "ExtensionTest_Extension1.h" //在实现方法里声明延展 @interface ExtensionTest() -( void ) privateFunction2; @end @implementation ExtensionTest //实现各种方法 -( void )publicFunction { NSLog(@ "publicFunction PS:我是正儿八经的公用方法,我在.h中被声明,在.m中被实现" ); //调用各种私有方法 [self privateFunction1]; [self privateFunction2]; [self privateFunction3]; } //实现第一个私有方法(第一种实现类方法私有化的方法) -( void )privateFunction1 { NSLog(@ "PrivateFunction1 PS:我是在别的头文件中定义的延展,在.m中被实现" ); } //实现第二个私有方法(第二种实现类方法私有化的方法) -( void )privateFunction2 { NSLog(@ "PrivateFunction2 PS:我是在本文件中定义的延展,在本文件中进行实现!" ); } //在头文件中为声明的方法在.m中直接定义是私有的方法 -( void )privateFunction3 { NSLog(@ "PrivateFunction3: 我是在实现方法中直接定义的方法,我也是私有变量" ); } end |
在main函数里进行测试,如果在main函数里引入#import "ExtensionTest_Extension1.h"也可以调用其里面声明的相应的方法
测试代码如下:
1
2
3
4
|
//测试延展 ExtensionTest *extension = [ExtensionTest new ]; [extension publicFunction]; [extension privateFunction1]; |
运行结果:
1
2
3
4
5
|
2014-08-05 15:54:46.147 Memory[1683:303] publicFunction PS:我是正儿八经的公用方法,我在.h中被声明,在.m中被实现 2014-08-05 15:54:46.149 Memory[1683:303] PrivateFunction1 PS:我是在别的头文件中定义的延展,在.m中被实现 2014-08-05 15:54:46.149 Memory[1683:303] PrivateFunction2 PS:我是在本文件中定义的延展,在本文件中进行实现! 2014-08-05 15:54:46.150 Memory[1683:303] PrivateFunction3: 我是在实现方法中直接定义的方法,我也是私有变量 2014-08-05 15:54:46.150 Memory[1683:303] PrivateFunction1 PS:我是在别的头文件中定义的延展,在.m中被实现 |
三、Objective中得协议Protocol
协议(protocol)提到OC中得协议个人感觉和JAVA中的接口的用法极为相似。把类中常用的方法抽象成OC中得协议,协议中只有方法的声明没有方法的实现,在protocol中可以把方法定义成@required(必须的):在使用协议的类中如果不实现@required的方法,编译器不会报错但会给出警告。还可以把protocol中的方法定义成@optional(可选的)如果在使用协议的类中不实现@optional方法,则不会警告。协议的关键字用@protocol来定义。
下面是协议的一个简单demo;
1.在Xcode中新建一个Protocol,命名为FirstProtocol,文件名为FirstProtocol.h . 在FirstProtocol协议中声明了两个方法,一个是@required一个是@optional的
1
2
3
4
5
6
7
8
9
10
11
12
|
#import <Foundation/Foundation.h> //创建第一个protocol @protocol FirstProtocol <NSObject> //为protocol里加入必须实现的方法 @required -( void )requiredFunction; //定义可选的方法 @optional -( void )optionalFunction; @end |
2.新建一个类命名为ProtocolClass, 在ProtocolClass.h中使用FirstProtocol协议,在ProtocolClass.m文件中实现协议中得方法
ProtocolClass.h的代码如下:
1
2
3
4
5
|
#import <Foundation/Foundation.h> #import "FirstProtocol.h" //在普通类中实现协议的方法如下<> @interface ProtocolClass : NSObject<FirstProtocol> @end |
ProtocolClass.m的代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#import "ProtocolClass.h" //不实现协议中必须的方法会产生警告 @implementation ProtocolClass //实现协议中必须的方法: required方法 -( void ) requiredFunction { NSLog(@ "RequiredFunction PS: 我是协议中required方法,不实现我会有警告!" ); } //实现协议中可选的方法,不实现不会有警告 -( void ) optionalFunction { NSLog(@ "OptionalFunction PS: 我是protocol中得可选协议,不实现我,不会有警告!" ); } @end |
测试的运行结果为:
1
2
|
2014-08-05 17:38:50.189 Memory[1907:303] RequiredFunction PS: 我是协议中required方法,不实现我会有警告! 2014-08-05 17:38:50.190 Memory[1907:303] OptionalFunction PS: 我是protocol中得可选协议,不实现我,不会有警告! |
在声明对象的时候引入协议可以类比这Java中得泛型来学习, 例如声明一个遵守FirstProtocol协议的对象: id<FirstProtocol> obj;下面我们将用一个事例来介绍具体的用法
1.创建一个CalculatorProtocol的协议,在协议中声明一个calculatorFunction的方法来进行两个数的计算,文件名为:calculatorProtocol.h
代码如下:
1
2
3
4
5
|
#import <Foundation/Foundation.h> //声明计算方法 @protocol CalculatorProtocol <NSObject> -( void )calculatorFunction : ( int ) x withY : ( int ) y; @end |
2.在CalculatorClass类中添加新的方法,在这个类中有一个计算方法,需要对两个数的计算,有一个参数是对象类型的必须遵循协议CalculatorProtocol,主要代码如下:
1
2
3
4
5
6
7
|
//实现传入的对象必须服从协议的方法 -( void ) calculatorFunction:( int )x withY:( int )y withObj:(id<CalculatorProtocol>)obj { [obj calculatorFunction:x withY:y]; } |
3.定义遵循协议calculatorProtocol的类AddClass,在AddClass中实现calculatorFunction方法,实现两个数相加的功能代码如下
1
2
3
4
5
6
7
8
9
10
11
|
#import "AddClass.h" @implementation AddClass //实现CalculatorProtocol必须的方法 -( void )calculatorFunction:( int )x withY:( int )y { int a = x + y; NSLog(@ "AddClass PS: 我是实现协议的加方法%d + %d = %d" , x, y, a); } @end |
4.新建一个DecClass类,同样遵循calculatorProtocol协议,实现两个数相减的功能,主要代码如下:
1
2
3
4
5
6
7
8
9
10
|
#import "DecClass.h" @implementation DecClass //实现protocol中必须实现的方法 -( void ) calculatorFunction:( int )x withY:( int )y { int a = x - y; NSLog(@ "DecClass PS: 我是重写的减方法%d - %d = %d" , x, y, a); } @end
|
测试代码:
1
2
3
4
5
6
7
|
//测试协议对象 AddClass *add = [AddClass new ]; //往protocol对象中的calculator方法中传入符合协议的add对象 [pro calculatorFunction:2 withY:2 withObj:add]; DecClass *dec = [DecClass new ]; [pro calculatorFunction:4 withY:3 withObj:dec]; |
运行结果如下:
1
2
|
2014-08-05 17:38:50.190 Memory[1907:303] AddClass PS: 我是实现协议的加方法2 + 2 = 4 2014-08-05 17:38:50.191 Memory[1907:303] DecClass PS: 我是重写的减方法4 - 3 = 1 |
再举一个理解协议更好理解协议的例子吧,我们声明一个文件协议,协议的内容是对文件的读和写。我们在声明一个文件管理系统的类,只要是文件能读和写就能放进我们的文件管理系统进行管理。
1.指定可放入文件管理系统文件需要遵循的协议,协议中规定文件必须有读写的功能
代码如下
#import <Foundation/Foundation.h> @protocol FileManagerProtocol <NSObject> //读方法 -(void) read; //写方法 -(void) writer; @end
2.编写文件管理系统,来对所有遵守协议的文件来进行的统一的管理
代码如下:
声明:
#import <Foundation/Foundation.h> #import "FileManagerProtocol.h" @interface FileManagerSystem : NSObject -(void) insertFileSystem: (id<FileManagerProtocol>) file; @end
实现:
#import "FileManagerSystem.h" @implementation FileManagerSystem -(void)insertFileSystem:(id<FileManagerProtocol>)file { [file read]; [file writer]; } @end
3.定义新的文件类来遵守我们的文件读写协议,之后就可以放入到我们的管理系统中进行管理
文件类1
#import <Foundation/Foundation.h> #import "FileManagerProtocol.h" @interface File : NSObject<FileManagerProtocol> @property (nonatomic,strong) NSString *fileName; @end #import "File.h" @implementation File //实现协议中的方法 -(void)read { NSLog(@"我是文件%@,你可以对我进行阅读",_fileName); } -(void)writer { NSLog(@"我是文件%@,你可以对我进行修改",_fileName); } @end
在定义一个简历文件,同样遵守我们的文件协议
#import <Foundation/Foundation.h> #import "FileManagerProtocol.h" @interface JianLi : NSObject<FileManagerProtocol> @property (nonatomic, strong) NSString *fileName; @end #import "JianLi.h" @implementation JianLi -(void)read { NSLog(@"对简历%@的读", _fileName); } -(void)writer { NSLog(@"对简历%@的写", _fileName); } @end
然后我们可以把各种不同文件但都遵循我们文件协议的文件放入到我们的文件管理系统进行管理
1 //声明文件,然后放入文件管理系统 2 File *file = [File new]; 3 file.fileName = @"浪潮之巅"; 4 5 //实例化文件二,只要符合文件协议即可 6 File *file1 = [File new]; 7 file1.fileName = @"file1"; 8 9 JianLi *jianLi = [JianLi new]; 10 jianLi.fileName = @"lusashi的简历"; 11 12 //实例化文件管理系统 13 FileManagerSystem *fileSystem = [FileManagerSystem new]; 14 15 16 17 //把书加入到管理系统中 18 [fileSystem insertFileSystem:file]; 19 [fileSystem insertFileSystem:file1]; 20 [fileSystem insertFileSystem:jianLi];
运行结果:
1 2014-08-14 12:05:47.956 Memory[985:303] 我是文件浪潮之巅,你可以对我进行阅读 2 2014-08-14 12:05:47.958 Memory[985:303] 我是文件浪潮之巅,你可以对我进行修改 3 2014-08-14 12:05:47.958 Memory[985:303] 我是文件file1,你可以对我进行阅读 4 2014-08-14 12:05:47.959 Memory[985:303] 我是文件file1,你可以对我进行修改 5 2014-08-14 12:05:47.959 Memory[985:303] 对简历lusashi的简历的读 6 2014-08-14 12:05:47.959 Memory[985:303] 对简历lusashi的简历的写