【问题标题】:How does class composition work in iOS?类组合在 iOS 中是如何工作的?
【发布时间】:2012-03-31 09:51:44
【问题描述】:

假设我在classA 上拥有A 属性,在classB 上拥有B 属性,我希望classAB 拥有AB 这两个属性。我仍然不明白如何使这一切与 composition 一起工作。

我意识到这可以通过继承来完成,但我想学习如何通过 composition 来做到这一点。我查看了示例,但仍然不明白它是如何工作的。

【问题讨论】:

    标签: objective-c ios composition


    【解决方案1】:

    您创建了一个新类,其中有 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 中可以做的一件事是捕获发送到您的对象但没有关联方法的消息,并将它们转发给另一个对象。如果您正在编写一个将伪装成另一个类的类,或者如果有许多不同的消息要转发并且您不想手动将它们全部写出来,则此技巧很有用。

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

    【讨论】:

    • @Jackson 在你接受这个答案之前,让我用 KVO 写一些东西,让你以更优雅的方式做到这一点。
    • 我相信这是更好的可读性解决方案。理查兹的解决方案是一个巧妙的技巧,但我总是将实用主义和可读性置于棘手的代码之上。我猜是风格问题
    • @Slappy 我只在一个场合使用了基本上是 Richard 的方法——当我有无法修改的现有代码时,我必须通过 NSString(这是一个类集群和子类化它不是微不足道的)我想要一个边带的通信。运行时的关联对象是考虑的主要替代方法,但我觉得 forwardingTargetForSelector:(尽管在我的情况下创建一个假装的子类而不是组合)是更清洁和更易读的解决方案。
    • 这是一个很好的答案,但为了完整起见,我要补充三个小点,尤其是对于新手:1) 确保要在 ClassA 和 ClassB 中访问的属性在头文件中公开,并且2)您必须在 ViewDidLoad 或 init 例程中的 ClassAB 中分配 init ClassA 和 ClassB,以及 3)您必须将“ClassA.h”和“ClassB.h”#import “ClassA.h”和“ClassB.h”到 ClassAB.m。
    【解决方案2】:

    假设 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;
    }
    

    【讨论】:

    • 哦,Richard J. Ross III,你怎么经常在我发表想法之前发表我的想法?通常我会及时注意到以避免按下按钮...可能值得明确指出,如果您将ab 转换为类型ClassA,那么对于所有定义的方法,它的行为就像ClassA 的真实实例一样和属性,如果您将其转换为ClassB,它对于所有未由ClassA 定义的方法和属性的行为都会相同?
    • @Tommy 这一定是我的忍者感官 :)
    • @Tommy 如果ClassAB 实现了ClassA 也可以执行的选择器,那么@Tommy 实际上是不正确的,在这种情况下将调用ClassAB 实现(例如,自定义-description 实现)。
    • 是的,在这些情况下不正确,但对于 ab 完全正确,如您的示例所示。
    • @Tommy 我不确定我是否理解你们在说什么,但 Objective-C 仅使用对象类型进行编译时检查。将指针从一种类型转换为另一种类型不会影响对象的行为方式;该信息在运行时会丢失。
    猜你喜欢
    • 2017-11-08
    • 1970-01-01
    • 2016-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多