【问题标题】:Hidden features of Objective-C [closed]Objective-C 的隐藏特性
【发布时间】:2010-09-17 17:14:09
【问题描述】:

Objective-C 被 Apple 用于 Mac OS X 和 iPhone 开发的用途越来越广泛。您最喜欢 Objective-C 语言的哪些“隐藏”特性?

  • 每个答案一个功能。
  • 提供功能示例和简短说明,而不仅仅是文档链接。
  • 使用标题作为第一行标记功能。

【问题讨论】:

标签: iphone objective-c macos hidden-features


【解决方案1】:

方法混搭

基本上,在运行时,您可以将一种方法的一种实现换成另一种。

Here is a an explanation with code.

一个聪明的用例是共享资源的延迟加载:通常你会通过获取一个锁来实现sharedFoo方法,如果需要,创建foo,获取它的地址,释放锁,然后返回@ 987654324@。这样可以确保 foo 只创建一次,但每次后续访问都会浪费时间使用不再需要的锁。

使用方法 swizzling,您可以像以前一样做,除了一旦创建了 foo,使用 swizzling 将 sharedFoo 的初始实现换成第二个不检查并简单地返回 @我们现在知道的 987654328@ 已经创建!

当然,方法调配可能会给您带来麻烦,并且在某些情况下,上面的示例可能不是一个好主意,但是嘿...这就是为什么它是一个隐藏功能。

