【问题标题】:Problems calling objective c @dynamic properties from objective c runtime从目标 c 运行时调用目标 c @dynamic 属性的问题
【发布时间】:2013-07-29 13:02:27
【问题描述】:

我正在编写用于从 python 访问目标 c 的 lib,因此我使用 Cython 和目标 c 运行时来访问目标 c。让我们说用户以这种方式声明属性:

@property (assign) NSString *prop_nsstring_dyn;

并且他想将@dynamic 与此属性一起使用。好的,让我们使用这个宏:

#define ADD_DYNAMIC_PROPERTY(PROPERTY_TYPE, PROPERTY_NAME, SETTER_NAME) \
\
@dynamic prop_nsstring_dyn; \
- ( PROPERTY_TYPE ) PROPERTY_NAME \
{ \
printf("returning\n"); \
return ( PROPERTY_TYPE ) objc_getAssociatedObject(self, @selector(PROPERTY_NAME)); \
} \
\
- (void) SETTER_NAME :( PROPERTY_TYPE ) PROPERTY_NAME \
{ \
printf("setting\n"); \
objc_setAssociatedObject(self, @selector(PROPERTY_NAME), PROPERTY_NAME, OBJC_ASSOCIATION_RETAIN); \
} \

之后我们可以在@implementation 中调用它:

ADD_DYNAMIC_PROPERTY(NSString*, prop_nsstring_dyn, setProp_nsstring_dyn);

如果我们以这种方式从目标 c 调用:

c.prop_nsstring_dyn = @"test str";

c.prop_nsstring_dyn;

getter 和 setter 被正确调用。 但是,我需要从目标 c 运行时设置和获取这个动态属性的值,所以如果我使用像 objc_getIvar/objc_setIvar 这样的函数,getter 和 setter 根本不会被调用,结果我得到的是空指针。

有谁知道如何调用 get/set 函数。来自目标 c 运行时的动态属性?

谢谢!

【问题讨论】:

  • 你调查过pyobjc吗?
  • 是的,我查看了 pyobjc 源代码,他们的方法与我不同,但我认为@Nathan Day 的回答会对我有所帮助
  • 酷;只是确保你没有重新发明那个特定的轮子。 :)

标签: objective-c objective-c-runtime


【解决方案1】:

getter 和 setter 被正确调用。但是,我需要从目标 c 运行时设置和获取这个动态属性的值,所以如果我使用像 objc_getIvar/objc_setIvar 这样的函数,getter 和 setter 根本不会被调用,结果我得到的是空指针。

这是你的错误。您不应该调用objc_getIvarobjc_setIvar 来与其他对象属性进行交互。属性不是 ivars。属性是实现方法的承诺。你不能假设这些方法是如何实现的。您当然不能假设它们等同于对 objc_getIvar 的调用(正如您所发现的那样)。

@NathanDay 使用 KVC 的方法是一种不错的方法,但 [obj valueForKey:@"foo"][obj foo] 并不完全相同。不过,它已经很接近了。

首先,要明确的是,您并没有真正“获取对象属性的值”。您甚至没有真正“调用对象方法”。您向对象发送消息,它们会返回结果。属性主要是一种方便的方式来声明您将响应某些消息(选择器)。

要正确发送消息,您必须致电objc_msgSend。请注意,它有多种形式,具体取决于返回值是浮点还是结构(这是使用 valueForKey: 的好处;它使所有返回值看起来都一样,您不需要知道以objc_msgSend 的形式调用)。在任何情况下,您都需要知道如何解释返回值,因为它可能是id,也可能是标量。

我不相信在最一般的情况下(例如,如果类实现forwardingTargetForSelector:)在运行时确定选择器的返回类型是可能的。您确实应该在编译时从头文件中获取此信息。但在大多数情况下,您可以使用class_getPropertyproperty_getAttributesclass_getInstanceMethodmethod_getReturnType 在运行时确定返回值(您需要同时检查两者;没有规定开发人员必须声明“类似属性的东西” @property)。

虽然可以获得指向方法实现的实际 C 函数指针,但通常不应从对象外部执行此操作。 ObjC 对象有许多方法可以处理不会转换为“调用具有该名称的方法”的消息。您应该传递消息并让对象自己处理。

但是对于读取和写入属性,Nathan 的 KVC 方法可能就是您所需要的。不过,我真的建议您再看看 PyObjC,因为他们已经解决了这些问题。至少,我会非常仔细地研究他们的代码。

【讨论】:

  • 我解决了 Nathan 的 KVC 方法的问题,它现在可以工作了 :)
【解决方案2】:

您可以使用 NSObject 方法间接调用 setter 和 getter 方法

- [NSObject setValue:(id) forKey:(NSString *)];
- (id)[NSObject valueForKey:(NSString *)];

其中键是属性名称,如果属性是原始类型,如 unsigned long、double 等。那么 setValue 和 value 方法将处理 NSNumber 等价物。

您还可以像调用其他方法一样调用属性 setter 和 getter,因此您可以使用常规方法可以使用的所有动态方法调用功能,您可能已经解决了这个问题。

【讨论】:

  • 有什么方法可以通过 objc 运行时获取某些属性的 getter 和 setter 的实际 IMP?
  • 如果您不在某处实现它们,则不会;如果方法不存在,KVC 机制将退回到直接访问。如果它们被实现——直接、通过综合或在运行时生成——那么它们的行为将与任何其他方法一样。
猜你喜欢
  • 2012-03-04
  • 2011-09-02
  • 2015-05-31
  • 2013-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-03
  • 1970-01-01
相关资源
最近更新 更多