2012-06-13 58 views
0

我不认为我想要做的事情是可能的,但这里有人比我更狡猾,所以我想我会问反正....覆盖基类@property,同时仍允许基类访问它

想象一下我们有A类:

@interface A 
@property (strong) NSArray *items; 
// There'll be methods and stuff too 
@end 

现在,我们需要在更换下降该类改变一些行为方面:

@interface B : A 
@property (strong) NSArray *items; 
// There'll be methods and stuff too 
@end 

特别,我们覆盖th e“物品”属性,因为我们需要它来返回不同的东西。

这很好地运行,直到一个在B实例上调用的方法,它在A中实现并且未被B覆盖,试图访问“self.items”。而不是访问它自己的副本,它访问B的重写版本,从而看到意想不到的结果 - 并确实崩溃在我的真实情况下的应用程序:(

我可以通过重命名B中的属性来解决此问题 - 但此时不再这是一个耻辱

请注意,B类可以通过超级访问A的属性版本。理想情况下,任何从A类访问属性也会访问A的版本。有没有办法知道谁在请求它,例如?

Tim

+0

当你说“A类中的某个方法试图访问”时,我认为你正在讨论一个在B实例上调用的方法,该实例在A中实现,而不是由B覆盖。是吗? A的实例无法使用B的访问器。 –

+2

我不认为有可能做你想做的事。通过覆盖子类中的变量/方法,该类的所有实例都将使用适合该类的方法,而不是调用方法所在的方法。 –

+0

乔希 - 是的,我已经更新了这个问题,以便更清楚。 – tarmes

回答

0

你可以在技术上实现你想要的,但在可怕的一轮 - 关于黑客。将这类事情置于自己的生产代码中,如果一切崩溃,不要期待任何同情。

请勿将B作为A的一个子类,使其仅属于具有A的类。在B上实现这些方法,无论如何您都可以在子类中替换这些方法。要处理items,请确保BsetItems:A进行适当的相应调用 - 事实上,对于您覆盖的所有方法而言,您希望也可以与A进行通信。

然后创建一个类C其中有BA。不要在其上执行任何操作。按照正常惯例allocinit回报(id)和执行的有点奇怪的任务:

A *instance = [[C alloc] init]; 

C只实现您的首选转发逻辑:

@implementation C 

- (id)forwardingTargetForSelector:(SEL)aSelector 
{ 
    if([self.instanceOfB respondsToSelector:aSelector]) return self.instanceOfB; 
    return self.instanceOfA. 
} 

@end 

现在每个引用A使得以self是引用A的实例而不是子类。尽管如此,在系统中您有一个B的实例,并且它将通过获取A的相关实例将优先于其服务的任何消息,同时能够推迟任何想要达到A的任何消息,从而充当它从A继承。

通过给那些本来具有A实例的实例C的类,它们将通过您的唯一继承逻辑而不是默认的东西。此外,由于您已在适当的位置返回(id),并且两者都是对象,所以这是一个安全的隐式转换,编译器不会抱怨说您继续调用C未实现的方法。

+0

聪明。在我的情况下,它更简单,只需将A作为B的一个实例来解决所有问题 - 其明显是明显的!感谢您的灵感,TAFetchedResultsController(在GitHub上)对您的干预现在好多了。 – tarmes

0

你的问题很混乱,但无论如何...

重写方法/属性不会改变它的全局;它只会改变它覆盖的类。如果A的一个实例访问属性,它仍然会访问它自己的属性,它不会被子类的实现神奇地接管。

(顺便说一句,我要提到您的类可能是非常糟糕,如果你需要这样的行为,设计...)

+0

对不起,我不够清楚。我指的是在B实例上调用的方法,它在A中实现,而不是由B重写。上述方法显然在某些时候访问“self.items”,并且这样做会检索我重写的版本 - 我想它继续得到它自己的代替。 A类是由Apple编写的 - 我不能改变它:) – tarmes

+0

如果你在'B'的实例上调用它,即使这个方法只在'A'中实现,它应该总是使用'​​B'''项目对象 –

+0

是的,但我希望有一个狡猾的方法来阻止这种情况的发生,比如如果我知道A请求它,就返回A的副本。 – tarmes

0

嗯,以及覆盖B中的属性意味着B中的任何存取将访问A中的新属性.A的行为保持不变,A中的访问者将始终访问A中的原始属性。类不能也不应该意识到它的子类。

如果您希望子类继续访问超类属性,则无法覆盖它。期。

另请注意,属性(或其访问者)必须在接口中声明(基本上公开),以使其副本可以在子类中直接访问。这是必要的,因为在Objective-C中不存在“受保护的属性”这样的东西。

最后我们假设你将A,B或C类的类放在一个包中,然后忘记它们的类型,只要知道所有共享的都是一个关系,那么就可以使用动态绑定/内省来决定哪个类方法将根据它的类型被调用。