Objective C基础(四)---Category、Extension、Protocol

Category、Extension、Protocol是oc中比较特色的技术。

一、Category类目:
Category类目允许你为一个已存在的类添加一些方法而不用子类化该类,也不需要你了解该类的细节。已存在的类包括框架类库中的类和自己创建的类。任何oc中的封装类都可以使用Category类目进行功能扩展。我们随便找一些介绍类目的资料都会发现,类目和继承类是那么的相似,类目相对于子类继承来说并没有节约多少代码。那么为什么要用类目呢?我们通过使用来发现类目的使用之地。

oc这门语言在对c进行类封装的时候,做了很多的类簇型封装类。也就是一个类中设置了很多的私有子类来将类功能分模块化实现:像NSArray、NSString、NSDictionary都是以类簇方式实现的。这种类簇形式体现的封装类是不推荐使用继承的。

例如:如果我们尝试继承自NSString来创建一个字符串的子类,我们可以看看会有什么现象:

@interface MyClass : NSString
@end
@implementation MyClass
@end

然后我们在main方法中来进行简单的调用:
这里写图片描述

我们使用继承的方式创建了一个NSString的子类,我们发现能编译通过并可以创建字符串,但是xcode给了我们警告:
incompatible pointer types initializing “MyClass *”with an expression of type “NSString”
不兼容的指针类型初始化:MyClass类带有NSString的类型描述。
也就是说,苹果公司并不希望开发者对已经封装好的类簇型封装类使用继承的方式来进行功能扩展,这个时候我们需要使用类目。

我们新建一个类目:在工程中command+N,我们选择OS X选项中的第二排Objective-C File图标来进行快速创建类目:在File中填入文件名称,注意不带后缀.h或者.m,xcode会直接将头文件和实现文件都创建出来;在File Type中选择Category,选择我们要针对已有的要扩展的类,比如我们选择NSString。
这里写图片描述
然后xcode就会帮我们自动创建好类目的头文件和实现文件:
NSString+MyClass.h、NSString+MyClass.m,我们会发现xcode自动给我们创建的类目文件名是很特别的,这是oc对类目的编码规范。
我们做一个小功能:给NSString添加上可追加字符串的功能。
NSString+MyClass.h中的代码:

@interface NSString (MyClass)

-(id)appendIngNewString:(NSString *)newString;

@end

NSString+MyClass.m中的代码:

@implementation NSString (MyClass)
-(id)appendIngNewString:(NSString *)newString{
    NSMutableString *result = [NSMutableString stringWithString:self];
    [result appendString:newString];

    return result;
}
@end

然后我们在main中调用下,看看效果:
这里写图片描述

这样我们就简单的给NSString设置了一个可追加字符串的功能,当然实际上还是使用了NSMutableString的追加字符串功能。

那么,通过这个小例子我们也发现了类目的一大好处,就是我们在原有类上进行功能扩展时都不需要修改引用就具有了扩展功能。这一点java是做不到的,java需要使用继承来进行扩展,使用继承就不得不引入新的子类来做,代码变动是比较大的。

另外,如果我们留心一点会发现,oc的头文件和实现体文件是可以声明多条类目(Category)或者延展(Extension)的。
这个该怎么使用呢。在说完延展之后,我们再来详说这个地方的巧妙使用。

类目的特点总结一下:
1.同一个类的类目名不能重复,但是可以给同一个类添加任意多个不同名的类目。
2.类目中不可以添加新的实例变量。
3.如果类目中的方法和已有类中的方法名冲突,类目中的方法优先级要高,所以在使用类目进行扩展时要避免方法冲突。
4.类目中的方法不强制实现。子类在继承时并不一定要实现类目中声明的方法。

非正式协议:由于NSObject的特殊性,给NSObject声明的类目其方法通常是不实现的,而由子类来实现,但是由于类目中的方法并不是强制要子类来实现,所以被称为非正式协议。

二、延展Extension:
在类目的基础上,我们仍旧需要针对一些特殊需求进行处理,有些类目的方法我们并不想让外部类知道甚至调用,那我们就只有考虑将方法私有化了。cocoa框架仅将头文件提供给开发者进行参考,在实现文件中实际上也做了很多的保密,这个苹果不会对我们开放,在其中进行私有化的功能设置,这就用到了私有化的类目和延展特性。

@interface Test()
-(void)testA;
@end
@implementation Test
  ...
