I've been trying to employ what looks like a very clever KVO pattern that resolves a selector to a Method pointer that can be passed as the context.
The last part of the part of the pattern is giving me some troubles:
- (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);
}
Specifically the part where the Method pointer is dereferenced and seemingly invoked.
(void(*)(id,SEL,id,id,id))(m->method_imp)(owner, m->method_name, keyPath, object, change);
I get this compile error error: dereferencing pointer to incomplete type
I'm very new to Objective-C and C and have two questions:
- What is the above syntax called? I'd like to read more about it.
- How do I fix that line to work?
Though I don't understand it I get the feeling that the statement could be split into two or more lines to be a little more readable. If that's true, I'd love to see how that might look.
It may be as simple as importing the Objective-C runtime headers:
#import <objc/runtime.h>
Then the compiler knows what Method
is and you'll be able to successfully dereference it.
(void(*)(id,SEL,id,id,id))(m->method_imp)
casts m->method_imp
to be a pointer to a (void) function, with arguments of type id,SEL,id,id,id
. Every Objective-C method is actually a C function, with the first argument being the pointer to the object (which you can access by using self
), and the second the Selector (which is accessible by _cmd
) following the normal method's parameters.
Thus, the compiler now asserts that you have this function with a couple of arguments, so you are able to call this function by using normal parenthesis. The first argument representing the object, the second the Selector, and then all the others.
To read more about it, please search for function pointers and learn how the Objective-C runtime works.
In essence, you could also have used the following code:
objc_msgSend(owner, m->method_name, keyPath, object, change);
But then, it wouldn't have been necessary to get the instance method. Now, the only thing you need from the method is the selector, which you already had. This means, you could've used the SEL
directly as the context argument, and then using the code:
objc_msgSend(owner, (SEL)context, keyPath, object, change);
Something about IMP
then, which m->method_imp
is defined as:
In <objc/objc.h>
, IMP
has been defined as follows:
typedef id (*IMP)(id, SEL, ...);
A bit of a weird syntax since it's not really clear which one is the type and which one is the definition. Well, only IMP
is the definition in this case, the rest id (*)(id, SEL, ...)
the type. To learn more about this, find some function-pointers article.
So, it is a pointer to a function that returns an object (id
) and takes multiple arguments: the default Objective-C ones, and probably some more (denoted by ...
, which means there may be additional arguments)
精彩评论