Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BOOL encoding wrong from NSMethodSignature

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?

like image 258
hris.to Avatar asked Oct 20 '14 06:10

hris.to


2 Answers

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.

like image 123
gabbler Avatar answered Oct 23 '22 11:10

gabbler


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");
    }
}
like image 39
hris.to Avatar answered Oct 23 '22 13:10

hris.to