【讨论】:

    【解决方案2】:

    摆姿势

    Objective-C 允许一个类在应用程序中完全替换另一个类。替换类被称为“冒充”目标类。然后,发送到目标类的所有消息都由 POS 类接收。对哪些类可以构成一些限制:

    • 类只能作为其直接或间接超类之一
    • 姿势类不得定义目标类中不存在的任何新实例变量(尽管它可以定义或覆盖方法)。
    • 在摆姿势之前不得向目标类发送任何消息。

    与类别类似,Posing 允许全局扩充现有类别。姿势允许类别中缺少两个特征:

    • pose 类可以通过 super 调用被覆盖的方法,从而合并目标类的实现。
    • 姿势类可以覆盖类别中定义的方法。

    一个例子:

    @interface CustomNSApplication : NSApplication
    @end
    
    @implementation CustomNSApplication
    - (void) setMainMenu: (NSMenu*) menu
    {
         // do something with menu
    }
    @end
    
    class_poseAs ([CustomNSApplication class], [NSApplication class]);
    

    这会拦截对 NSApplication 的每次 setMainMenu 调用。

    【讨论】:

    • Class_poseAs 从 10.5 开始已弃用。替换是在一个方法一个方法的基础上做同样的事情,现在在运行时明确支持。
    • 糟糕,忘记链接docs。
    【解决方案3】:

    对象转发/方法丢失

    当一个对象被发送一个它没有的消息时 方法,运行时系统给了它另一个处理的机会 放弃前的电话。如果对象支持 -forward:: 方法,运行时调用这个方法,传递它 有关未处理呼叫的信息。返回值来自 转发的呼叫被传播回原来的呼叫者 方法。

    -(retval_t)forward:(SEL)sel :(arglist_t)args {
      if ([myDelegate respondsTo:sel])
     return [myDelegate performv:sel :args]
     else
     return [super forward:sel :args];
     }
    

    来自Objective-C Pocket Reference的内容

    这是非常强大的,并在 Ruby 社区中大量使用,用于各种 DSL 和 rails 等。起源于影响 Objective-C 和 Ruby 的 Smalltalk。

    【讨论】:

    • 我建议使用文档化的方法 ´-[NSObject forwardInvocation:]`。未记录的方法 -[Object forward::] 已被弃用很久,并且在现代 Objective-C 运行时不再可用。如 iOS 设备上的 64 位和 ARM。
    • 有谁知道这是否仍然有效?我似乎根本找不到任何关于此的文档。
    【解决方案4】:

    ISA 切换

    需要覆盖对象的所有行为?您实际上可以通过一行代码更改活动对象的类:

    obj->isa = [NewClass class];
    

    这只会更改接收该对象的方法调用的类;它不会改变对象在内存中的布局。因此,只有当您有一组具有相同 ivars(或一个具有其他 ivars 的子集)的类并且您想在它们之间切换时,这才真正有用。

    我编写的一段代码使用它进行延迟加载:它分配一个 A 类的对象,填充几个关键 ivars(在本例中,主要是记录号)并将 isa 指针切换到指向LazyA。当调用除了像release 和retain 这样非常小的集合之外的任何方法时,LazyA 从磁盘加载所有数据,完成对 ivars 的填充,将isa 指针切换回A,然后转发调用真正的类。

    【讨论】:

    • 天哪,我从来没想过。我需要对此进行测试,因为它很棒。我想知道是否有任何令人讨厌的副作用?
    • 我试着回答stackoverflow.com/questions/874906/…,但我得到编译错误:Instance variable 'isa' is protected. 有没有办法解决这个问题?见stackoverflow.com/questions/8512793/…
    • @MattDiPasquale 最佳答案是正确的——现代方法是使用 objc_setClass()。
    【解决方案5】:
    #include <Foundation/Debug.h>
    

    许多工具可用于尝试在该头文件中追踪内存泄漏、过早解除分配等。

    【讨论】:

    • 那是 Cocoa 特有的(也许 OpenSTEP 也是?)而不是语言功能,尽管除了 Cocoa 之外没有人使用 ObjC,我想这并没有太大的区别
    • 那么,这不适用于 Cocoa Touch,即 iOS?它到底是做什么的?
    【解决方案6】:

    类别

    使用类别,您可以将方法添加到内置类而无需子类化。 Full reference.

    给常用的类添加方便的方法很好,例如 NSString 或 NSData。

    【讨论】:

    • 很棒的提示,但不是很隐蔽……:)
    【解决方案7】:

    Objective-C 运行时参考

    很容易忘记,Objective-C 的语法糖被转换为普通的 C 函数调用,即 Object-C 运行时。您可能永远不需要在运行时实际深入研究和使用任何东西。这就是为什么我会认为这是一个“隐藏功能”。

    让我给出一种使用运行时系统的方法。

    假设某人正在设计一个将由第三方使用的外部框架 API。并且有人在框架中设计了一个抽象表示数据包的类,我们称之为MLAbstractDataPacket。现在取决于在框架中链接到子类MLAbstractDataPacket 并定义子类数据包的应用程序。每个子类都必须重写方法+(BOOL)isMyKindOfDataPacket:(NSData *)data。

    记住这些信息...

    如果MLAbstractDataPacket 提供一个方便的方法,该方法为+(id)initWithDataPacket:(NSData *)data 形式的数据包返回正确的初始化类,那就太好了。

    这里只有一个问题。超类不知道它的任何子类。所以在这里您可以使用运行时方法objc_getClassList() 和objc_getSuperclass() 来查找作为MLAbstractDataPacket 子类的类。获得子类列表后,您可以在每个子类上尝试 +isMyKindOfDataPacket:,直到找到或未找到。

    关于这方面的参考信息可以在http://developer.apple.com/documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html找到。

    【讨论】:

      【解决方案8】:

      我喜欢像[myArray writeToFile:myPath atomically:YES] 这样的详细方法命名,其中每个参数都有一个标签。

      【讨论】:

      • 那是怎么隐藏的?... :)
      • @Psionides:这对于实际使用我同意的语言的人来说是很明显的,但大多数来自其他语言的人似乎都忽略了这个简单的事实。至少是我与之交谈过的人。
      • 你不能错过!如果您真的想传递消息,则不是!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-05
      • 2013-07-27
      • 1970-01-01
      • 2011-03-19
      • 1970-01-01
      • 1970-01-01
      • 2010-11-07
      相关资源
      最近更新 更多