OC基础8:分类和协议

时间:2022-12-28 17:45:58

  "OC基础"这个分类的文章是我在自学Stephen G.Kochan的《Objective-C程序设计第6版》过程中的笔记。

1、关于分类(category):

(1)、分类用来拓展现有的类,增加方法,同时可以不必访问主类的源代码,也不用创建子类;

(2)、只能增加方法,不能增加变量;

(3)、在代码中也只需要import进父类的h文件,不能编写任何继承的语法,因为不是子类;

(4)、在分类里添加的新方法会被子类继承。

2、编写一个分类的模板:

原类是MyClass,分类的名称是MyClass+Cate,那么模板如下:

#import “MyClass.h”

@interface MyClass (Cate)

…   //各种方法

@end

其中要注意:import文件使用的是双引号,@interface后面没有继承的冒号,紧跟着用括号声明这个分类的名称。可用下面的图片来说明:

OC基础8:分类和协议

3、

分类的m文件的内容可以写在主类的m文件里面。

比如Class和ClassCateClass可以共用一个Class.m文件。也可以各自分开为Class.m文件和ClassCateClass.m文件,如果分开的话, ClassCateClass.m文件应该这样写:

#import “ClassCateClass.h”

@implementation Class (CateClass)

@end

4、由3也可以看到,分类的h文件和m文件的命名方式。直接用主类名称加上分类名称来命名,比如Class+CateClass.h,或者可以是ClassCateClass.h;

5、扩展(extension):类的扩展是一种特殊的分类,是没有命名的分类。扩展可以定义新的实例变量和属性,这在分类里是不允许的。它的定义语法和分类基本一样,区别只是在括号里不需要写出名称,保持空即可。即是只要在类名后加一对括号即可。

6、扩展类的方法是私有的,即是它定义的数据和方法只供这个类本身使用。

7、扩展类的@interface部分最好写在主类的m文件里,保持代码良好的封装性。

8、要添加额外方法,分类(category)必须在第一个@interface中声明方法,并且在@implementation中提供实现,不然运行时会出错。而类扩展(extension)中添加的方法可以不在第一个@interface中去声明,而且没有必要在扩展里添加方法,因为扩展里声明的方法是私有方法,而不需声明只定义在@implementation中的方法就是私有方法了。

9、分类可以覆写主类的方法,不过覆写之后就再也不能访问原来的方法了,因为它们不是父类子类的关系,无法通过super关键字来访问。所以如果要覆写方法,或者使用子类,或者要先小心地把主类方法中的所有功能复制到覆写的方法里。

10、协议:协议是一串方法的列表,只有方法名,没有方法的具体实现。当某些类遵守某个协议之后,在类里面才会去具体地定义协议包括的这些方法。当某一个类声明遵守了某项协议之后,我们就可以在不知晓这个类的源代码的情况下,通过协议规定的方法来访问这个类。

11、协议使用@protocol指令和@end指令来定义,在这两者之间仅仅需要声明一些方法即可。声明的方法默认是@required类型的,即是遵守这个协议的类必须去实现的。如果有一些选择实现的方法,使用@optional去定义。模板如下,定义在h文件内:

@protocol Xprotoc

…   //方法列表名,声明在这里的方法是遵守这个协议的类必须要实现的

@optional   //从这个关键字开始,下方的方法列表是不强制要求实现的

…   //不强制要求实现的方法列表

@required   //从这个关键字开始,下方的方法列表又是必须要实现的了

…   //要求必须实现的方法列表

@end

12、声明某个类XCalss遵守某个协议XProtoc的模板如下:

@interface XClass: Nobject <XProtocol>

尖括号内可以列出多个协议,使用逗号分开即可。

13、如果有一个对象xClass,想要测试它是否遵循了XProtoc协议,可以使用以下方法:

if ([xClass conformsToProtocol: @protocol (XProtoc)] == YES) {

...

}

14、使用id类型声明对象时,使用以下命令:

id <XProtoc> xClass;

那么当程序为xClass指定静态类型的对象的时候,如果这个对象不遵循XProtoc协议,编译器就会提示警告。尖括号内可以用逗号隔开多个协议,表示这个动态对象必须遵循多个协议。

15、协议也可以像类一样进行扩展,方法即是在声明一个新的协议的时候,使用尖括号让它必须遵循旧的某个协议,效果其实就是旧协议的扩展。如下:

@protocol NewProtoc <XProtoc>

那么任何采用NewProtoc协议的类都必须要同时遵循NewProtoc协议和XProtoc协议。

16、分类也可以采用某项协议,如下:

 @interface XClass (CateClass) <NewProtoc>

17、如果你不想公开在h文件中声明你的类遵循的协议,那么你也可以把采用某些协议的语句(@interface语句,参照扩展类)写在m文件里,如下:

@interface XClass () < NewProtoc >

...

@end

18、定义了协议的类可以看做是把协议里定义的方法代理(delegate)给了实现它们的类。Cocoa和iOS非常依赖代理这个概念。

19、关于合成对象

有时子类继承了父类之后,同时也继承了一些子类不需要或者有冲突的方法。比如Square类继承自Rectangle类,也继承了Rectangle类设置长和宽的方法,但是对于Square类来说只需要设置一个边长,继承来的设置长和宽的方法并不适用,如果有对象不小心调用了父类继承来的这个设置长和宽的方法,反而会出现错误。

那么这时可以采取另一种处理方法:定义一个新类,然后不要让这个新类继承旧类,而是在这个新类中包含一个或多个它本来要继承的旧类的对象。

这种新类的对象就是合成对象,因为它是由其他对象组成的。比如以下的例子:

@interface Square: NSObject {

  Rectangle *rect;

}

-(int) setSide: (int) s;

...

@end

注意这个Square类就不是Rectangle类的子类了,子类的对象是可以直接访问父类的方法的,而在这个例子中,Square类的对象就没法直接访问Rectangle类的方法了。

这时要通过Square类的对象要访问Rectangle类的方法就必须受到Square类的限制了,比如假设Rectangle类有一个方法-(int) area,那么在Square类里面可以这么定义:

-(int) area {

return [rect area];

}

那么要访问Rectangle类的方法就只能通过Square类提供的area方法,实现了监控。但是要注意在合成对象的初始化的时候记得也要为这个内部的对象rect做初始化。