2012-03-14 57 views
18

比方说,我有classA财产A和财产BclassB,我想classAB有两个属性AB。我仍然不知道如何使这一切与组成工作。班级作文在iOS中如何工作?

我意识到这可以通过继承完成,但我想学习如何使用组合来做到这一点。我已经看过例子,但是我仍然不明白它是如何工作的。

回答

20

您创建了一个新的类,它具有classA和classB的实例作为成员变量。然后通过传递get/set方法来实现这些属性。

@interface ClassAB 
{ 
    ClassA *objectA; 
    ClassB *objectB; 
} 
@property (nonatomic,strong) id propertyA; 
@property (nonatomic,strong) id propertyB; 
@end 

@implementation ClassAB 
- (id)propertyA { return objectA.propertyA; } 
- (void)setPropertyA:(id)value { objectA.propertyA = value; } 
- (id)propertyB { return objectB.propertyB; } 
- (void)setPropertyB:(id)value { objectB.propertyB = value; } 
@end 

而这就是组成。有些语言有特殊的语法来做这件事(例如,在Ruby中,你可以包含一组来自一个类/模块的方法),但Objective-C不允许这样做。

有一件事你可以在Objective-C中做的是catch消息发送到你的对象没有关联的方法,并将它们转发给另一个对象。如果您正在编写将构成另一个类的类,或者有许多不同的消息需要转发并且您不想手动将其全部写出,则此技巧非常有用。

使用消息转发的缺点是您放弃了一些控制,并且可能很难预测消息何时由您的类或转发类处理。例如,如果一个超类实现了一个方法,那么该方法将被执行,并且转发代码将不会被调用。

+0

@杰克逊在接受这个答案之前,让我写一些与KVO相关的东西,让您以更优雅的方式做到这一点。 – 2012-03-14 21:26:50

+2

我相信这是更好的可读性解决方案。理查兹解决方案是一个巧妙的技巧,但我总是把实用主义和可读性放在棘手的代码上。风格的问题我猜 – Slappy 2012-03-14 22:04:22

+0

@Slappy我曾经使用过本质上是Richard的方法 - 当我有一些我不能修改的代码时,我必须通过一个'NSString'(这是一个类集群,所以对它进行子类化并不是微不足道的),我希望通信边带。运行时的关联对象是考虑的主要替代方法,但我认为'forwardingTargetForSelector:'(尽管创建假装子类而不是在我的情况下构成)是更清晰和更可读的解决方案。 – Tommy 2012-03-14 23:01:18

8

假设ClassAClassB按照你说的那样实现,这很好地工作,并且很容易扩展。

@interface ClassAB : NSObject 

@property int a; 
@property int b; 

@property ClassA *aObject; 
@property ClassB *bObject; 

@end 

@implementation ClassAB 

@dynamic a, b; 
@synthesize aObject, bObject; 

-(id) forwardingTargetForSelector:(SEL)aSelector 
{ 
    if ([aObject respondsToSelector:aSelector]) 
     return aObject; 
    else if ([bObject respondsToSelector:aSelector]) 
     return bObject; 

    return nil;  
} 

@end 

int main(int argc, const char * argv[]) 
{ 
    @autoreleasepool { 
     ClassA *a = [ClassA new]; 
     ClassB *b = [ClassB new]; 

     ClassAB *ab = [ClassAB new]; 
     ab.aObject = a; 
     ab.bObject = b; 

     ab.a = 10; 
     ab.b = 20; 

     NSLog(@"%i, %i", a.a, b.b); // outputs 10, 20 
    } 
    return 0; 
} 
+0

哦,理查德J.罗斯三世,你是如此经常在我这么做之前张贴我的想法?通常我会及时注意到避免点击按钮...可能值得明确指出的是,如果您使用'ab'键入'ClassA',那么对于所有定义的方法而言,它的行为完全像'ClassA'的实例和属性,如果你把它转换成'ClassB',那么对于所有定义的方法和属性,它们的行为都不会同样由'ClassA'定义? – Tommy 2012-03-14 21:37:35

+0

@Tommy它一定是我的忍者感觉:) – 2012-03-14 21:40:02

+0

@Tommy实际上是不正确的,如果'ClassAB'实现了'ClassA'的选择器,在这种情况下'ClassAB'实现将被调用(例如,一个自定义'-description'实现)。 – 2012-03-14 21:41:56