【问题标题】:How to perform an action after init of all sub classes?如何在所有子类初始化后执行操作?
【发布时间】:2012-06-16 17:35:32
【问题描述】:

我有一组 Objective-C 类,它们被各种不同的类在不同的深度进行子分类。初始化整个对象后(所有子类的 init 函数都已完成),我需要运行“更新缓存”方法,然后根据需要被子类覆盖。

我的问题: 我的类树有各种不同的继承深度,没有一个地方可以放[self UpdateCache],而且我可以确定没有没有被初始化的子类。唯一可能的解决方案是在每个类初始化之后调用 [super init],以便始终最后调用父类。我想避免这种情况,因为这违反了编写 Objective-C 的所有准则。这个问题有什么干净的解决方案吗?

这是一个示例代码:

@interface ClassA : NSObject

-(void)UpdateCache
@end

@interface ClassB : ClassA

-(void)UpdateCache
@end

@interface ClassC : ClassB

-(void)UpdateCache
@end

现在对于实现,我们需要在知道所有子类都已初始化后以某种方式调用 UpdateCahce无论哪个类已被初始化

@implementation A
-(id)init
{
   if(self = [super init])
   {
       // Placing [self UpdateCache] here would make it be called prior to 
       // B and C's complete init function from being called.
   }
}

-(void)UpdateCache
{

}


@end

@implementation B
-(id)init
{
   if(self = [super init])
   {
       // Placing [self UpdateCache] would result in UpdateChache not being
       // called if you initialized an instance of Class A
   }
}

-(void)UpdateCache
{
   [super UpdateCache];
}

@end

@implementation C
-(id)init
{
   if(self = [super init])
   {
       // Placing [self UpdateCache] would result in UpdateChache not 
       //being called if you initialized an instance of Class A or B
   }
}

-(void)UpdateCache
{
   [super UpdateCache];
}

@end

【问题讨论】:

  • 冒着听起来很愚蠢的风险......在分配和初始化它之后,只为每个实例调用[yourObj updateCache]; 是不是一个大问题?
  • 你的描述不好理解。 “一旦整个对象被初始化”?你的意思是整个类树? updateCache 是方法还是函数?是类方法,还是每个子类都实现的实例方法?为什么一个实例会关心树中的其他类——也就是说,为什么你不能在每个子类的 init 末尾调用这个方法?
  • @HachiEthan - 这是我目前正在使用的解决方案,但我正在寻找更清洁的东西。实际上,我在很多地方都遇到过同样的问题,我想看看是否有任何通用的解决方案
  • @JoshCaswell - 此更新缓存功能是一项非常昂贵的操作,这就是缓存其结果的原因。如果我在每个子类中都调用它,那么在每一步调用它都会导致为相同的数据生成最多 4 次信息。
  • 你所描述的仍然不清楚。你说它是一个“函数”(应该是“方法”,我假设)被“子类覆盖”,但你似乎也说它只需要为整个类树调用一次。您的描述很混乱,您没有解决我原始评论中的任何问题。请详细说明您的设计并更具体;也许发布一些虚拟代码。

标签: iphone objective-c


【解决方案1】:

声明一个虚方法,在需要的时候调用它....

因为这是客观的,请参阅Implement a pure virtual method in Objective-C

【讨论】:

  • 我不相信这能解决我的问题。这里的问题是何时调用 [self UpdateCache] 函数,因为最后一个运行的 init 函数将根据有多少子类要初始化而变化。同一个类可能会或可能不会被各种不同的类子类化,因此没有正确的地方可以拨打电话。如果我误解了您的回答,请告诉我并说明这是如何解决的。
【解决方案2】:

是的,不久前我问过类似的问题... 您可以将“挂钩”/“代理”添加到对象实例以覆盖 -forwardInvocation: 选择器并执行您想要的操作。最初的问题是here,我对此的回答是被接受的。

【讨论】:

    【解决方案3】:

    与其在初始化之后立即更新缓存,不如在第一次使用之前更新它?也许你可以有一个布尔实例变量 cacheIsDirty 在 init 中设置为 TRUE。然后,如果缓存脏了,缓存的 getter 会首先调用 updateCache。假设您总是使用 getter 而从不直接使用实例变量(这在 Objective-C 中是一种很好的做法),客户端应该不会注意到差异。

    【讨论】:

    • 这可能是最好的建议,因为似乎唯一可行的方法就是维护一个子类列表。
    【解决方案4】:

    您的子类需要唯一的初始化方法签名吗? (例如,初始化对象所需的子类特定参数)如果不是,遵循简单的类似工厂的设计模式可能会很好。

    添加父类/基类的示例:

    +(id)buildSelf {
        YourParentClass* obj = [[[self alloc] init] autorelease];
        if (obj) {
            [obj updateCache];
        }
        return obj;
    }
    

    向它添加参数,供所有子类使用(如果需要)。

    同样,如果您的子类需要支持唯一的 init 方法签名,那么这将无法正常工作。

    【讨论】:

    • 查看修改后的代码示例。我不认为这解决了问题的根源。
    • @David 它应该可以正常工作...我在发布之前对其进行了测试以确定。默认情况下,在父类中调用[[self alloc] init] 将调用最顶层派生类的init 方法(如果存在)。 updateCache 也一样。多态性不是很好吗? ;D 这与您当前在实例化后手动调用updateCache 的解决方案非常相似。
    • @David 话虽如此,我会 100% 同意亚当伦纳德的回答,因为它更灵活并且遵循缓存数据的常见做法。如果 updateCache 不脏,请使用脏标志快速中止它,并在访问它正在更新的缓存/数据之前始终调用 updateCache
    • 我花了一段时间才明白你的意思,但这是正确的。您本质上需要一个单独的方法,该方法被覆盖而不调用 [Super X]。
    • @David 是的!确切地。我的示例+(id)buildSelf 方法应该只存在于父类中。此外,您可能需要采取其他步骤来帮助避免以其他方式实例化子类。
    猜你喜欢
    • 2021-12-02
    • 2017-11-28
    • 2020-09-05
    • 1970-01-01
    • 2018-02-07
    • 1970-01-01
    • 2021-03-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多