【问题标题】:Objective-C, protocols and subclassesObjective-C,协议和子类
【发布时间】:2011-07-07 08:26:00
【问题描述】:

假设我定义了以下协议:

// 用户界面对象的基本协议:

@protocol UIObjectProtocol <NSObject>
@property (assign) BOOL touchable;
@end

//holder 对象持有的用户界面对象的基本协议:

@protocol UIHeldObjectProtocol <UIObjectProtocol>
@property (readonly) id holder;
@end

以及以下类层次结构:

// 用户界面对象的基类,它综合了touchable 属性

@interface UIObject : NSObject <UIObjectProtocol> {
   BOOL _touchable;
}
@end

@implementation UIObject
@synthesize touchable=_touchable;
@end

此时,一切正常。然后我创建了一个名为UIPlayingCardUIObject 子类。本质上,UIPlayingCard 符合 UIObjectProtocol,因为它的超类也这样做。

现在假设我希望 UIPlayingCard 符合 UIHeldObjectProtocol,所以我执行以下操作:

@interface UIPlayingCard : UIObject <UIHeldObjectProtocol> {
}
@end

@implementation UIPlayingCard
-(id)holder { return Nil; }
@end

请注意,UIPlayingCard 符合 UIHeldObjectProtocol,后者可传递地符合 UIObjectProtocol。但是我在UIPlayingCard 中收到编译器警告,例如:

警告:属性 'touchable' 需要 要定义的方法“-touchable” - 使用@synthesize、@dynamic 或提供 方法实现

这意味着 UIPlayingCard 超类与 UIObjectProtocol 的一致性没有被继承(可能是因为 @synthesize 指令是在 UIObject 实现范围中声明的)。

我是否有义务在 UIPlayingCard 实现中重新声明 @synthesize 指令?

@implementation UIPlayingCard
@synthesize touchable=_touchable; // _touchable now must be a protected attribute
-(id)holder { return Nil; }
@end

或者还有其他方法可以消除编译器警告?会不会是设计不好的结果?

提前致谢,

【问题讨论】:

  • 这与您的问题无关,但在您自己的类/协议/常量/whatevers 中使用“Apple 的”前缀通常不是最好的主意:您永远不知道会包含什么大水果下一个版本...
  • 我同意你的观点 danyowdee,事实上我并没有在我的应用程序中使用“UI”前缀,我确实遵循了你列出的所有这些良好做法。我只是在帖子中隐藏了真正的前缀(例如公司前缀),因为它与问题无关。

标签: iphone objective-c properties protocols compiler-warnings


【解决方案1】:

我将使用另一种方式来消除警告:使用@dynamic,编译器将假定该实现将以其他方式提供,而不是在类的实现中的声明中(在这种情况下,它由超类提供)。

【讨论】:

  • 总结:解决方法(您认为这是一种解决方法还是在这种情况下使用@dynamic语言功能是正确的?)是执行以下操作:1)在UIObject实现中:@synthesize touchable=_touchable 2) 在UIPlayingCard 实现中:@dynamic touchable;。这是解决此类问题的最小实现,对吗?
  • 我没有查看语言定义来确定 GCC 或 CLANG 行为是否正确(或者这种情况是否没有解决或不清楚)。如果 GCC 是正确的,或者没有解决或不清楚,我会称之为正常和适当的使用;如果 CLANG 是正确的,我会称之为解决方法。无论哪种方式,这都是我会做的。是的,这是使警告静音的最小实现。
【解决方案2】:

我是否有义务在 UIPlayingCard 实现中重新声明 @synthesize 指令

虽然我没有从你的例子中看到,为什么你正在做你正在做的事情:

不,您不需要重新实现已在父类中实现的方法。

如果编译器警告这些方法不存在,那么这是一个错误:
正如The Objective C Programming Language 所说...

当一个类采用协议时,它必须实现协议声明的所需方法,如前所述。此外,它必须符合所采用的协议所包含的任何协议。如果合并的协议还包含其他协议,则该类也必须符合它们。一个类可以通过以下任一方式符合一个合并的协议:

  • 实现协议声明的方法,或
  • 从采用协议并实现方法的类继承。

