Почему NSInvocation getReturnValue: потерять объект? - PullRequest
3 голосов
/ 16 августа 2011

Мне нужна твоя помощь. У меня есть некоторые проблемы с NSInvocation 'getReturnValue:' метод. Я хочу создать UIButton программно, и даже больше, я хочу создать его динамически, используя NSInvocation и передавая значение через NSArray (поэтому я обернул UIButtonTypeRoundedRect).

Листинг.

NSLog(@"Button 4 pushed\n");//this code executed when button pushed
Class cls = NSClassFromString(@"UIButton");//if exists {define class},else cls=nil
SEL msel = @selector(buttonWithType:);
//id pushButton5 = [cls performSelector:msel withObject:UIButtonTypeRoundedRect];//this code works correctly,but I want to do this by NSInvocation
//---------------------------
NSMethodSignature *msignatureTMP;
NSInvocation *anInvocationTMP;

msignatureTMP = [cls methodSignatureForSelector:msel];
anInvocationTMP = [NSInvocation invocationWithMethodSignature:msignatureTMP];
[anInvocationTMP setTarget:cls];
[anInvocationTMP setSelector:msel];

UIButtonType uibt_ = UIButtonTypeRoundedRect;
NSNumber *uibt = [NSNumber numberWithUnsignedInt:uibt_];
NSArray *paramsTMP;
paramsTMP= [NSArray arrayWithObjects:uibt,nil];

id currentValTMP = [paramsTMP objectAtIndex:0];//getParam from NSArray
NSInteger i=2;
void* bufferTMP;

//if kind of NSValue unwrapp it.
if ([currentValTMP isKindOfClass:[NSValue class]]) {
    NSUInteger bufferSize = 0;
    NSGetSizeAndAlignment([currentValTMP objCType], &bufferSize, NULL);
    bufferTMP = malloc(bufferSize);
    [currentValTMP getValue:bufferTMP];//copy currentVal to bufer
    [anInvocationTMP setArgument:bufferTMP atIndex:i];// The +2 represents the (self) and (cmd) offsets
}else {
    [anInvocationTMP setArgument:&currentValTMP atIndex:i];//Again,+2 represents (self) and (cmd) offsets
}
void* result = malloc([[cls methodSignatureForSelector:msel] methodReturnLength]);
[anInvocationTMP invoke];
[anInvocationTMP getReturnValue:result];

NSLog(@"sizeof(UIButton)=%i,sizeof(result)=%i,methodreturnlength = %i,sizeof(*result)=%i",class_getInstanceSize(NSClassFromString(@"UIButton")),sizeof(result),[[cls methodSignatureForSelector:msel] methodReturnLength],sizeof(*result));

id pushButton5;
pushButton5=result;
//---------------------------

Вывод NSLog: sizeof (UIButton) = 140, sizeof (результат) = 4, методturnlelength = 4, sizeof (* результат) = 1

Проблема в том, что значение из NSInvocation является указателем размера 4 байта. Он должен указывать на объект UIButton размером 140 байт. Но на самом деле относится к 1-байтовым данным. Так что же происходит с объектом UIButton, который должен быть инициализирован с помощью 'buttonWithType:'?

Добавлено после получения некоторых ответов:

Чтобы уточнить: я хочу получить UIButton объект, но после этого кода id pushButton5 = (id) result;, когда я пытаюсь работать с pushButton5, он вызывает EXC_BAD_ACCESS. Кто-нибудь может мне помочь?

Может ли это произойти из-за этого?

Class cls = NSClassFromString(@"UIButton");
...
[anInvocationTMP setTarget:cls];

Это правильно, не правда ли?

Ответы [ 5 ]

18 голосов
/ 20 июля 2012

Если вы используете ARC, я бы заменил это:

void* result = malloc([[cls methodSignatureForSelector:msel] methodReturnLength]);
[anInvocationTMP invoke];
[anInvocationTMP getReturnValue:result];

С этим:

CFTypeRef result;
[anInvocationTMP invoke];
[anInvocationTMP getReturnValue:&result];
if (result)
    CFRetain(result);
UIButton *pushButton5 = (__bridge_transfer UIButton *)result;

Причина в том, что возвращаемый объект вызова не сохраняется, поэтому он исчезнет, ​​даже если вы немедленно назначите его для ссылки на объект, если только вы сначала не сохраните его, а затем не сообщите ARC о передаче права собственности.

1 голос
/ 16 августа 2011

result имеет тип void*, а ваше sizeof(*result) выражение измеряет sizeof(void), что, по-видимому, дает 1 в вашем компиляторе.

Чтобы проверить тип объекта Objective C, используйте метод isKindOfClass::

id resObj = (id)result;
if ([resObj isKindOfClass:[UIButton class]])
    NSLog(@"mazel tov, it's a button");

Только сначала убедитесь, что это действительно объект-цель.

0 голосов
/ 17 августа 2011

Действительно ответ, что мне нужно было ... Как вы думаете, где? Да, в документации.

  • Используйте метод NSMethodSignature methodReturnLength, чтобы определить размер, необходимый для буфера:

    NSUInteger length = [[myInvocation methodSignature] methodReturnLength];
    buffer = (void *)malloc(length);
    [invocation getReturnValue:buffer];
    
  • Когда возвращаемое значение является объектом, передайте указатель на переменную (или память), в которую должен быть помещен объект:

    id anObject;
    NSArray *anArray;
    [invocation1 getReturnValue:&anObject];
    [invocation2 getReturnValue:&anArray];
    

Итак, проблема решена. Спасибо за ваш ответ, ребята.

0 голосов
/ 16 августа 2011

Это не проблема.

Во-первых, getReturnValue: должно прийти после вызова.Итак,

[anInvocationTMP getReturnValue:result];
[anInvocationTMP invoke];

должно быть

[anInvocationTMP invoke];
[anInvocationTMP getReturnValue:result];

Далее следует забыть о размере UIButton.buttonWithType возвращает UIButton*, а не UIButton.В Objective-C вы никогда не должны напрямую иметь дело с самим объектом.Вы всегда должны работать с указателем на объект.

Наконец, оператор sizeof (Objective-) C является чисто операцией времени компиляции.Итак, sizeof(*result) вообще не знает, на что указывает result во время выполнения.Но это не имеет значения ... вам не нужно заботиться о размере UIButton, как я уже говорил вам.

0 голосов
/ 16 августа 2011

Возвращаемое значение - UIButton *, а не UIButton. Таким образом, все выглядит хорошо в вашем коде.

...