I want to write a function or a directive like NSLog() that takes any kind of variable, primitives and objects. In that function I want to distinguish those.
我想写一个函数或者一个像NSLog()这样的指令,它接受任何类型的变量、原语和对象。在这个函数中,我要区分它们。
I know how it works for objects:
我知道它是如何作用于物体的:
- (void)test:(id)object {
if ([object isKindOfClass:[NSString class]])
...
but how do I distinguish objects from structs or even integer or floats. Something like:
但是如何区分对象和结构,甚至是整数或浮点数。喜欢的东西:
"isKindOfStruct:CGRect" or "isInt"
for example?
例如呢?
Is this possible? I thought since you can send everything to NSLog(@"...", objects, ints, structs) it must be possible?
这是可能的吗?我想既然你可以把所有东西都发送到NSLog(@)“,对象,int, structs)它一定是可能的吗?”
Thanks for any help!
感谢任何帮助!
EDIT
编辑
My ultimate goal is to implement some kind of polymorphism.
我的最终目标是实现某种多态性。
I want to be able to call my function:
我想调用我的函数:
MY_FUNCTION(int)
MY_FUNCTION(CGRect)
MY_FUNCTION(NSString *)
...
or [self MYFUNCTION:int]...
and in MY_FUNCTION
而在MY_FUNCTION
-(void)MYFUNCTION:(???)value {
if ([value isKindOf:int])
...
else if ([value isKindOf:CGRect])
...
else if ([value isKindOfClass:[NSString class]])
...
}
I know that isKindOf doesn't exists and you can't even perform such methods on primitives. I'm also not sure about the "???" generic type of "value" in the function header.
我知道isKindOf并不存在,你甚至不能对原语执行这样的方法。我也不确定函数头中的“???”泛型类型的“值”。
Is that possible?
这有可能吗?
6 个解决方案
#1
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
Also, check out Vincent Gable's The Most Useful Objective-C Code I’ve Ever Written for some very handy stuff that uses the @encode()
compiler directive (that) returns a string describing any type it’s given..."
另外,看看Vincent Gable的Objective-C代码吧,这是我写过的最有用的Objective-C代码,它使用@encode()编译器指令返回描述给定类型的字符串。
LOG_EXPR(x) is a macro that prints out x, no matter what type x is, without having to worry about format-strings (and related crashes from eg. printing a C-string the same way as an NSString). It works on Mac OS X and iOS.
LOG_EXPR(x)是一个输出x的宏,不管x是什么类型,都不必担心格式字符串(以及与eg相关的崩溃)。用与NSString相同的方式打印c字符串。它适用于Mac OS X和iOS。
#2
3
A function like NSLog()
can tell what types to expect in its parameter list from the format string that you pass as the first parameter. So you don't query the parameter to figure out it's type -- you figure out what type you expect based on the format string, and then you interpret the parameter accordingly.
像NSLog()这样的函数可以从您作为第一个参数传递的格式字符串中得知它的参数列表中期望的类型。所以你不需要查询参数来确定它的类型——你需要根据格式字符串确定你想要的类型,然后你相应的解释参数。
#3
3
@alex gray answer did not work(or at least did not work on iOS SDK 8.0). You can use @deepax11 answer, however I want to point how this 'magic macro' works. It relies on type encodings provided from the system. As per the Apple documentation:
@alex gray的答案无效(或者至少在iOS SDK 8.0上无效)。您可以使用@deepax11答案,但是我想指出这个“魔法宏”是如何工作的。它依赖于系统提供的类型编码。根据苹果文件:
To assist the runtime system, the compiler encodes the return and argument types for each method in a character string and associates the string with the method selector. The coding scheme it uses is also useful in other contexts and so is made publicly available with the @encode() compiler directive. When given a type specification, @encode() returns a string encoding that type. The type can be a basic type such as an int, a pointer, a tagged structure or union, or a class name—any type, in fact, that can be used as an argument to the C sizeof() operator.
为了帮助运行时系统,编译器在字符串中为每个方法编码返回和参数类型,并将字符串与方法选择器关联。它使用的编码方案在其他上下文中也很有用,因此可以通过@encode()编译器指令公开使用。当给定类型规范时,@encode()返回该类型的字符串编码。类型可以是基本类型,例如int、指针、标记结构或union,或者类名称——任何类型,实际上都可以用作对C sizeof()操作符的参数。
To break the macro apart, we first get "typeOf" our variable, then call @encode() on that type, and finally compare returned value to 'object' and 'class' types from encoding table.
为了将宏分开,我们首先获得“typeOf”变量,然后调用该类型上的@encode(),最后从编码表中将返回的值与'object'和'class'类型进行比较。
Full example should look like:
完整示例应如下:
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
}
Please note that NSInteger will return int on 32-bit devices, and long on 64-bit devices. Full list of encodings:
请注意,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)
Read more about Type Encodings at Apple
在苹果阅读更多类型编码
#4
2
You can't pass a C struct or primitive as a parameter of type id
. To do so, you'll have to wrap the primitive in an NSNumber or NSValue object.
您不能将C结构或原语作为类型id的参数传递。要这样做,您将不得不在NSNumber或NSValue对象中包装原语。
e.g.
如。
[self test: [NSNumber numberWithInt: 3.0]];
id
is defined as a pointer to an Objective-C object.
id被定义为指向Objective-C对象的指针。
#5
1
#define IS_OBJECT(x) ( strchr("@#", @encode(typeof(x))[0]) != NULL ) This micro works which I got somewhere in stack overflow.
定义IS_OBJECT(x) (strchr(“@#”,@encode(类型(x))[0]) !
#6
0
It's important to note that id represents any Objective-C object. And by Objective-C object, I mean one that is defined using @interface. It does not represent a struct or primitive type (int, char etc).
需要注意的是,id表示任何Objective-C对象。Objective-C对象,我指的是用@interface定义的对象。它不表示结构或原始类型(int, char等)。
Also, you can only send messages (the [...] syntax) to Objective-C objects, so you cannot send the isKindOf: message to a normal struct or primitive.
此外,您只能发送消息((语法)对Objective-C对象,所以你不能发送isKindOf:消息到一个正常的结构或原语。
But you can convert a integer etc to a NSNumber, a char* to a NSString and wrap a structure inside a NSObject-dervied class. Then they will be Objective-C objects.
但是您可以将一个整数etc转换成一个NSNumber,一个char*转换成一个NSString,并将一个结构封装到一个nsobjecdervied类中。然后它们就是Objective-C对象。
#1
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
Also, check out Vincent Gable's The Most Useful Objective-C Code I’ve Ever Written for some very handy stuff that uses the @encode()
compiler directive (that) returns a string describing any type it’s given..."
另外,看看Vincent Gable的Objective-C代码吧,这是我写过的最有用的Objective-C代码,它使用@encode()编译器指令返回描述给定类型的字符串。
LOG_EXPR(x) is a macro that prints out x, no matter what type x is, without having to worry about format-strings (and related crashes from eg. printing a C-string the same way as an NSString). It works on Mac OS X and iOS.
LOG_EXPR(x)是一个输出x的宏,不管x是什么类型,都不必担心格式字符串(以及与eg相关的崩溃)。用与NSString相同的方式打印c字符串。它适用于Mac OS X和iOS。
#2
3
A function like NSLog()
can tell what types to expect in its parameter list from the format string that you pass as the first parameter. So you don't query the parameter to figure out it's type -- you figure out what type you expect based on the format string, and then you interpret the parameter accordingly.
像NSLog()这样的函数可以从您作为第一个参数传递的格式字符串中得知它的参数列表中期望的类型。所以你不需要查询参数来确定它的类型——你需要根据格式字符串确定你想要的类型,然后你相应的解释参数。
#3
3
@alex gray answer did not work(or at least did not work on iOS SDK 8.0). You can use @deepax11 answer, however I want to point how this 'magic macro' works. It relies on type encodings provided from the system. As per the Apple documentation:
@alex gray的答案无效(或者至少在iOS SDK 8.0上无效)。您可以使用@deepax11答案,但是我想指出这个“魔法宏”是如何工作的。它依赖于系统提供的类型编码。根据苹果文件:
To assist the runtime system, the compiler encodes the return and argument types for each method in a character string and associates the string with the method selector. The coding scheme it uses is also useful in other contexts and so is made publicly available with the @encode() compiler directive. When given a type specification, @encode() returns a string encoding that type. The type can be a basic type such as an int, a pointer, a tagged structure or union, or a class name—any type, in fact, that can be used as an argument to the C sizeof() operator.
为了帮助运行时系统,编译器在字符串中为每个方法编码返回和参数类型,并将字符串与方法选择器关联。它使用的编码方案在其他上下文中也很有用,因此可以通过@encode()编译器指令公开使用。当给定类型规范时,@encode()返回该类型的字符串编码。类型可以是基本类型,例如int、指针、标记结构或union,或者类名称——任何类型,实际上都可以用作对C sizeof()操作符的参数。
To break the macro apart, we first get "typeOf" our variable, then call @encode() on that type, and finally compare returned value to 'object' and 'class' types from encoding table.
为了将宏分开,我们首先获得“typeOf”变量,然后调用该类型上的@encode(),最后从编码表中将返回的值与'object'和'class'类型进行比较。
Full example should look like:
完整示例应如下:
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
}
Please note that NSInteger will return int on 32-bit devices, and long on 64-bit devices. Full list of encodings:
请注意,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)
Read more about Type Encodings at Apple
在苹果阅读更多类型编码
#4
2
You can't pass a C struct or primitive as a parameter of type id
. To do so, you'll have to wrap the primitive in an NSNumber or NSValue object.
您不能将C结构或原语作为类型id的参数传递。要这样做,您将不得不在NSNumber或NSValue对象中包装原语。
e.g.
如。
[self test: [NSNumber numberWithInt: 3.0]];
id
is defined as a pointer to an Objective-C object.
id被定义为指向Objective-C对象的指针。
#5
1
#define IS_OBJECT(x) ( strchr("@#", @encode(typeof(x))[0]) != NULL ) This micro works which I got somewhere in stack overflow.
定义IS_OBJECT(x) (strchr(“@#”,@encode(类型(x))[0]) !
#6
0
It's important to note that id represents any Objective-C object. And by Objective-C object, I mean one that is defined using @interface. It does not represent a struct or primitive type (int, char etc).
需要注意的是,id表示任何Objective-C对象。Objective-C对象,我指的是用@interface定义的对象。它不表示结构或原始类型(int, char等)。
Also, you can only send messages (the [...] syntax) to Objective-C objects, so you cannot send the isKindOf: message to a normal struct or primitive.
此外,您只能发送消息((语法)对Objective-C对象,所以你不能发送isKindOf:消息到一个正常的结构或原语。
But you can convert a integer etc to a NSNumber, a char* to a NSString and wrap a structure inside a NSObject-dervied class. Then they will be Objective-C objects.
但是您可以将一个整数etc转换成一个NSNumber,一个char*转换成一个NSString,并将一个结构封装到一个nsobjecdervied类中。然后它们就是Objective-C对象。