代码之家  ›  专栏  ›  技术社区  ›  Ian1971

充分利用Objective-C动态特性

  •  12
  • Ian1971  · 技术社区  · 16 年前

    我使用Objective-C已经有一段时间了,但是由于是静态类型的背景(C),我认为我使用它的方式非常静态。将对象声明为ID对我来说很陌生,我看不出有什么好处。有人能为我照亮一盏灯,让我更好地理解这一点吗?

    4 回复  |  直到 16 年前
        1
  •  13
  •   Adrian Kosmaczewski    16 年前

    Objective-C是一种混合语言,在这种语言中,你可以随心所欲地保持动态和静态。如果需要,可以声明所有变量的所有类型,如果需要,甚至可以将委托变量声明为nsObject<Protocol>*类型。 ID类型的工作方式不是真正的类型,更像是编译器的提示。 告诉他“嘿,我知道我在做什么,相信我”,让编译器避免对特定变量进行任何类型检查。

    Objective-C类型系统的第一个明显好处是容器类型(nsarray、nsdictionary、nsset)接受和返回ID类型。这完全消除了对模板和泛型的需求(如C++、Java和C语言)。

    更好的是,实际上可以在容器中包含任何类型的元素。只要你知道里面有什么,如果你在同一个nsarray中添加两个nsstring,一个nsnumber和一个nsvalue,没有人会抱怨。您可以在其他语言中这样做,但是您必须使用“对象”基类或void*类型,然后需要对变量进行装箱和取消装箱(或上下转换)以获得相同的行为。在Objective-C中,您只需分配,它将消除由铸造操作员和装箱操作产生的噪声。然后,您可以在运行时向每个对象询问“respondstoselector:”或“class”,以了解您可以使用它们执行的标识和操作。在客观性C中,反思是一等公民。

    另一个好处是编译时间减少;编译一个Objto-C程序通常比C++中的等价程序快得多,因为没有执行很多类型的检查,并且在运行时完成了很多链接。编译器更信任程序员。

    最后,Objective-C的动态类型系统使得拥有类似工具的界面构建器成为可能。这就是为什么Cocoa和Cocoa Touch的开发速度更快的主要原因;图形用户界面可以在各处生成“id”类型的代码,并且每当NIB加载到内存中时,都会对其进行反序列化。在用户界面设计经验方面,唯一接近于Objective-C的语言是C(当然还有vb.net),但代价是应用程序要重得多。

    我个人更喜欢使用更静态的类型检查,甚至在Objective-C编译器中打开了“将警告视为错误”设置;我写了一篇关于它的博客文章:

    http://akosma.com/2009/07/16/objective-c-compiler-warnings/

    当您与不熟悉该语言的开发人员一起工作时,这尤其有用。它使编译器比平常更经常地发出呜呜声:)

    静态类型系统专家可能不同意所有这些观点,他们认为静态类型检查允许使用“智能感知”的IDE,并且总体上可以更好地维护。我用.NET工作了很多年(2001-2006年),我必须说,动态语言产生的代码更少,更容易阅读,而且总体上,给了我更多的工作自由。权衡(总是有一个权衡)是编译时的信息更少。但正如我常说的, 编译器是穷人的一套测试。 最好的事情是有一套很好的测试,和一堆人类测试人员折磨你的代码来发现错误,无论你选择什么语言。

        2
  •  5
  •   Yuji    16 年前

    客观-C的活力不仅仅在于每一个物体都是 id . 相反,它在Objective-C运行时的强大功能和易用性中脱颖而出。苹果自己巧妙使用运行时的几个例子:

    DO 允许您在单独的应用程序/单独的计算机中为obj-c对象设置代理对象。这是通过截取发送到代理对象的所有消息、打包、发送到另一个应用程序并在那里调用它来完成的。

    KVO 通过动态替换setter方法来实现,以便它自动通知观察者。(事实上,这比……更微妙)

    CoreData accessors 在运行时为的每个子类生成 NSManagedObject 等。

    而且,您可以使用 runtime 也从你的代码。我曾经使用它来获得良好的效果,在运行时模仿coredata并生成访问器,并且在头文件中只有它们的声明。因此,您可以同时获得静态类型(来自头中声明的编译时错误)和动态类型(方法的运行时生成)的优点。

    MikeAsh写了一系列关于运行时如何工作以及如何有效地使用它的优秀博客文章。你只要读一下就行了! DO , KVO , message forwarding 还有更多。网上还有很多其他有趣的帖子,比如 fun with kvc 以及更高阶的消息传递 1 ,请 2 .

        3
  •  3
  •   Ciarán Walsh    16 年前

    实际上很少需要将对象声明为类型 id ,因为您通常应该知道所期望的类型。有时您可能会使用 id<Protocol> 类型,如果您不知道对象的实际类型,但知道它应该符合特定的协议。

    您是否在考虑特定的场景?

        4
  •  2
  •   Laurent Etiemble    16 年前

    将实例作为传递 id 在设计动作的方法时很常见;将按钮连接到方法时,目标看起来像 doSomething:(id) sender; .

    在这种情况下,它允许不同类型的控件使用同一操作的方法,而不必事先知道这些控件将是什么。在操作的方法代码中,可以测试发送者的类,也可以简单地使用 tag 属性,以决定要做什么。

    -(void) doSomething:(id) sender {
        // Get the sender's tag whatever it is
        int tag = [sender tag]; 
        switch(tag) {
            case 1:
                // ...
                break;
            case 2:
                // ...
                break;
        }
    }