[objective-c] Interface & Protocol & Categories
1 Interface(接口约定)
Interface理解为接口概念,于JAVA\C#中的接口有所不同的是Obj-C中的接口应该理解为一种非正式协议,Obj-C中的Interface只是表明应该会处理其中声明的消息(方法),但是不是必须处理。
表明Sample Interface中,约定了应该会处理HelloWorld消息,但是Sample.m中我们可以不去实现对HelloWorld的处理,这样编译器会给出警告但是编译可以通过,但是在程序中向Sample发送该消息会引发异常。
2 Protocol(正式协议)
Protocol(正式协议)看上去更正规一些,语义上更强烈一些:要求采用该协议的类,"必须"实现协议中约定的方法。但是比较娱乐的是,即使是号称正式协议,编译器在编译时,遇到不守规矩的情况,仍然只是给出警告。你会觉得Protocol跟Interface比起来,都是类似的概念,Protocol设计纯属多余。其实不然,Protocol存在的一个重要意义在于:
Protocol(正式协议)可以将业务中的方法定义剥离出来,形成一个单独的文件,这跟传统OO中的提取接口是不谋而合的。如果遇到二个系统需要交换数据,可以制定一套双方都遵守的Protocol,然后这二个系统中都把这个协议文件添加到项目中,实现它即可。
这一功能,非正式协议(@interface)是做不到的。因为在Obj-C中是不允许多继承的,双方系统可能都需要继承某一个类以实现某些功能,这时候只能实现一个协议(Protocol)。 此外,Obj-C 2.0中对协议还做了一些扩展,允许把协议中的方法标识为“必须实现(@required)”和“可选实现(@optional)”二类,如果协议中的方法被标识为@optional,即使采用该协议的类不实现这些方法,编译器也不会给出警告。这样接口约定的很多作用就可以由协议来实现,不过协议中不能含有数据(但是接口约定中可以有数据成员)。
NSObject是一个Interface也是一个Protocol,而Interface NSObject实现了Protocol NSObject,在Obj-c的OO世界中,身为万物之祖的NSObject其实也就一个"正式协议”,所以从NSObject派生出的所有类,都只是在遵守一个或多个协议而已。
3 Categories (类型分类,非正式协议)
非正式协议并没有设计为正式协议的宽松版本,而是设计为Class Categories(类型分类)。Categories是Objective-C里面最常用到的功能之一。大体上说,Categories可以让我们给已经存在的类增加方法,而不需要增加一个子类。而且不需要知道它内部具体的实现。如果我们想增加某个framework自带的类的方法,这非常有效。如果我们想在我们程序工程的NSString能够增加一个方法,我们就可以使用Categories,不需要自己实现一个NSString的子类。比如,我们想在NSString里面增加一个方法来判断它是否是一个URL,那我们就可以这么做:
1) 在categoriesTest.h头文件中:
1: @interface NSString (URLDetecter)
2: - (BOOL) isURL;
3: @end
这跟类的定义非常类似。区别就是category没有父类,而且在括号里面要有category的名字。名字可以随便取,但是习惯叫法会让人比较明白category里面有些什么功能的方法。这里是具体的实现。但是要注意,这本身并不是一个判断URL很好的实现。我们主要是为了整体的了解category的概念。
2) 在categoriesTest.m中给出实现
1: #import "categoriesTest.h"
2: @implementation NSString (URLDetecter)
3: - (BOOL) isURL{
4: if ( [self hasPrefix:@"http://"] )
5: return YES;
6: else
7: return NO;
8: }@end
1: NSString* string1 = @"http://www.CocoaDev.cn/";
2: NSString* string2 = @"Pixar";
3: if ([string1 isURL]) NSLog(@"string1 is a URL");
4: if([string2 isURL]) NSLog(@"string2 is a URL");
4) 如果在main.m没有import头文件categoriesTest.h,会有如下警告
warning: 'NSString' may not respond to '-isURL'
warning: (Messages without a matching method signature
warning: will be assumed to return 'id' and accept
warning: '...' as arguments.
编译器认为NSString不会对消息isURL作出回应。
此时运行得到的结果如下:
5) 如果在main.m中import头文件categoriesTest.h
运行结果如下:
跟子类不一样,category不能增加成员变量。我们还可以用category来重写类原先的存在的方法,但是这需要非常非常小心。记住,当我们通过category来修改一个类的时候,它对应用程序里的这个类所有对象都起作用。