문제

I have been searching around for this for a few good hours.

It doesn't seem like it has been asked before.

Does anyone know how to convert a C function pointer that is pointing an Objective-C method to a SEL type (@selector)?


Update:

I was just playing around with runtime:

Method method = class_getInstanceMethod([self class], @selector(test:));
IMP objectMethod = method_getImplementation(method);
objectMethod(self, @selector(<???>), [self superclass]);

And I somehow began to remember reading from somewhere that SEL (@selector) is only a pointer. So out of curiosity I thought, if I could set a pointer pointing to an Objective-C method, could I replace (SEL) @selector() with this pointer to a function since they are both just pointers?

도움이 되었습니까?

해결책

@selector is not a method, it is a, well, selector, which helps the Objective C runtime select a method. Methods in Objective C are C functions with at least two parameters, which are required: id self, which represents the object and SEL selector which represents the selector that was used to call the function. If the method was defined with more parameters, they come after the two mandatory parameters. This function is called an IMP, implementation. Each Objective C method has a backing IMP.

IMP is defined like so:

typedef id (*IMP)(id, SEL, ...);

If you have a C function, and it has the correct parameter layout, you can assign that function to an existing, or entirely new method, and add to a class as an instance or class method. If your C function does not have the required parameter layout, you are looking at a crash. Make sure the C function is correct.

So let's assume the C function:

void func_name(id self, SEL sel, NSUInteger x)
{
    NSLog(@"I was called on object %@ with selector %@ and argument %lu", self, NSStringFromSelector(sel), x);
}

And wish to assign it to -[NSObject myNewMethodWithUnsignedInteger:], you would do it like so using the runtime:

class_addMethod([NSObject class], @selector(myNewMethodWithUnsignedInteger:), (IMP)func_name, [NSString stringWithFormat:@"v@:%s", @encode(NSUInteger)]);

Make sure to read the documentation of class_addMethod here.

If your C function is not in the required format, you can use imp_implementationWithBlock to wrap the call of your function inside a block.

So let's assume you have the following function:

void nonCompliantFunc(NSUInteger x)
{
    NSLog(@"Can't touch this!");
}

You could add it as a method like so:

IMP imp = imp_implementationWithBlock(^ (id self, NSUInteger x) { nonCompliantFunc(x); };
class_addMethod([NSObject class], @selector(myNewMethodWithUnsignedInteger:), imp, [NSString stringWithFormat:@"v@:%s", @encode(NSUInteger)]);

You should check the return value of class_addMethod to make sure the method was added successfully.


To answer your update, selectors are pointers, but they are pointers in a method table. All selectors are collected when the runtime is loaded, and a table is built. Then, when each class is loaded, a method table is set up, pairing selector and implementation. This is a lot more complex, but this is the gist of it.

The pointer you are thinking of is the one obtained using method_getImplementation(), but you cannot pass that instead of selectors.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top