7

我想编写一个像 NSLog() 这样的函数或指令,它接受任何类型的变量、原语和对象。在那个功能中,我想区分这些。

我知道它对对象的工作原理:

- (void)test:(id)object {
    if ([object isKindOfClass:[NSString class]])
        ...

但是我如何区分对象与结构甚至整数或浮点数。就像是:

"isKindOfStruct:CGRect" or "isInt" 

例如?

这可能吗?我想既然您可以将所有内容发送到 NSLog(@"...", objects, ints, structs) 它一定是可能的吗?

谢谢你的帮助!

编辑

我的最终目标是实现某种多态性。

我希望能够调用我的函数:

MY_FUNCTION(int)
MY_FUNCTION(CGRect)
MY_FUNCTION(NSString *)
...

or [self MYFUNCTION:int]...

并在 MY_FUNCTION

-(void)MYFUNCTION:(???)value {
    if ([value isKindOf:int])
        ...
    else if ([value isKindOf:CGRect])
        ...
    else if ([value isKindOfClass:[NSString class]])
        ...
 }

我知道 isKindOf 不存在,你甚至不能对原语执行这样的方法。我也不确定“???” 函数头中“值”的通用类型。

那可能吗?

4

6 回答 6

8
#define IS_OBJECT(T) _Generic( (T), id: YES, default: NO)

NSRect    a = (NSRect){1,2,3,4};
NSString* b = @"whatAmI?";
NSInteger c = 9;

NSLog(@"%@", IS_OBJECT(a)?@"YES":@"NO"); // -> NO
NSLog(@"%@", IS_OBJECT(b)?@"YES":@"NO"); // -> YES
NSLog(@"%@", IS_OBJECT(c)?@"YES":@"NO"); // -> NO

另外,请查看 Vincent Gable 的The Most Useful Objective-C Code I've Ever Written,了解一些非常方便的东西,这些东西使用@encode()编译器指令(它)返回一个描述它给出的任何类型的字符串......”

LOG_EXPR(x) 是一个宏,它打印出 x,无论 x 是什么类型,而不必担心格式字符串(以及相关的崩溃,例如,以与 NSString 相同的方式打印 C 字符串)。它适用于 Mac OS X 和 iOS。

于 2013-07-07T03:56:47.350 回答
3

像这样的函数NSLog()可以从作为第一个参数传递的格式字符串中判断其参数列表中期望的类型。因此,您无需查询参数来确定它的类型——您可以根据格式字符串确定您期望的类型,然后相应地解释参数。

于 2012-04-06T15:25:44.487 回答
2

您不能将 C 结构或原语作为 type 的参数传递id。为此,您必须将原语包装在 NSNumber 或 NSValue 对象中。

例如

[self test: [NSNumber numberWithInt: 3.0]];

id被定义为一个指向 Objective-C 对象的指针。

于 2012-04-06T15:31:55.730 回答
2

@alex gray 答案不起作用(或者至少在 iOS SDK 8.0 上不起作用)。您可以使用@deepax11 答案,但是我想指出这个“魔术宏”是如何工作的。它依赖于系统提供的类型编码。根据苹果文档:

为了帮助运行时系统,编译器将每个方法的返回和参数类型编码在一个字符串中,并将该字符串与方法选择器相关联。它使用的编码方案在其他上下文中也很有用,因此可以通过 @encode() 编译器指令公开使用。当给定类型规范时,@encode() 返回一个编码该类型的字符串。该类型可以是基本类型,例如 int、指针、标记结构或联合,或类名——事实上,任何类型都可以用作 C sizeof() 运算符的参数。

为了将宏分开,我们首先获取变量“typeOf”,然后对该类型调用@encode(),最后将返回值与编码表中的“object”和“class”类型进行比较。

完整示例应如下所示:

    const char* myType = @encode(typeof(myVar));//myVar declared somewhere
    if( [@"@" isEqualToString:@(myType)] || [@"#" isEqualToString:@(myType)] )
    {
        //myVar is object(id) or a Class
    }
    else if ( NSNotFound != [[NSString stringWithFormat:@"%s", myType] rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"{}"]].location )
    {
        //myVar is struct
    }
    else if ( [@"i" isEqualToString:@(myType)] )
    {
        //my var is int
    }

请注意,NSInteger 将在 32 位设备上返回 int,在 64 位设备上返回 long。完整的编码列表:

‘c’ - char
‘i’ - int
’s’ - short
‘l’ - long
‘q’ - long long
‘C’ - unsigned char
‘I’ - unsigned int
’S’ - unsigned short
‘L’ - unsigned long
‘Q’ - unsigned long long
‘f’ - float
‘d’ - double
‘B’ - C++ bool or a C99 _Bool
‘v’ - void
‘*’ - character string(char *)
‘@’ - object(whether statically typed or typed id)
‘#’ - class object(Class)
‘:’ - method selector(SEL)
‘[<some-type>]’ - array
‘{<some-name>=<type1><type2>}’ - struct
‘bnum’ - bit field of <num> bits
‘^type’ - pointer to <type>
‘?’ - unknown type(may be used for function pointers)

阅读有关Apple 的类型编码的更多信息

于 2014-10-03T11:24:44.813 回答
1

#define IS_OBJECT(x) ( strchr("@#", @encode( typeof (x))[0]) != NULL ) 这个微工作我在堆栈溢出的某个地方得到了。

于 2014-09-04T09:48:25.100 回答
0

重要的是要注意 id 代表任何 Objective-C 对象。我所说的 Objective-C 对象是指使用@interface 定义的对象。它不代表结构或原始类型(int、char 等)。

此外,您只能将消息([...] 语法)发送到 Objective-C 对象,因此您不能将 isKindOf: 消息发送到普通结构或原语。

但是您可以将整数等转换为 NSNumber,将 char* 转换为 NSString,并将结构包装在 NSObject 派生类中。然后它们将是 Objective-C 对象。

于 2012-04-06T17:24:03.150 回答