8

Итак, я родом из мира Java, где мы блаженно игнорируем проблемы управления памятью. По большей части ARC спас мою задницу, но вот что-то, что меня озадачило. В основном я использую NSInvocations для некоторых вещей, и я столкнулся с некоторыми неприятными проблемами памяти, прежде чем я сделал следующие модификации кода. Поскольку я сделал эти изменения, сбой памяти исчез, но я, как правило, очень боюсь кода, который я не понимаю. Правильно ли я это делаю?Вопросы NSInvocation и памяти

Перед: всякие проблемы с памятью:

NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[target class] instanceMethodSignatureForSelector:selector]]; 
[invocation setSelector:selector]; 
[invocation setTarget:target]; 
[invocation setArgument:&data atIndex:2]; 
[invocation setArgument:&arg atIndex:3]; 
[invocation invoke]; 

NSString *returnValue; 
[invocation getReturnValue:&returnValue]; 

После: Нет проблем с памятью, но я не уверен, что я получил это право:

NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[target class] instanceMethodSignatureForSelector:selector]]; 
[invocation setSelector:selector]; 
[invocation setTarget:target]; 
[invocation setArgument:&data atIndex:2]; 
[invocation setArgument:&arg atIndex:3]; 
[invocation invoke]; 

CFTypeRef result; 
[invocation getReturnValue:&result]; 

if (result) 
    CFRetain(result); 

NSString *returnValue = (__bridge_transfer NSString *)result; 

Edit:

I Just хотел добавить на основе ответа ниже, я использовал objc_msgSend, как таковой .:

NSString * returnValue = objc_msgSend(target, selector, data, arg); 

И он решает все проблемы с памятью, а также выглядит намного проще. Прокомментируйте, если вы видите какие-либо проблемы с этим.

+2

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

+0

Селектор - это простой метод для целевого класса. Принимает 2 объекта и возвращает строку. Проблемы различны - в основном плохой доступ. – user2453876

ответ

5

Я отвечу на ваш вопрос следующим образом: Не используйте NSInvocation. Это просто дружеский совет, чтобы избежать этого , если возможно.

Есть много хороших способов сделать обратные вызовы в Objective-C, вот два, которые могут быть полезны для вас:

  • Блоки: Определенно в контексте выбрать любое количество аргументов и тип, возможные проблемы с памятью тоже. Есть много ресурсов о том, как их использовать.
  • performSelector: аргументы не более 2 объекта, вызывается с помощью:

    [target performSelector:selector withObject:data withObject:args]; 
    

Кроме того, когда мне нужно вызвать селектор с 4-мя аргументами я до сих пор не используют NSIvocation, а скорее позвоните по номеру objc_msgSend:

id returnValue = objc_msgSend(target, selector, data, /* argument1, argument2, ... */); 

Просто.

Edit: С objc_msgSend вы должны быть осторожны с возвращаемым значением. Если ваш метод возвращает объект, используйте приведенное выше. Если он возвращает примитивный тип, вам необходимо использовать метод objc_msgSend, чтобы компилятор знал, что происходит (see this link). Вот пример для метода, который принимает один аргумент и возвращает BOOL:

// Cast the objc_msgSend function to a function named BOOLMsgSend which takes one argument and has a return type of BOOL. 
BOOL (*BOOLMsgSend)(id, SEL, id) = (typeof(BOOLMsgSend)) objc_msgSend; 
BOOL ret = BOOLMsgSend(target, selector, arg1); 

Если метод возвращает-структуру, все немного сложнее. Вы может (но не всегда) необходимо будет использовать objc_msgSend_stret - see here for more info.

Edit: - эта линия должна быть добавлена ​​к коду, или Xcode будет жаловаться:

#import <objc/message.h> 

или

@import ObjectiveC.message; 
+0

Я знаю о блоках, но не об objc_msgSend. Как objc_msgSend лучше, чем NSInvocation? – user2453876

+0

@ user2453876 Меньше кода, проще, без проблем с памятью. 'objc_msgSend' - это методы реализации Objective-C. Каждый вызов метода '[target selector: arg]' переводится в 'objc_msgSend (target, selector, arg)' – Tricertops

+0

@iMartin: objc_msgSend() или objc_msgSend_fpret() или objc_msgSend_stret() ... –

4

Как правило, вы должны рассматривать блоки как превосходную альтернативу (они преуспели NSInvocation).

Что касается возвращаемого значения, вы можете использовать это:

CFTypeRef result = NULL; 
[invocation getReturnValue:&result];  
NSString *returnValue = (__bridge NSString *)result; 

Основной проблемой здесь является то, что -getReturnValue: не возвращает из объект, насколько ARC обеспокоен. Поэтому вероятно, что операции ссылочного счета ошибочны (компилятор добавляет их для вас в ARC), потому что параметр -getReturnValue: - это void*, а не внешний объект (например, NSObject**).

+1

Сравните ответ rob mayoff на «возможный дубликат»: '__unsafe_unretained NSString * result' кажется элегантным решением. –

+0

@MartinR право, это решение также хорошо +1. спасибо – justin

+1

Спасибо, ребята, - видимо, используя obj_msgSend, как в ответе выше, решены все мои проблемы, что тоже с 1 строкой кода. – user2453876