在Ruby中,有模块,您可以通过“混入”模块来扩展类。
module MyModule
def printone
print "one"
end
end
class MyClass
include MyModule
end
theOne = MyClass.new
theOne.printone
>> one
在Objective-C中,我发现我有一组常用的方法,我想要一些Class来“继承”。如果不创建一个公共类并从该公共类派生所有其他方法,我可以通过什么方式实现这一目标?
答案 0 :(得分:31)
无耻的插件:ObjectiveMixin
它利用了Objective-C运行时在运行时向类添加方法的功能(与类别相反,只是编译时)。看看它,它的效果非常好,并且与Ruby的mixins类似。
答案 1 :(得分:28)
修改:添加了更改,因为有些人认为我对Objective-C的限制负责。
简短回答:你不能。 Objective-C没有相应的Ruby mixins。
稍微不那么简短的回答:Objective-C确实有一些可以说具有相同风味的东西:协议。协议(一些其他语言中的接口)是一种定义一组方法的方法,一个采用该协议的类承诺实现。但协议不提供实现。这种限制阻止使用协议作为Ruby mixins的完全等价物。
更简短的答案但是,Objective-C运行时具有一个公开的API,可让您使用该语言的动态功能。然后你走出语言,但你可以拥有默认实现的协议(也称为具体协议)。弗拉基米尔的答案显示了一种方法。在这一点上,在我看来你得到Ruby mixins就好了。
但是,我不确定我会建议那样做。在大多数情况下,其他模式符合条件,而无需使用运行时游戏。例如,您可以拥有一个实现混合方法的子对象( has -a 而不是 is-a )。使用运行时是可以的,但有两个缺点:
您的代码可读性较差,因为它要求读者了解的语言远远多于语言。当然你可以(并且应该)对它进行评论,但请记住,任何必要的评论都可以被视为实施缺陷。
您依赖 语言的实现。当然,Apple平台是迄今为止最常见的Objective-C平台,但不要忘记Cocotron或GnuStep(或Etoilé)具有不同的运行时间,在这方面可能与Apple兼容,也可能不兼容。
作为旁注,我在下面说明类别不能将状态(实例变量)添加到类中。通过使用运行时API,您也可以解除该限制。然而,这超出了这个答案的范围。
答案很长:
两个Objective-C功能看起来像是可能的候选者:类别和协议。如果我正确理解这个问题,那么类别在这里并不是正确的选择。正确的功能是协议。
让我举个例子。假设您希望一堆类具有称为“唱歌”的特定能力。然后定义协议:
@protocol Singer
- (void) sing;
@end
现在您可以声明您自己的任何类通过以下方式采用协议:
@interface Rectangle : Shape <Singer> {
<snip>
@end
@interface Car : Vehicle <Singer> {
<snip>
@end
通过声明他们采用协议,他们承诺实施sing
方法。例如:
@implementation Rectangle
- (void) sing {
[self flashInBrightColors];
}
@end
@implementation Car
- (void) sing {
[self honk];
}
@end
然后你使用这些类,例如:
void choral(NSArray *choir) // the choir holds any kind of singer
{
id<Singer> aSinger;
for (aSinger in choir) {
[aSinger sing];
}
}
请注意,数组中的歌手不需要具有公共超类。另请注意,一个类只能有一个超类,但许多采用的协议。最后注意,类型检查由编译器完成。
实际上,协议机制是用于mixin模式的多重继承。由于协议无法向类中添加新的实例变量,因此多重继承受到严重限制。协议仅描述了采用者必须实现的公共接口。与Ruby模块不同,它不包含实现。
这是最重要的。但是,让我们提一下类别。
声明类别不在尖括号中,而是在括号之间声明。不同之处在于可以为现有类定义类别以扩展它,而无需对其进行子类化。您甚至可以为系统类执行此操作。可以想象,可以使用类别来实现类似于mixin的东西。并且它们通常以这种方式使用,通常作为NSObject
(继承层次结构的典型根)的类别,在某种程度上被称为“非正式”协议。
这是非正式的,因为1-没有类型检查由编译器完成,并且2-实现协议方法是可选的。
今天没有必要使用类别作为协议,特别是因为正式协议现在可以声明它们的某些方法对于关键字@optional
是可选的,或者是@required
所需的(默认)。
类别对于向现有类添加某些特定于域的行为仍然很有用。 NSString
是此的共同目标。
有趣的是指出大多数(如果不是全部)NSObject
设施实际上是在NSObject
协议中声明的。这意味着使用NSObject
作为所有类的公共超类并不是真的很有吸引力,尽管这仍然是出于历史原因通常做的,而且......因为这样做没有任何缺点。但是某些系统类(例如NSProxy
)不 NSObject
。
答案 2 :(得分:11)
你可以使用#include从字面上混合代码。这是不可取的,并且是针对Objective-c中的所有宗教,但是效果很好。
请不要在生产代码中执行此操作。
例如在文件中:
MixinModule.header (不应编译或复制到目标)
-(void)hello;
MixinModule.body (不应编译或复制到目标)
-(void)hello{
NSLog(@"Hello");
}
在mixin类中:
@interface MixinTest : NSObject
#include "MixinModule.header"
@end
@implementation MixinTest
#include "MixinModule.body"
@end
使用案例
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]){
@autoreleasepool {
[[[MixinTest new] autorelease] hello];
}
return 0;
}
请不要在生产代码中执行此操作。
答案 3 :(得分:1)
这是我在Objective-C中实现Mixins的看法,而不直接使用Objective-C运行时。也许这对某人有帮助:https://stackoverflow.com/a/19661059/171933