2

I have a collection of Objective-C classes that get sub-classed at a variety of different depths by a variety of different classes. Once the entire object is initialized (all of the sub-classes init functions have finished), I need to run an "Update Cache" method which then gets Overridden by the sub-classes as needed.

My Problem: With a variety of different inheritance depths to my class tree, there is no one place where I can put [self UpdateCache] and I can be sure that there is no sub-class that has not been initialized. The only possible solution would be to call [super init] after the initialization of each class so that the parent class is always called last. I want to avoid this as this goes against all guidelines of writing Objective-C. Is there any clean solution to this problem?

Here is an some example code:

@interface ClassA : NSObject

-(void)UpdateCache
@end

@interface ClassB : ClassA

-(void)UpdateCache
@end

@interface ClassC : ClassB

-(void)UpdateCache
@end

Now for the implementation we need to somehow get UpdateCahce to be called after we know all sub classes have initialized regardless of which class has been initialized

@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
David
  • 1,648
  • 1
  • 16
  • 31
  • At the risk of sounding stupid... is it a huge problem to just call `[yourObj updateCache];` per instance, after you've allocated and initialized it? – MechEthan Jun 13 '12 at 18:15
  • Your description is not easy to understand. "Once the _entire object_ is initialized"? Do you mean the entire class tree? Is `updateCache` a method or a function? Is it a class method, or is it an instance method that each subclass implements? Why does an instance care about other classes in the tree -- i.e., why can't you just call this method at the end of `init` in each of your subclasses? – jscs Jun 13 '12 at 18:18
  • @HachiEthan - This is the solution I am currently using, but I was looking for something cleaner. I actually have had this same problem show up in a variety of places and I wanted to see if there was any general solution to it – David Jun 13 '12 at 18:47
  • @JoshCaswell - This update cache function is a very expensive operation which is why its results are being cached. Calling it at every step would cause the information to be generated up to 4 times for the same data if I did it in every subclass. – David Jun 13 '12 at 18:49
  • What you're describing still isn't clear. You say it's a "function" (should be "method", I assume) that's being "overridden by the sub-classes", but you also seem to be saying that it needs to be called only once for the entire class tree. Your description is confused and you haven't addressed any of the issues in my original comment. Please elaborate more on your design and be more specific; perhaps post some dummy code. – jscs Jun 13 '12 at 19:16
  • @JoshCaswell - I have updated the original question addressing your requests – David Jun 13 '12 at 19:36
  • Okay, that makes a little bit more sense, thanks. So do any of those subclasses do any actual work in their overrides? It really seems like `updateCache` should be a class method, especially if it has something to do with the inheritance tree itself. – jscs Jun 13 '12 at 19:43
  • I can imagine how to do this only by maintaining a list of the subclasses and "checking them off" as they are instantiated; I think Adam Leonard's answer may be the way to go. – jscs Jun 13 '12 at 19:55
  • @JoshCaswell - This is a great simplification of a fairly complex system. All the overridden "UpdateCache" methods rely on the super classes "UpdateCache" functions finishing first so they can use their data. They also rely on instance variables and would not be suited for a class method. I really just wanted to know if there is a possible solution. I like Adam Leonard's answer, but its more of a work around for a problem I see in Objective-C – David Jun 13 '12 at 20:28
  • I wish I could help. Good luck! – jscs Jun 13 '12 at 20:31

4 Answers4

3

Rather than updating the cache right after init, why not instead update it right before it is first used? Perhaps you can have an boolean instance variable cacheIsDirty which you set to TRUE in init. Then, your getter for the cache calls updateCache first if the cache is dirty. Assuming that you're always using getters and never using the instance variable directly (which is good practice in Objective-C) clients shouldn't notice a difference.

Adam Leonard
  • 489
  • 2
  • 5
  • This is probably the best suggestion, since it seems like the only other thing that would work would be to maintatin a list of the subclasses. – jscs Jun 13 '12 at 19:44
1

Do your sub-classes require unique init method signatures? (e.g. sub-class specific parameters required to initialize the object) If not, following a simple factory-like design pattern may work fine.

Example of what to add your parent/base class:

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

Add parameters to it, for all sub-classes to use, if needed.

Again, if your sub-classes need to support unique init method signatures then this won't work so well.

MechEthan
  • 5,703
  • 1
  • 35
  • 30
  • See the revised code example. I don't think this addresses the root of the problem. – David Jun 13 '12 at 19:39
  • @David It should work fine... I ran it through a test before posting to be sure. Calling `[[self alloc] init]` in the parent class will, by default, call the the topmost derived class's `init` method if one exists. Same for `updateCache`. Ain't polymorphism great? ;D This is very similar to your current solution of manually calling `updateCache` after you instantiate. – MechEthan Jun 13 '12 at 20:19
  • @David That being said, I would 100% go with Adam Leonard's answer because its more flexible and follows a common practice for cached data. Use a dirty-flag to quickly abort `updateCache` if its not dirty, and always call `updateCache` before accessing the cache/data it's updating. – MechEthan Jun 13 '12 at 20:28
  • It took me a while to understand what you were getting at, but this is correct. You essentially need a separate method which is overridden does not call [Super X]. – David Jun 13 '12 at 21:17
  • @David yep! exactly. My example `+(id)buildSelf` method should only exist in the parent class. Also, you may want to take additional steps to help avoid sub-classes from being instantiated in other ways. – MechEthan Jun 13 '12 at 21:22
0

Declare a virtual method and call it when you need it....

and since this is objective c see Implement a pure virtual method in Objective-C

Community
  • 1
  • 1
Jay
  • 3,276
  • 1
  • 28
  • 38
  • I don't believe this solves my problem. The issue here is when to call the [self UpdateCache] function, as the last init function to run will be variable based on how many child classes are left to init. The same class my or may not be subclassed by a variety of different classes so there is no correct place to place the call. If I have misunderstood your answer, please let me know and clarify how this solves it. – David Jun 13 '12 at 17:51
0

Yes, I asked a similar question a while back... You can add a 'hook' / 'proxy' to an instance of an object to override the -forwardInvocation: selector and perform what you want. The original question is here, and my answer on it is the accepted one.

Community
  • 1
  • 1
Richard J. Ross III
  • 55,009
  • 24
  • 135
  • 201