Как вернуть значение структуры из метода класса, определенного во время выполнения, в ARC? - PullRequest
8 голосов
/ 06 декабря 2011

У меня есть метод класса, возвращающий CGSize, и я хотел бы вызвать его через функции времени выполнения Objective C, потому что мне даны имена классов и методов в виде строковых значений.

Я компилирую с флагами ARC в XCode 4.2.

Подпись метода:

+(CGSize)contentSize:(NSString *)text;

Первым делом я попытался вызвать его с помощью objc_msgSend следующим образом:

Class clazz = NSClassFromString(@"someClassName);
SEL method = NSSelectorFromString(@"contentSize:");

id result = objc_msgSend(clazz, method, text);

Это сбой с "EXC_BAD_ACCESS" и без трассировки стека. Я использовал это сначала, потому что документация для objc_msgSend гласит,

Когда он сталкивается с вызовом метода, компилятор генерирует вызов одной из функций objc_msgSend, objc_msgSend_stret, objc_msgSendSuper или objc_msgSendSuper_stret. [...] Методы, которые имеют структуры данных в качестве возвращаемых значений, отправляются с использованием objc_msgSendSuper_stret и objc_msgSend_stret.

Далее я использовал objc_msgSend_stret вот так:

Class clazz = NSClassFromString(@"someClassName);
SEL method = NSSelectorFromString(@"contentSize:");

CGSize size = CGSizeMake(0, 0);
objc_msgSend_stret(&size, clazz, method, text);

Использование вышеуказанной подписи дает мне следующие две ошибки компилятора и два предупреждения:

ошибка: автоматический подсчет ссылок Проблема: неявное преобразование типа указателя не Objective C 'CGSize *' (он же struct CGSize * ') в' id 'запрещено с помощью ARC

предупреждение: семантическая проблема: несовместимые типы указателей, передающие «CGSize *» (он же «struct CGSize *») параметру типа «id»

ошибка: автоматический подсчет ссылок. Проблема: неявное преобразование указателя Objective C в 'SEL' запрещено с помощью ARC

предупреждение: семантическая проблема: передача несовместимых типов указателей '__unsafe_unretained Class' для параметра типа 'SEL'

Если я посмотрю на объявление метода, это:

OBJC_EXPORT void objc_msgSend_stret(id self, SEL op, ...)
    __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);

, что идентично objc_msgSend:

OBJC_EXPORT id objc_msgSend(id self, SEL op, ...)
    __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);

Это объясняет мне ошибки компилятора, но что я использую на уровне времени выполнения, чтобы вызывать класс и его статический метод во время выполнения и возвращать значение структуры?

Ответы [ 2 ]

16 голосов
/ 07 декабря 2011

Вам необходимо привести objc_msgSend_stret к правильному типу указателя на функцию. Он определен как void objc_msgSend_stret(id, SEL, ...), что является неподходящим типом для фактического вызова. Вы хотите использовать что-то вроде

CGSize size = ((CGSize(*)(id, SEL, NSString*))objc_msgSend_stret)(clazz, @selector(contentSize:), text);

Здесь мы просто приводим objc_msgSend_stret к функции типа (CGSize (*)(id, SEL, NSString*)), которая является фактическим типом IMP, который реализует +contentSize:.

Обратите внимание, мы также используем @selector(contentSize:), потому что нет никакой причины использовать NSSelectorFromString() для селекторов, известных во время компиляции.

Также обратите внимание, что приведение указателя функции требуется даже для регулярных вызовов objc_msgSend(). Даже если вызов objc_msgSend() напрямую работает в вашем конкретном случае, он полагается на предположение, что ABI-вызов метода varargs совпадает с ABI для вызова метода не-varargs, что может быть не верно для всех платформ.

7 голосов
/ 07 декабря 2011

Если вы хотите получить структуру из вашего метода класса, вы можете использовать NSInvocation следующим образом:

Class clazz = NSClassFromString(@"MyClass");
SEL aSelector = NSSelectorFromString(@"testMethod");

CGSize returnStruct; // Or whatever type you're retrieving

NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[clazz methodSignatureForSelector:aSelector]];

[invocation setTarget:clazz];
[invocation setSelector:aSelector];

[invocation invoke];
[invocation getReturnValue:&returnStruct];

В конце этого, returnStruct должно содержать ваше значение структуры. Я только что проверил это в приложении с поддержкой ARC, и это прекрасно работает.

...