来自 NSMethodSignature 的 BOOL 编码错误

Posted

技术标签:

【中文标题】来自 NSMethodSignature 的 BOOL 编码错误【英文标题】:BOOL encoding wrong from NSMethodSignature 【发布时间】:2014-10-20 06:38:32 【问题描述】:

我在使用 [NSMethodSignature getArgumentTypeAtIndex] 函数时遇到了非常奇怪的行为。根据objective-c type encodings,它返回错误的BOOL类型的'@'字符。如果我使用 objc\runtime.h 库方法 method_getTypeEncoding BOOL 类型正确表示为“B”,但是我不明白为什么它不能与更高级别的层 NSMethodSignature 一起使用。下面的代码演示了这个问题:

-(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

上面令人惊讶的(至少对男性来说)打印出以下内容:

来自 NSMethodSignature 的 BOOL arg:@

来自 objc/runtime.h 的 BOOL arg:B

我目前正在使用自己的实现来避免这种奇怪的行为,但是我想知道我是否遗漏了某些东西或者这只是一个错误。我唯一的线索是 BOOL 是原始的,像这样在调用objective-c方法时不能直接使用它。但是,当我尝试检查它时 [object isKingOfClass:[NSNumber class]] 返回 NO。

更新

好的,我已将 XCode 更新到最新版本(6.1 6A1052d),情况大大改善。但是我现在的问题是将无符号字符编码与真正的布尔编码区分开来。我知道在旧版本中 BOOL 是 typedef 作为 char,但我如何才能完成真正的 char vs BOOL 编码?我现在的结果是:

模拟器 iPhone6 和真机 iPhone6 我收到了:

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

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

这很棒,但是对于 模拟器 iPhone4s 和真实设备 iPhone5 我得到了:

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

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

我很确定,如果我检查 iPhone5s,它会得到与 iPhone6 相同的输出(因为我认为这都是关于 64 位架构的)。所以我现在的问题是如何正确解决旧设备,如何区分它们的 BOOL 字符?或者我应该假设如果编码是'c'并且参数等于'1'我们有YES,而对于'0'我们有NO?

【问题讨论】:

我无法回答您的问题,但是我不确定 ARC 将如何处理 [inv methodSignature] 返回的对象,您可以从中获得 const char *。显然,该缓冲区在对象域内(在其管理下),所以我想您不应该创建一个临时对象并以这种方式取消引用它。 由于 methodSignature 是 ObjectiveC 类,我很确定它在 ARC 下可以正常工作。然而问题的重点是为什么它返回错误的值。 嗯,使用临时对象可能会导致问题。请尝试使用非临时变量。 不,使用属性存储编码也没有帮助。仍在寻找答案。我还注意到 const char* 类型被编码为 'r*' 没有记录(char* 确实被编码为 '*',所以我猜 'r' 在这种情况下代表 const。 【参考方案1】:
char *buf1 = @encode(BOOL);
NSLog(@"bool type is: %s", buf1);

在 32 位模拟器上,encode(BOOL) 返回“c”,而在 64 位模拟器上,它返回“B”。在 Build Settings 中,将 Architectures 更改为 $(ARCHS_STANDARD_32_BIT),然后它将为您返回 'c'。

在 objc/objc.h 文件中。

#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

32 位的 BOOL 是有符号字符的类型。您可以将其与 unsigned char 区分开来。

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

你可以从here判断设备是32位还是64位,如果是32位,'c'对BOOL检查有效。

【讨论】:

好点!根据您的回答,我找到了解决方案。在第一篇文章中查看我的更新。 我看过了,很高兴它有帮助。【参考方案2】:

根据@gabbler 的回答,我设法检查参数是否为布尔值。以下代码适用于 32 位和 64 位架构:

-(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");
    

【讨论】:

【参考方案3】:

我在调试器中的 po [inv methodSignature] 上得到了以下信息:

(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

我用 NSLog 打印出来了

来自 NSMethodSignature 的 BOOL arg:c

我无法写评论,所以我写了这个答案。你能po你的[inv methodSignature]吗?!您能否在分配给 encFromGetArgument 后直接输出您的 NSLog

【讨论】:

以上是关于来自 NSMethodSignature 的 BOOL 编码错误的主要内容,如果未能解决你的问题,请参考以下文章

什么是ObjCTypes?

带 bo​​ost asio 的多线程服务器

iOS runtime forwardInvocation一些总结

来自 ArrayList 类的列表视图

PHP:检查ID是来自Vimeo还是YouTube

Django 使用来自 Ajax 的成功数据更新模板 div