如何从其超类的实例中分配NSObject子类实例?

时间:2013-12-02 22:55:29

标签: objective-c cocoa inheritance subclass nsobject

给定一个类结构,如......

@interface SuperClassView : NSView                              @end
@interface SubClassedView : SuperClassView @property int aProp; @end

如何从SubClassedView实例实例化SuperClassView

假设一个方法返回超类SuperView的实例....

SuperClassView *superInstance = [ViewFactory makeSuperClassView];

但我希望获取 子类 SubClassedView的实例?不可能简单地“施放”它......

SubClassedView *subClsInstance = (SubClassedView*)[ViewFactory makeSuperClassView];

并且没有像

这样的NSObject方法的内置(或容易想象的实现)
self = [super initWithInstance:[superInstance copy]];`

是将超类实例的所需属性复制到新实例化的子类对象的唯一方法,例如......

SubClassedView *subClsInstance = SubClassedView.new;
for (NSString* someKey in @["frame",@"color",@"someOtherProperty])
   [subClsInstance setValue:[superInstance valueForKey:someKey] forKey:someKey];

或者将(“em> swizzle at runtime ”)子类'“其他属性方法”(在本例中为setAProp:aProp)添加到超类实例(并且还将其强制转换)向上)...

SubClassedView *subClsInstance = (SubClassedView*)[ViewFactory makeSuperClassView];
[subClsInstance addSwizzleMethod:@selector(setAProp:) viaSomeMagic:....];
[subClsInstance addSwizzleMethod:@selector(aProp)     viaSomeMagic:....];

希望这是一个简单的运行时技巧,我根本就不知道......不是一个悲伤的迹象,我不幸地试图通过一些<{1}}欺骗ObjC进行多重继承em>令人尴尬的反模式。无论哪种方式,想法?

编辑:等待@BryanChen发表评论作为答案,这可以通过他建议的运行时功能轻松实现,或者作为NSObject上的类别实现...

@implementation NSObject (SettingClass)
- (void)setClass:(Class)kls { if (kls) object_setClass(self, kls); } @end

3 个答案:

答案 0 :(得分:6)

你要做的事情非常非惯用......感觉你正在尝试做一些像基于原型的OOP。几点快点:

  1. 不要做调酒。你不能沉溺于一个实例上,你就会沉迷于类定义,所以如果你这样做,就不会将子类方法添加到超类的“an”实例上,你将它们添加到超类的所有实例上
  2. 是的,如果你想这样做,你只需要将你想要的属性从super复制到子类的新实例中。
  3. 您可以在超类中使用一个返回子类的工厂方法,并将所有复制封装在那里(因此,-[SuperClassView makeSubclassView]返回SubClassedView *。这实际上相对常见,是如何实现了许多类集群(尽管它们返回符合超类实现的私有子类)

答案 1 :(得分:1)

object_setClass不是您正在寻找的机器人。

是的,它会更改实例的。但是,它不会更改它的大小。因此,如果您的SubClassView声明了未在SuperClassView上声明的额外属性或实例变量,那么您在此frankenstein实例上访问它们的尝试将导致(最多)缓冲区溢出,(可能)损坏的数据,(最糟糕的是)你的应用程序崩溃。

听起来你真的只想在工厂方法中使用self

+ (instancetype)makeView {
    return [[self alloc] init];
}

然后,如果您致电[SuperClassView makeView],则会返回SuperClassView的实例。如果您致电[SubClassView makeView],则会返回SubClassView的实例。

“但是,”你说,“如果它是SubClassView,我如何自定义视图的属性?”

就像你对其他任何东西一样:你覆盖SubClassView上的方法:

@implementation SubClassView

+ (instancetype)makeView {
    SubClassView *v = [super makeView];
    v.answer = 42;
    return v;
}

@end

答案 2 :(得分:0)

object_setClass可能是也可能不是您正在寻找的“运行时技巧”。它确实是一个在运行时更改实例类的混合体。然而,它确实有许多约束,例如新类不能有额外的ivars。您可以查看this question了解详情。

我认为更好的方法是,不要使用[ViewFactory makeSuperClassView]制作视图,而是将其设为[[SuperClassView alloc] initWithSomething]。然后你可以[[SubClassView alloc] initWithSomething]

或者如果您真的想使用ViewFactory,请将其设为[ViewFactory makeViewOfClass:]