【讨论】:

  • 感谢分享这条信息。但是,我仍然不清楚最终的解决方案。因此,“从采用协议并实现方法的类继承。”意思是:即使UIObject 类合成了touchable 属性,UIPlayingCard 子类必须实现-(void)setTouchable:(BOOL)touchable { [super setTouchable:touchable]; }- (BOOL)touchable { return [super touchable]; } 才能使用超类实现(而不是重新实现它们)以避免编译器警告?或者还有其他我没有看到的解决方案?
  • 否:无论何时创建子类,它都会继承其祖先提供的所有功能——因此它自动符合那些祖先的任何协议符合。这是第二个要点的含义,也是我说这是您使用的任何编译器版本中的错误的原因:UIObject : NSObject &lt;UIObjectProtocol&gt;any UIObject(包括任何派生自它的类的实例)将提供协议中定义的方法,@synthesize 履行此合同。
  • “或者还有其他我没有看到的解决方案?” 1. 向 Apple 提交包含示例项目的错误。这样,它可能会在未来的版本中得到修复。 2. 在他们修复它之前,使用 Anomie 的提示让编译器关闭 ;-)
【解决方案3】:

协议就像合同。你说的是 UIPlayingCard 必须实现持有者和可触摸的。

当编译器编译 UIPlaying 卡时,它看不到任何定义 @property (nonatomic) BOOL touchable; 的地方。

您不能将合成添加到打卡,因为打卡没有可触摸的 ivar。

变化:

@protocol UIHeldObjectProtocol <UIObjectProtocol>

@protocol UIHeldObjectProtocol <NSObject>

由于 UIPlayingCard 继承自 UIObject,协议不需要相互继承。

【讨论】:

  • 我已经验证,使用 GCC,将 @property 声明添加到 UIObject.h 不会阻止 GCC 发出警告。
  • 是的。我的错误......我会修复答案。
  • 这实际上是错误的:该属性是在UIObject符合的协议中定义的。并且由于UIPlayingCard 继承UIObject 它自动符合UIObjectProtocol
  • 是的,这就是UIPlayingCard 继承自UIObject 的点,因此自动符合UIObjectProtocol。正如 Jeremy W. Sherman 所说:“该实现实际上是继承的,因为您可以通过一个简单的 main 函数来验证 UIPlayingCard 的可触摸属性。”。问题是编译器显示了令人不快的警告。
【解决方案4】:

您评论说 _touchable 必须受到保护,但您没有包含 @private 编译器指令,因此 _touchable受到保护。

Clang 在这种情况下似乎没有给出这个警告,所以最直接的解决方案:

  • 将编译器从 GCC 更改为 Clang。

这也有很多附带好处,包括更易理解的错误和警告消息。

这是 GCC 抱怨的一个错误。该实现实际上是继承的,您可以通过一个简单的main 函数来验证UIPlayingCardtouchable 属性。所以,你的第二个解决方案:

  • 忽略警告为虚假。如果您的任何测试都没有失败,那么您还关心什么?

【讨论】:

  • 感谢您提醒有关受保护的财产。我宁愿为此警告提供解决方案,而不是忽略它们(即使它们是虚假的)。我正在使用 XCode 4,但没有发现将编译器从 GCC 更改为 Clang 的直接方法。 1)更改编译器会避免此类警告吗? 2)你在Xcode的哪个版本中使用Clang编译器?感谢您提供的重要信息。
  • Clang 获得了更多 Apple 的喜爱,并且拥有更现代的架构,因此它往往比现有的任何东西都拥有更好的 Obj-C 支持。我仍在使用 3 系列的 Xcode,但我相信您应该能够选择编译器作为构建方案的一部分。尝试搜索编译器、GCC 或“默认编译器”,然后翻转该设置。
猜你喜欢
  • 1970-01-01
  • 2011-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多