使用父的init - 从另一个类创建类的子类

时间:2013-10-20 12:11:24

标签: objective-c initialization

编辑:这个downvoting syndrom在这里吸引了大量时间。我问了一个问题,我认为我做了我的作业,并征求意见。上调的答案意味着编写时间警告,而我自己的,可能是最干净的OOP方式没有任何兴趣。

简要概述,以便了解我为什么需要这个以及我尝试做什么:我正在编写一个实现数据映射器模式的ORM。映射器(即用于SQLite结果)必须使用基本实体类的初始化器创建实体类的子类。所以有问题。

映射器不会,也不应该知道特定的类。不同数据源的映射描述和特定映射器都是从实体类中抽象出来的,并且是实体描述的设计部分。

实体类似于NSManagedObject,但ORM遵循不同的模式。创建任何实体的描述类似于NSEntityDescription(但也遵循不同的模式和目的)。

所以我的目标是使用ManagedEntity的init方法创建我知道是ManagedEntity子类的实体。

所以我的mapper的init看起来像这样:

- (id)initWithEntityClass:(Class)EntityClass entityDescriptor:(EntityDescription*)entityDescriptor
{
self = [super init];
if (self)
{
    _EntityClass = EntityClass; 
    _entityDescription = entityDescription;

    ... (assert that class is of subclass of ManagedEntity)
}

稍后在我的映射器中,我想创建具体的实体:

-(void)createEntityWithSQLiteResultSet:(sqlite3_stmt*)resultSet
{
    // Problem: How to init a class known to be a subclass of ManagedEntity?  
    ManagedEntity *newEntity = [[_EntityClass] alloc]     initWithEntityDescription:_entityDescription];
}

那么如何使用ManagedEntity的init创建ManagedEntity的子类?

当然,我可以使用respondsToSelector()来执行initWithEntityDescription并调用它。但是有些东西告诉我应该有一种更优雅的方式,类已经知道了。此外,respondsToSelector和selector调用仅执行运行时检查。即使实体初始化程序不应该更改,但如果此方法存在,则丢失编译时检查似乎是一个糟糕的选择。

2 个答案:

答案 0 :(得分:5)

作为映射的一部分,您必须知道所需的子类。然后使用

ManagedEntity *newEntity = [[NSClassFromString(className) alloc] initWithEntityDescription:_entityDescription];

编辑: 正如我所承诺的那样,我正在GitHub项目中构建它,并意识到为什么它可能无法编译。您必须在范围内可访问的已知类中声明-initWithEntityDescription:。在这种情况下,这意味着您必须声明并实现ManagedEntity -initWithEntityDescription:并且在文件顶部有“#import”ManagedEntity.h“。

答案 1 :(得分:0)

强化Neal的正确答案,即OP声称无法工作,因为他知道objC:)

#import <CoreData/CoreData.h>

@interface TestMapper : NSObject

- (NSManagedObject*)createClassForEntity:(NSEntityDescription*)entity context:(NSManagedObjectContext*)ctx;

@end

#import "TestMapper.h"

@implementation TestMapper

- (NSDictionary*)entityToClassMap {
    return nil; //TODO ;)
}

- (NSManagedObject*)createClassForEntity:(NSEntityDescription*)entity context:(NSManagedObjectContext*)ctx {
    NSString *className = self.entityToClassMap[entity.name];
    assert(className);

    return [[NSClassFromString(className) alloc] initWithEntity:entity insertIntoManagedObjectContext:ctx];
}

@end

替代使用您想要的运行时

id cls = NSClassFromString(className);
id alloced_cls = objc_msgSend(cls, @selector(alloc));
id newEntity = objc_msgSend(alloced_cls, @selector(initWithEntity:insertIntoManagedObjectContext:), entity, ctx);
return newEntity;