【问题标题】:Why is contentView property of NSWindow of type id?为什么 NSWindow 的 contentView 属性是 id 类型的?
【发布时间】:2012-02-28 03:22:39
【问题描述】:

为什么NSWindow类的contentView属性是id而不是NSView

这对我来说没有意义,为什么 contentView 应该是 NSView 子类以外的任何东西。

所以在我的情况下,我必须这样输入才能访问它的框架:

NSView *contentView = self.window.contentView; // returns an `id`
CGRect frame = contentView.frame

而不是这个,编译器不喜欢:

CGRect frame =  self.window.contentView.frame; // This does not compile

【问题讨论】:

    标签: objective-c cocoa nsview nswindow


    【解决方案1】:

    这可能是历史性的。 Objective-C 支持严格类型,但它也支持“鸭子类型”,在这种情况下,您不关心对象是什么,只关心它响应什么消息(即如果它看起来像一只鸭子并且它像鸭子一样嘎嘎叫,它可能是一只鸭子)。您可以将每个对象指针键入为id 并发送任何消息。事实上,接收者不需要为它接收到的任何消息实现方法:它也可以将消息转发给另一个对象。

    在 Application Kit(OpenStep 和 Cocoa 的前身 GUI 框架)中,几乎所有对象都是通过鸭子类型来使用的。这是 Application Kit 3.2 版中Window 的(部分)接口。

    @interface Window : Responder
    {
      NXRect frame;
      id contentView;
      id delegate;
      id firstResponder;
      id lastLeftHit;
      id lastRightHit;
      id counterpart;
      id fieldEditor;
      int winEventMask;
      int windowNum;
      float backgroundGray;
      //some bit masks indicating whether the window is visible, is key etc.
    }
    -contentView;
    -setContentView:aView;
    //more methods
    @end
    

    注意contentView ivar 被定义为id,并且访问器方法中的所有类型也被隐式定义为id(所以-setContentView: 返回一个对象:可能是@987654328 @实例self)。这就是 1990 年代初期大多数 Objective-C 代码的样子:Application Kit 可能是 1990 年代初期大多数 Objective-C 代码。

    NSWindow 是在 AppKit 的第一个版本中引入的 - 早在 1994 年成为 Cocoa 的 GUI 框架。AppKit 通常使用比 Application Kit 更严格的类型声明,但并未严格遵守。事实上,AppKit 的 NSWindow 甚至可能包含来自 Application Kit 的 Window 的代码,并且这个 contentView ivar 没有在更改中更新。

    确实,Objective-C 变量中类型一致性的严格要求是最近才出现的。大多数严格性是通过属性声明(除了 C 存在并支持强制转换外,它们都是强类型)或通过更改允许可选方法的协议来引入的,因此可以严格类型化委托对象。

    【讨论】:

    • 我感觉使用越来越多的静态类型是一个持续时间更长的过程。当我接触到这门语言(2002 年)时,它已经无处不在了。 Apple 的 Objective-C 指南建议尽可能多地使用它。事实上,苹果似乎也确实在几乎所有语言允许的情况下都使用了它(容器、初始化程序、委托除外)。
    • 感谢您的深入回答。我想我可以(嗯……不得不)忍受演员阵容或第二行代码:-)
    • @NikolaiRuhe 第一个激励因素之一是 DO,当根对象符合已知协议时,它的工作效率更高(因为这意味着客户端不必继续发送 respondsToSelector: 调用跨边界):但这显然并没有改变每个的情况。这确实意味着人们开始使用 void 作为方法返回类型(因此跨边界发送消息可能是一种方式)。
    【解决方案2】:

    -[NSWindow contentView] 具有 id 类型这一事实可能是早期 Cocoa 和 Objective-C 的遗留物。

    无论如何:编译器警告是您用于发送消息的属性样式语法的结果。在 Cocoa(相对于 Cocoa-Touch)中,windowcontentViewframe 不是它们类的属性。这意味着您应该使用正常的消息发送语法:

    CGRect frame = [[[self window] contentView] frame];
    

    这将在没有编译器警告的情况下工作。

    【讨论】:

    • 不,错误来自这样一个事实,即类型 'id' 的接口中没有显示正确的方法声明以使点符号生效。属性不需要使用点符号,如果我愿意,我可以调用NSObject.new 而不是[NSObject new]
    • @Richard J. Ross III:您不能使用点语法向id 类型变量发送任何消息(属性访问器或非属性)。但是您可以使用正常的消息发送语法发送任何已知的选择器。
    • @NikolaiRuhe:如果选择器尚未声明,则在 ARC 模式下除外。这是手动内存管理中的一个警告,但它是 ARC 的错误。
    【解决方案3】:

    它用于动态类型。 NSWindow 并不真正关心它是否是 contentView 是否真的是 NSView,如果它响应发送的选择器。因此,理论上您可以创建自己的类,该类不继承自 NSView 用于显示内容,并且编译器不会阻塞。

    【讨论】:

    • setter 的签名是- (void)setContentView:(NSView *)view :(
    • @NikolaiRuhe 正确,但问题是,它不能隐式转换允许您在 id 上使用点表示法,因为在 id 的接口中没有定义方法。
    • 我不确定你想说什么。 id 不声明接口。无论如何,为了正确起见,您应该根据 Apple 的文档编辑您的答案:contentView 返回一个 NSView
    猜你喜欢
    • 2011-02-06
    • 2012-10-21
    • 2010-12-06
    • 2011-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    相关资源
    最近更新 更多