5

I've encountered really odd behaviour when using [NSMethodSignature getArgumentTypeAtIndex] function. It returns me '@' character for BOOL type which in wrong according to objective-c type encodings. If I use objc\runtime.h library method method_getTypeEncoding BOOL type is correctly represented as a 'B', however I don't understand why it's not working with higher level layer NSMethodSignature. Following code demonstrates the problem:

-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    NSInvocation* inv = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(viewDidAppear:)]];
    const char* encFromGetArgument = [[inv methodSignature] getArgumentTypeAtIndex:2];

    const char* encFromMethodSignature = method_getTypeEncoding(class_getInstanceMethod([self class], @selector(viewDidAppear:)));;
    const char* methodEncodingPure = [[[[NSString stringWithUTF8String:encFromMethodSignature] componentsSeparatedByCharactersInSet:[NSCharacterSet decimalDigitCharacterSet]] componentsJoinedByString:@""] UTF8String];//remove stack sizes


    NSLog(@"BOOL arg from NSMethodSignature: %s", encFromGetArgument);
    NSLog(@"BOOL arg from objc/runtime.h: %c", methodEncodingPure[3]);//first type is for return, second is target, third is selector
}

The above surprisingly(at least for men) prints the following:

BOOL arg from NSMethodSignature: @

BOOL arg from objc/runtime.h: B

I'm currently using my own implementation to avoid this odd behaviour however I want to know if I'm missing something or it's just a bug. My only clue is that BOOL is primitive and like this it cannot be used directly when calling objective-c methods. However when I try to check it [object isKingOfClass:[NSNumber class]] returns NO.

UPDATE

Ok I've update XCode to the latest version(6.1 6A1052d) and situation greatly improves. However my problem now is to distinguish unsigned char encoding from real bool encoding. I know in old versions BOOL is typedef as a char, but how I can accomplish real char vs BOOL encoding? My results now are:

For Simulator iPhone6 and real device iPhone6 I received:

argument 2: -------- -------- -------- --------
        type encoding (B) 'B'
        flags {}

BOOL arg from NSMethodSignature: B
BOOL arg from objc/runtime.h: B

which is awesome, however for simulator iPhone4s, and real device iPhone5 I'm getting:

argument 2: -------- -------- -------- --------
        type encoding (c) 'c'
        flags {isSigned}

BOOL arg from NSMethodSignature: c
BOOL arg from objc/runtime.h: c

I'm pretty sure that if I check iPhone5s it'll get the same output as iPhone6(as I think it's all about the 64-bit architecture). So my question now is how to properly workaround older devices, how to distinguish char of BOOL for them? Or should I just assume that if encoding is 'c' and argument is equal to '1' we have YES, and for '0' we have NO?

Slipp D. Thompson
  • 33,165
  • 3
  • 43
  • 43
hris.to
  • 6,235
  • 3
  • 46
  • 55
  • I cannot answer you question, however I'm not sure what ARC will do with the object returned by `[inv methodSignature]` from which you get a `const char *` from. Clearly that buffer is within the objects domain (under its management) and so I would imagine you should not be creating a temporary object and de-referencing it in this way. – Droppy Oct 20 '14 at 07:40
  • As methodSignature is ObjectiveC class I'm pretty sure it'll work fine under ARC. However main point of the question is why it is returning wrong values. – hris.to Oct 21 '14 at 06:24
  • Well it's possible the use of a temporary object is causing the issue. Please try it using variables that are not temporary. – Droppy Oct 21 '14 at 06:44
  • Nope, using property to store encoding didn't help also. Still seeking for the answer. Also I noticed that type const char* is encoded as 'r*' which is not documented(char* is indeed encoded as '*', so I guess 'r' stands for const in this case. – hris.to Oct 22 '14 at 06:23

3 Answers3

5
char *buf1 = @encode(BOOL);
NSLog(@"bool type is: %s", buf1);

On 32bit simulator, encode(BOOL) returns 'c', while on 64 bit, it returns a 'B'. In Build Settings, change Architectures to $(ARCHS_STANDARD_32_BIT), then it will returns 'c' for you.

in the objc/objc.h file.

#define OBJC_BOOL_DEFINED

/// Type to represent a boolean value.
#if !defined(OBJC_HIDE_64) && TARGET_OS_IPHONE && __LP64__
typedef bool BOOL;
#else
typedef signed char BOOL; 
// BOOL is explicitly signed so @encode(BOOL) == "c" rather than "C" 
// even if -funsigned-char is used.
#endif

BOOL in 32bit is a typeof signed char. You can distinguish it from unsigned char.

@encode(char) --> 'c'
@encode(unsigned char) --> 'C'

You can tell a device is in 32bit or 64bit from here,and if it is in 32 bit, 'c' is valid for BOOL check.

Community
  • 1
  • 1
gabbler
  • 13,626
  • 4
  • 32
  • 44
3

Based on the @gabbler answer I've managed to check if argument is bool or not. The following code works for both 32 and 64bit architecture:

-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    NSInvocation* inv = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(viewDidAppear:)]];
    const char* encFromGetArgument = [[inv methodSignature] getArgumentTypeAtIndex:2];

    if( 0 == strcmp(@encode(BOOL), encFromGetArgument) )
    {
        //BOOL val
        NSLog(@"arg is bool");
    }
    else
    {
        //not BOOL
        NSLog(@"arg is not bool");
    }
}
hris.to
  • 6,235
  • 3
  • 46
  • 55
2

I got the following out on po [inv methodSignature] in the debugger:

(lldb) po [inv methodSignature]

<NSMethodSignature: 0x7be7f660>
    number of arguments = 3
    frame size = 12
    is special struct return? NO
    return value: -------- -------- -------- --------
        type encoding (v) 'v'
        flags {}
        modifiers {}
        frame {offset = 0, offset adjust = 0, size = 0, size adjust = 0}
        memory {offset = 0, size = 0}
    argument 0: -------- -------- -------- --------
        type encoding (@) '@'
        flags {isObject}
        modifiers {}
        frame {offset = 0, offset adjust = 0, size = 4, size adjust = 0}
        memory {offset = 0, size = 4}
    argument 1: -------- -------- -------- --------
        type encoding (:) ':'
        flags {}
        modifiers {}
        frame {offset = 4, offset adjust = 0, size = 4, size adjust = 0}
        memory {offset = 0, size = 4}
    argument 2: -------- -------- -------- --------
        type encoding (c) 'c'
        flags {isSigned}
        modifiers {}
        frame {offset = 8, offset adjust = 0, size = 4, size adjust = -3}
        memory {offset = 0, size = 1}

There for I got printed out with NSLog

BOOL arg from NSMethodSignature: c

I could not write an comment, so I wrote this answer. Could you please po your [inv methodSignature] ?! And could you please output your NSLog directly after it is assigned to encFromGetArgument

Kordi
  • 2,405
  • 1
  • 14
  • 13