概述
OC用于拓展已存在类的内置功能是它最强大的功能之一。类目、延展、协议提供了可以让你扩展类功能的方式。使用他们,无需继承便可以扩展类功能。需要注意的是,这些手段只能增加类的方法,并不能用于增加实例变量,要想增加实例变量,还是需要定义子类来实现。
1、类目(Category):指向已知的类,增加新的方法,不会破坏封装性。已知的类既包括已定义的类,也包括系统已有的类。
2、延展(Extension):即通过在自己类的实现文件中添加类目来声明私有方法。
3、协议(Protocol):声明一些方法,但让别的类来实现,也能为类增加方法。
类目 Category
类目简介
类目也称为分类。通过定义类目,你可以为已知的类增加新的方法,哪怕是那些你没有源码的类。这是OC提供的一种强大的功能,使得你不用定义子类就能扩展一个类的功能。使用类目,你还可以将你自己的类的定义分开放在不同的源文件里。
-
可以为已知的类添加方法,哪怕是你没有源码的类;
-
通过类目添加方法会成为原始类的一部分,调用与其他方法一致;
-
与原类中的方法同级;
-
在父类中添加类目子类会继承该类目中的方法,但在子类中添加类目父类无法拥有这些方法;
-
把类中的方法归类,可以更好地管理和维护类;
类目的声明和实现
1、类目的命名规则:类名+扩展方法,如“ClassName+CategoryName”。类目不继承于父类,但接口与定义类很相似,用一个括号表示用途。注意定义类目的时候一定要把原类包含进来。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
2、实现
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
类目的创建
-
步骤1:在工程目录中按 command + N > 选择OS X环境 >选择 Objective-C File 模板 >点击Next进入配置界面
-
步骤2:在File项输入类目名 >File Type项选择文件类型(Category、Extension、Protocol)>Class项选择拓展类(Category) >点击Next
-
创建之后,工程目录列表如下,
Person + Handle.h
以及Person + Handle.m
文件为创建的类目文件。
类目的使用
通过类目加入的方法,会成为原始类的一部分。例如:通过类目给Person
类增加方法,编译器会把这些方法加到Person
类的定义里面。通过类目加入的方法,使用起来和原始类里面的方法没有等级差别,同等对待。类目里定义的方法会被原始类的子类所继承,就跟原始类的其他方法一样。使用类目的最大好处就是可以扩展别人实现的类,而且不需要获得原始类的源代码。但需要注意以下几点:
-
不能在类目中添加实例变量;
-
可以为同一类添加多个类目,但类目名和方法名不能重复;
-
如果添加的方法和系统重名,优先实现类目中的方法,因此,不能随意重写类的方法;
现在在刚刚创建的Person + Handle.h
文件中,声明方法SayHi()
。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
实现方法
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
调用方法
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
类目的局限性
-
无法向类中添加实例变量,如果需要添加实例变量则只能在子类中添加;
-
如果在类目中重写父类方法,可能导致super消息的断裂,因为在类目中的方法优先级高于父类。
类目中的属性
在类目中不能声明实例变量,但是允许声明属性,值得注意的是,如果声明成属性,并不会生成setter( )、getter( ) 方法,并且在类目中不能使用关键字@synthesize
,只能通过@dynamic
动态合成变量和属性,在原始类中必须存在对应的实例变量;
假设在刚创建的类目中添加一个属性address
,代码如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
在main函数通过点语法调用此属性时,程序奔溃,因为系统并未生成对应的setter( )、getter( )方法,改进方法如下,首先在Person.h
文件中声明对应的实例变量_address
,代码如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
其次在Person + Handle.m
文件中,动态合成address
属性,并实现对应的setter( )、getter( )方法,代码如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
现在在main函数中通过类目访问address就没有问题啦。
延展 Extension
类的私有方法:
1、objective-c中没有绝对意义上的私有方法;
2、 在.h文件中声明的方法都属于公开的方法,意味着开放给别人调用;
3、如果不想公开某些方法,可以不在.h文件中声明;
4、 这样的方法可以被本类中的其他方法所调用;
5、如果在类外面强行调用这些未公开的方法,也能调用,但是会有编译器警告;
延展简介
-
延展是匿名类目,为当前类添加私有方法;
-
使用类目增加的方法是让外部可见,而延展的目的是增加方法,让外部不可见;
-
不可见的目的更多的是封装代码,对于想要隐藏的算法和接口,可以使用延展;
-
延展接口部分写在当前类的.m文件中,实现部写在当前类的实现中;
- 1
- 2
- 3
- 1
- 2
- 3
延展使用
- 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
- 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
延展补充
- 在延展中,允许声明实例变量、属性以及方法,但是这些都是私有的,只能在对应类的实现文件访问。声明顺序如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
协议 Protocol
协议简介
-
协议是一个命名的方法列表,是对象之间的交互原则与共识,一个类使用协议称作该类遵守了协议;
-
协议中的所有内容,只是作为一个声明,由遵守该协议的类的实现部分去实现协议方法(和我们生活中一样,如果没有人履行它,协议就是无用的纸而已);
-
协议可以声明属性和方法,协议的目的是告诉外部我一定会有,所以它无法用来声明全局变量,而实际上,你声明的属性和方法具体是否存在,得看你是否正确的履行了协议中的所有内容;
-
协议声明的属性和方法可以选择是否必须,关键字分别为@required必须和@optional可选,默认为必须;
-
苹果同样提供了专门的协议文件模版,它只有一个.h文件。其创建方法与延展类似,只需将File Type项选择为Protocol即可;当然还有更简便的创建方法,通过
@protocol
指令来定义一个协议,后文主要讲解通过@protocol
指令创建协议的方式。
协议的定义
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
协议的遵守
1、接受协议在某些方面与声明父类很相似:
-
它们都为类带来了额外的方法声明;
-
它们都写在类的接口的类名后;
2、当一个类把一个协议的名字列在它的父类名后的尖括号中,它即被称为“接受”了这个协议
- 1
- 1
3、一个类可同时遵守多个协议,协议名之间以逗号隔开。
- 1
- 1
4、一个类实现了协议中声明的方法,称为“确认”了这个协议。协议需要被其他类所“确认”,否则这个协议就没有什么意义了。
5、可通过如下方法检查对象是否实现协议或协议方法
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
Tips
1、协议同样可以用于对对象进行类型指定。在使用协议进行类型指定是时,协议名写在类名后的尖括号中。
2、
id <MyProtocol> anyObject;
,这表明anyObject是接受了MyProtocol协议的id类型的对象。
协议下的代理委托模式
该示例涉及类为Teacher
类与Student
类,在Person类中定义一个协议,并在协议中指定买书的方法,Teacher类不直接实现买书,而是指定代理,委托Student类实现。日常生活中也会经常遇到这种情况,比如你现在正在做饭,突然发现没盐,而你不能抽身,此时你将会选择别人帮你实现下楼买盐的步骤,这就设计到代理委托模式。如下将具体讲解协议下的代理委托模式的实现。
1、第一步,在"Teacher.h"
文件中定义一个协议,指定买书方法,设置代理属性delegate,并在Teacher类的接口部分声明一个buyBooks()
方法;
- 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
- 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
2、第二步,在Teacher.m
文件中,实现buyBooks()
方法,判断代理是否存在并且实现了协议,如果代理存在并且实现了协议方法,则让代理人执行买书操作。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
3、第三步,在main函数中,引入Teacher类与Student类,并实例化相应对象,将teacher对象的代理属性指定为student对象。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
4、第四步,在Student.h
文件中,导入Teacher.h
,并遵守TeacherDelegate
协议。
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
5、第五步,在Student.m
文件中,实现协议方法。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
Tips
声明delegate属性需定义成weak,而非strong,因为两者之间必须为“非拥有关系”。通常情况下,扮演delegate的那个对象也要持有本对象。例如在本例中,想使用Teacher的那个对象就会持有本对象,直到用完本对象之后,才会释放。假如声明属性的时候用strong将本对象与委托对象之间定为“拥有关系”,那么就会引入“保留环”。因此,本类中存放委托对象的这个属性要么定义成weak,用么定义成unsafe_unretained,如果需要在相关对象销毁时自动清空,则定义为前者,若不需要自动清空,则定义为后者。
6、第六步,在main函数指定teacher对象代理之后,调用buyBooks()
方法,观察控制台输出情况,输出如下:
- 1
- 2
- 1
- 2
类目与非正式协议
-
创建一个NSObject的类目而不实现称为“创建一个非正式协议”;
-
因为一般情况下类都从NSObject继承,所以NSObject的类目中所声明的方法,这个类可以实现也可以不实现;
-
因为NSObject的特殊性,所以NSObject的类目声明称为非正式协议
原文:http://blog.csdn.net/hierarch_lee/article/details/49922925