协议对象有静态存储持续时间吗?

时间:2014-04-11 10:59:05

标签: objective-c objective-c-runtime objective-c-protocol

当您将@protocol(SomeProtocol)作为参数传递给方法时,结果指针是否可以被视为具有静态存储持续时间?

现在考虑到协议是在编译时定义的,在.h文件中,这意味着它的指针在程序的整个生命周期内是相同的,并且可以安全地复制,在某种意义上,在运行时是静态的 - 时间?

1 个答案:

答案 0 :(得分:2)

TL; DR:是的,有一些警告。

关于Protocol指针是什么有一些非常明显的混淆,所以我会试图澄清对此的任何困惑。

早在ObjC的旧时代,协议根本就不存在。因此,当NeXT的API需要多重继承时,他们将其作为自己的类“入侵”为语言,并使用特殊语法修改编译器以接受它。

在ObjC2推出之前一直运行良好,并且协议将作为官方语言(和运行时API)功能添加,但这会导致一些向后兼容性问题,因为我们已经有{{1由NeXT定义的类。

出现的解决方案是保持Protocol类,但弃用它。因此,我们在技术上仍然具有Protocollying around in the runtime,但是没有一种方法在ObjC2中工作(实际情况就是很多不同的旧构造,例如Protocol选择器,forward::和其他一些小事,但我离题了。

您不应该(读取不能)向此类发送消息 - 虽然它在技术上仍然是一个对象,但您不应期望它符合应用程序中存在的其他对象的相同约定(协议?)。

然后,解决方案是使用前缀为objc_msgSendv的运行时中的函数,例如:

  • protocol_代替protocol_getName()
  • -name代替protocol_conformsToProtocol()

请注意,在协议类的当前实现下,这些方法实际上仍然可以被调用,并且只是它们的C函数对应物的垫片。

话虽如此,如果所有协议都是使用-conformsTo:获得的,那么它们与运行时的当前版本保证具有“静态”存储,我们可以通过{{ 3}}:

objc_getProtocol

没有协议的副本,并且每个后续调用都将返回相同的指针。这也适用于使用/*********************************************************************** * objc_getProtocol * Get a protocol by name, or return nil * Locking: read-locks runtimeLock **********************************************************************/ Protocol *objc_getProtocol(const char *name) { rwlock_read(&runtimeLock); Protocol *result = (Protocol *)NXMapGet(protocols(), name); rwlock_unlock_read(&runtimeLock); return result; } 表达式,它基本上(尽管不完全是,还有一些其他编译器魔法)被认为是对@protocol(name)的调用。

现在 - 理论上,某人可能故意创建协议“对象”的副本,因为结构非常简单,如implementation所述:

objc_getProtocol

(请注意,这是一个模仿objective-c对象的C ++结构,如果你试图自己模拟它会导致一些困难。)

这里要注意的重要一点是,只需对运行时给你的协议指针执行struct protocol_t : objc_object { const char *name; struct protocol_list_t *protocols; method_list_t *instanceMethods; method_list_t *classMethods; method_list_t *optionalInstanceMethods; method_list_t *optionalClassMethods; property_list_t *instanceProperties; uint32_t size; // sizeof(protocol_t) uint32_t flags; const char **extendedMethodTypes; }; 就会导致很少的问题,因此,你应该总是使用{{1}用于比较协议,它将检查协议的字段以确保它们实际上是等效的,即使它们有不同的指针。

简单地将memcpy视为静态是完全正常的,并且是在代码中引用协议的正确方法。

但是,请注意,与任何运行时功能一样,这可能会随着API的另一个版本,编译器,ABI,目标体系结构和其他内容而发生变化,因此请务必阅读最新信息可以谈论这个主题!