我一直在尝试使用看起来像very clever KVO pattern的东西来解析一个可以作为上下文传递的Method指针的选择器。
模式部分的最后一部分给了我一些麻烦:
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
Method m = (Method)context;
(void(*)(id,SEL,id,id,id))(m->method_imp)(owner, m->method_name, keyPath, object, change);
}
特别是解除引用方法指针并且看似调用的部分。
(void(*)(id,SEL,id,id,id))(m->method_imp)(owner, m->method_name, keyPath, object, change);
我收到此编译错误error: dereferencing pointer to incomplete type
我是Objective-C和C的新手,有两个问题:
虽然我不明白,但我觉得这句话可以分成两行或更多行,以便更具可读性。如果这是真的,我很想看看它的外观。
答案 0 :(得分:2)
它可能与导入Objective-C运行时标头一样简单:
#import <objc/runtime.h>
然后编译器知道Method
是什么,并且您将能够成功解除引用它。
(void(*)(id,SEL,id,id,id))(m->method_imp)
将m->method_imp
转换为指向(void)函数的指针,其参数类型为id,SEL,id,id,id
。每个Objective-C方法实际上都是一个C函数,第一个参数是指向对象的指针(可以使用self
访问),第二个参数是选择器(可以通过_cmd
访问) )遵循正常方法的参数。
因此,编译器现在断言你有这个函数有几个参数,所以你可以使用普通的括号来调用这个函数。第一个参数表示对象,第二个参数表示选择器,然后是所有其他参数。
要了解有关它的更多信息,请搜索函数指针并了解Objective-C运行时的工作原理。
实质上,您也可以使用以下代码:
objc_msgSend(owner, m->method_name, keyPath, object, change);
但是,没有必要获得实例方法。现在,你唯一需要的方法就是你已经拥有的选择器。这意味着,您可以直接使用SEL
作为上下文参数,然后使用代码:
objc_msgSend(owner, (SEL)context, keyPath, object, change);
关于IMP
的内容,m->method_imp
定义为:
在<objc/objc.h>
中,IMP
的定义如下:
typedef id (*IMP)(id, SEL, ...);
有点奇怪的语法,因为它不清楚哪一个是类型,哪一个是定义。好吧,在这种情况下,只有IMP
是定义,其余是id (*)(id, SEL, ...)
类型。要了解更多相关信息,请查找一些函数指针文章。
因此,它是一个指向函数的指针,该函数返回一个对象(id
)并接受多个参数:默认的Objective-C,可能还有一些(由...
表示,这意味着可能是其他参数)