将Objective-C中的超类强制转换为子类

3

我有2个类:

  • NWStorePackage(NW商店包)

  • NWStorePackageConsumable(可消费的NW商店包)

NWStorePackageConsumable是NWStorePackage的子类(用于不可消耗内容设计)。

最近我将NWStorePackageConsumable添加到项目中。在整个代码中,我都使用NWStorePackage。

我有一个类构造函数来创建正确的实例。

+ (id) storePackageFromStorePackageDictionary: (NSDictionary *) dictionary

根据字典内容,此函数返回一个NWStorePackageConsumable或者NWStorePackage。

当某些操作需要相关消耗品时,我会检查包类型(在NWStorePackage中定义的枚举)。如果该类型设置为可消耗品产品类型,我将将NWStorePackage实例转换为NWStorePackageConsumable实例并调用下面的方法。我想这种类型转换不会成为问题,因为我之前已经正确创建了它们。

- (id) updateConsumableCount: (int) increase;

这会生成以下错误信息:
-[NWStorePackage consumableCount]: unrecognized selector sent to instance 0x9c34380

这里是完整的代码,其中调用了该方法。
for(NWStorePackage *storePackage in _storePackagesArray) {
    if ([storePackage purchased]) {
        NWStorePackageStorage *sps = [NWStorePackageStorage storePackageStorageWithProductIdentifier:storePackage.productIdentifier andIsPurchased:storePackage.purchased];

        if ([storePackage packageType] == StorePackageTypeConsumable) {
            NWStorePackageConsumable *consumable = (NWStorePackageConsumable *) storePackage;
            [sps setConsumableCount: [consumable consumableCount]];
        }
        [purchases addObject:sps];
    }
}

以下是NWStorePackage的构造函数,我没有在NWStorePackageConsumable中进行重写。
+ (id) initWithContentsOfDictionary: (NSDictionary *) dictionary {
NSLog(@"Initializing NWStorePackage with dictionary content", kLOGLEVEL_STORE);

for (id key in dictionary) {
    NSLog(@"Key: %@ :: Value: %@", kLOGLEVEL_STORE, key, [dictionary objectForKey: key]);
}
NWStorePackage *package = [[NWStorePackage alloc] init];
[package setPackageID:[(NSString *)[dictionary objectForKey:@"id"] integerValue]];
[package setTitle:[dictionary objectForKey:@"title"]];
[package setProductIdentifier:[dictionary objectForKey:@"productIdentifier"]];
[package setDescriptionLong:[dictionary objectForKey:@"descriptionLong"]];
[package setDescriptionShort:[dictionary objectForKey:@"descriptionShort"]];
[package setPackageType: (StorePackageType) [[dictionary valueForKey:@"type"] longValue]];

return package;

}


似乎packageType设置不正确?即您正在将其设置为StorePackageTypeConsumableNWStorePackage实例上。您在工厂方法中传递给*-alloc*的是哪个类? - nielsbot
1
为什么要使用 packageType?直接使用 [storePackage isKindOfClass:[NWStorePackageConsumable class]] 不就可以了吗? - Bryan Chen
bryan:因为我需要从一个字典开始了解它的类型。不管怎样,我突然意识到了从nielsbot的评论中发现的问题所在。类构造函数仍然生成错误的类型。我将其删除并更改为真正的构造函数。感谢你激活我的头脑 :) - tomvda
“未识别的选择器”总是正确的。当它说它被“发送”了一个NWStorePackage时,它确实被发送了一个NWStorePackage。你的任务通常是找出为什么该类不是预期类型。 - Hot Licks
2个回答

1
通常情况下,您应该尝试使用多态来替换'instanceof'逻辑。因此,不是:
if (myObject isKindOfClass:[NWStorePackageConsumable class])
   //doSomething
else if //etc

尝试识别两个类共同拥有的抽象方法,并在每个子类中创建一个具体实现。例如:

[storePackage deliver]; //This will be different depending on consumable or not. 

如果您无法做到这一点,那么可能表明这两个类实际上不应该共享一个公共祖先。
抽象基类与协议与类簇:
- 当您有一个公共框架时,其中某些细节将由子类处理时,请使用抽象基类。 - 当您想创建一个公共契约,但实例之间的实现将非常不同时,请使用协议。 - 在您使用抽象基类的情况下,使用类簇(抽象工厂模式的变体),当您还希望该公共类包含决定在幕后返回哪个具体实例的逻辑时,请使用类簇。
调试问题:
您是否正在尝试将基类的实例强制转换为子类?这是不可能的(在正常情况下 - 您可以进行一些isa指针交换,但不建议这样做)。您需要预先决定要求出什么实例。基类的目的是向消费者呈现一个公共接口,消费者不必关心背后发生了什么以符合该接口。
使用调试器检查您的实例实际上是否是子类的实例。

0

将类的构造函数更改为以下内容

- (id) initWithContentsOfDictionary: (NSDictionary *) dictionary {
self = [super init];

if (self) {
    NSLog(@"Initializing NWStorePackage with dictionary content", kLOGLEVEL_STORE);

    for (id key in dictionary) {
        NSLog(@"Key: %@ :: Value: %@", kLOGLEVEL_STORE, key, [dictionary objectForKey: key]);
    }

    [self setPackageID:[(NSString *)[dictionary objectForKey:@"id"] integerValue]];
    [self setTitle:[dictionary objectForKey:@"title"]];
    [self setProductIdentifier:[dictionary objectForKey:@"productIdentifier"]];
    [self setDescriptionLong:[dictionary objectForKey:@"descriptionLong"]];
    [self setDescriptionShort:[dictionary objectForKey:@"descriptionShort"]];
    [self setPackageType: (StorePackageType) [[dictionary valueForKey:@"type"] longValue]];
}

return self;

}

并提供了方便的方法来实现此操作


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接