@end

举个例子,我们以一个学生为例:
Student.h

@interface Student : NSObject

-(void)study;
-(void)exam;
@end

Student.m

@implementation Student

-(void)study{
    NSLog(@"学生要好好学习");
}
-(void)exam{
    NSLog(@"学生参加考试来检验学到的知识");
}

@end

但是要考试了,学生没学好,想打个小抄,总不能监考老师看到吧,那就得偷偷来做了:
我们不能把小抄方法放在Student.h中,因为那是公开的,只要import引用头文件就可以直接调用小抄方法,那还不得让监考老师逮个正着啊。那怎么办呢,使用extension,在Student.m中私有化操作:

#import "Student.h"

@interface Student ()
-(void)plagiarize;//抄袭
@end

@implementation Student

-(void)study{
    NSLog(@"学生要好好学习");
}
-(void)exam{
    NSLog(@"学生参加考试来检验学到的知识");
    [self plagiarize];
}
-(void)plagiarize{
    NSLog(@"学生没好好学习,为了高分选择抄袭");
}
@end

这里写图片描述

这样就达到了学生偷偷作弊的行为,而不被老师发现。
如果我们尝试在main方法中去调用我们设置的作弊方法plagiarize,xcode会给我们抛出一个错误,告诉我们没有公开可见的@interface声明了该方法。这样就充分的达到了保密的效果。

Extension延展的使用是多种多样的:

1.我们可以单独创建一个延展的头文件,然后引用到该类的.m实现文件中。这个延展是公开的,其他类也可以引用这个延展的头文件,并可以通过相关的类的实例调用这个延展中的方法,这样起不到封装的效果。
2.像上面的示例那样在该类的.m实现文件中声明延展,并进行功能扩展,这样就起到了封装的效果,外面任何类都无法调用这个延展中设置的方法。
3.不声明延展,直接在该类的实现文件中实现一个功能方法,该功能方法不在其头文件中声明(即不公开),同样起到私有化的效果。这一点就很像 java中使用private限定的方法了。

类目Category与延展Extension:
1.类目需要提供一个名称以便进行区别调用和实现,延展则没有名称(更像个匿名类)。
2.类目不能设置属性,只能设置方法,包括类方法和实例方法;延展可以设置属性和方法,包括类方法和实例方法。

我们接着补充上面提到的一条内容:.h头文件和.m实现文件中都是可以实现多条的类目category和延展extension的。
.h头文件是为了公开声明,所以在其中设置类目和延展中的方法都是可视的,也是可调用的。如果我们想让一个类在不同的代码模块中体现不同的功能特征,就可以使用.h头文件来设置公开的类目。

.m文件中声明的类目和延展都是私有化的,外部类和外部创建的实例都是不可调用的。这样就起到了封装,保密的效果。如果我们想让我们的类分模块化的具备多种处理功能,就可以在.m文件中声明多个类目和延展来进行分模块化处理。这个实际上有点类簇的味道,就像上面提到的oc中封装的NSString,NSArray等类。

三、协议Protocol:

oc中的协议和java中的接口非常相像。
但是oc并不会强制遵循协议的类实现协议中的方法,虽然协议中的方法可以使用@required @optional进行控制是否强制实现,如果不实现xcode只会给我们提出编译警告,仍旧是可以编译通过的。只是未实现的协议方法,不能进行调用,调用是必然会出错的。
Protocol声明的方法默认是@required修饰的。

oc中的类是可以遵循多个协议的,这一点和java中的接口是一致的。

协议的语法结构:

@protocol 协议名称
//方法列表
@required ...
@optional  ...
@end

//实现部分:
@interface ClassName:父类名<协议1,协议2...>
@end
@implementation ClassName
//实现协议中的方法
@end

我们还以学生举例,学生应当遵守考场纪律,那么就可以设定一个考场纪律的协议:

@protocol ExamRule <NSObject>

@required
-(void)disciplined;

@end

由学生来遵循这个纪律:

#import <Foundation/Foundation.h>
#import "ExamRule.h"

@interface Student : NSObject<ExamRule>

-(void)study;

-(void)exam;
@end

那么我们的监考老师怎么来监督学生呢,设定一个方法检查学生是否遵循了考场纪律:

#import "ExamRule.h"
@interface Teacher : NSObject
-(BOOL)checkExamRule:(id<ExamRule> *)student;
@end
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值