首页 Objective-C 从 C++ 到 Objective-C(8):继承(续)

从 C++ 到 Objective-C(8):继承(续)

5 6

Protocol对象

运行时,协议就像是类对象,其类型是Protocol *。例如,conformsToProtocol:方法就需要接受一个Protocol *类型的参数。@protocol关键字不仅用于声明协议,还可以用于根据协议名返回Protocol *对象。

Protocol* myProtocol = @protocol(协议名)

远程对象的消息传递

由于 Objective-C 的动态机制,远程对象之间的消息传递变得很简单。所谓远程对象,是指两个或多个处于不同程序,甚至不同机器,但是可以通过代理完成同一任务,或者交换信息的对象。正式协议就是一种可以确保对象提供了这种服务的有效手段。正式协议还提供了很多额外的关键字,可以更好的说明各种参数。这些关键字分别是in,out,inout,bycopy,byrefoneway。这些关键字仅对远程对象有效,并且仅可以在协议中使用。出了协议,它们就不被认为是关键字。这些关键字被插入到在协议中声明的方法原型之中,提供它们所修饰的参数的额外信息。它们可以告知,哪些是输入参数,哪些是输出参数,哪些使用复制传值,哪些使用引用传值,方法是否是同步的等等。以下是详细说明:

  • in:参数是输入参数;
  • out:参数是输出参数;
  • inout:参数即是输入参数,又是输出参数;
  • bycopy:复制传值;
  • byref:引用传值;
  • oneway:方法是异步的,也就是函数调用会立即返回(否则的话,调用者会一直堵塞,直到被调用函数执行完毕,即使被调用者返回值是void,也同样会被阻塞)。它的返回值必须是void(其它返回值是没有意义的,因为被调用函数是立即返回,必然无法得到正确的返回值)。

例如,下面就是一个返回对象的异步方法:

-(oneway void) giveMeAnObjectWhenAvailable:(bycopy out id *)anObject;

默认情况下,参数都被认为是inout的。如果参数由const修饰,则被当做in参数。为参数选定是in还是out,可以作为一种优化手段。参数默认都是传引用的,方法都是同步的(也就是不加oneway)。对于传值的参数,也就是非指针类型的,outinout都是没有意义的,只有in是正确的选择。

分类

创建类的分类 categories,可以将一个很大的类分割成若干小部分。每个分类都是类的一部分,一个类可以使用任意多个分类,但都不可以添加实例数据。分类的好处是:

  • 对于精益求精的开发者,分类提供了一种划分方法的机制。对于一个很大的类,它可以将其划分成不同的角色;
  • 分类允许分开编译,也就是说,同一个类也可以进行多人的分工合作;
  • 如果把分类的声明放在实现文件(.m)中,那么这个分类就只在文件作用域中可见(虽然这并没有调用上的限制,如果你知道方法原型,依然可以调用)。这样的分类可以取一个合适的名字,比如FooPrivateAPI
  • 一个类可以在不同程序中有不同的扩展,而不需要丢弃通用代码。所有的类都可以被扩展,甚至是 Cocoa 中的类。

最后一点尤其重要。很多开发人员都希望标准类能够提供一些对他们而言很有用的方法。这并不是一个很困难的问题,使用继承即可实现。但是,在单继承的环境下,这会造成出现很多的子类。仅仅为了一个方法就去继承显得有些得不偿失。分类就可以很好的解决这个问题:

C++

class MyString : public string
{
public:
    // 统计元音的数目
    int vowelCount(void);
};

int MyString::vowelCount(void)
{
...
}

Objective-C

@interface NSString ()
// 注意并没有使用 {}
-(int) myPrivateMethod;
@end

@implementation NSString ()
-(int) myPrivateMethod
{
...
}
@end

在 C++ 中,这是一个全新的类,可以自由使用。

在 Objective-C 中,NSString是 Cocoa 框架的一个标准类。它是使用分类机制进行的扩展,只能在当前程序中使用。注意此时并没有新增加类。每一个NSString对象都可以从这个扩展获得统计元音数目的能力,甚至常量字符串也可以。同时注意,分类不能增加实例数据,因此没有花括号块。

分类也可以使匿名的,更适合于 private 的实现:

@interface NSString ()
// 注意并没有使用 {}
-(int) myPrivateMethod;
@end

@implementation NSString ()
-(int) myPrivateMethod
{
...
}
@end

混合使用协议、分类和子类

混合使用协议、分类和子类的唯一限制在于,你不能同时声明子类和分类。不过,你可以使用两步来绕过这一限制:

@interface Foo1 : SuperClass  //ok
@end

@interface Foo2 (Category)   //ok
@end

// 下面代码会有编译错误
@interface Foo3 (Category)  : SuperClass
@end

// 一种解决方案
@interface Foo3 : SuperClass  // 第一步
@end

@interface Foo3 (Category) // 第二步
@end

5 评论

willonboy's blog 2011年4月1日 - 13:57

看的真过瘾!

回复
hewig 2011年10月17日 - 15:31

“oneway:方法是异步的,也就是不会立即返回”疑似有误,异步方法应该是立即返回吧

回复
DevBean 2011年10月18日 - 08:56

谢谢指出错误,原文已经更正。

回复
marx 2012年9月27日 - 17:03

->bycopy:复制传值
->对于传值的参数,也就是非指针类型的,out 和 inout 都是没有意义的
根据前面两句,下面的示例代码 bycopy和out同时出现,是不是不大合适?
-(oneway void) giveMeAnObjectWhenAvailable:(bycopy out id *)anObject;

回复
DevBean 2012年9月28日 - 07:23

bycopy 和 out 是可以一起使用的。bycopy 复制传值含义是通过复制的方式接收对象;下文“对于传值的参数”,意思是非指针类型。二者的“传值”不是一个概念。id * 显然是一个指针类型,所以可以使用 bycopy 修饰。

回复

发表评论

关于我

devbean

devbean

豆子,生于山东,定居南京。毕业于山东大学软件工程专业。软件工程师,主要关注于 Qt、Angular 等界面技术。

主题 Salodad 由 PenciDesign 提供 | 静态文件存储由又拍云存储提供 | 苏ICP备13027999号-2