*Type Encodings *
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 anint, a pointer, a tagged structure or union, or a class name—any type
前言
在Objective-C的运行时环境中,类型编码器(Type Encoder)被广泛地应用,例如属性、变量、方法甚至结构体等任何类型的编码都是通过类型编码器来描述的,以方便编译器和运行时环境进行处理。了解类型编码器的语法和结构可以更好地理解Objective-C的运行时机制,从而更好地进行开发和调试。
@encode
在Objective-C中,编译器指令@encode用于返回给定参数的Objective-C类型编码,它将指定的类型编码成一个字符串,可以用于序列化和反序列化。在一些情况下,我们需要使用@encode指令将一个对象或类型编码成字符串,以便于进行数据传输、存储和解析等操作。
语法格式
// type-name是要编码的类型,可以是一个基本类型、指针类型、结构体类型等等
@encode(type-name)
日常使用
数组类型编码
NSLog(@"%s", @encode(NSObject*[10])); // [10@]
NSLog(@"%s", @encode(float[10])); // [10f]
NSLog(@"%s", @encode(float*[10])); // [10^f]
一个数组的类型编码是用一个中括号[]表示的,中括号里面是数组的成员数量和类型,上面的例子中第一个表示10个对象的数组,第二个表示10个浮点型变量的数组,第三个便是10个浮点型指针的数组
结构体类型编码
typedef struct example {
id anObject;
int aInt;
unsigned int bInt;
char aChar;
unsigned char bChar;
char *aString;
} Example;
NSLog(@"encode %s", @encode(Example)); // {example=@icCI*}
NSLog(@"encode %s", @encode(Example *)); // ^{example=@icCI*}
NSLog(@"encode %s", @encode(Example **)); // ^^{example}
在这段代码中,我们定义了一个名为Example的结构体类型,其包含了多种类型的成员变量,通过@encode指令输出结构体Example的编码结果
解析编码结果:
- { 和 } 括号表示一个结构体类型的开始和结束
- @ 表示第一个成员变量的类型是一个对象
- * 表示第二个成员变量的类型是一个字符数组
- i 表示第三个成员变量的类型是一个整数
对象的类型编码
Objects are treated like structures.
The
NSObjectclass declares just one instance variable,isa, of type Class
@interface Person : NSObject {
NSString *_desc;
int age;
double assets;
}
@property (nonatomic, strong) NSString *name;
@end
NSLog(@"%s", @encode(Person)); // {Person=#@id}
NSLog(@"%s", @encode(NSObject)); // {NSObject=#}
NSLog(@"%s", @encode(NSArray)); // {NSArray=#}
NSLog(@"%s", @encode(NSString)); // {NSString=#}
NSLog(@"%s", @encode(NSMutableArray)); // {NSMutableArray=#}
通过以上例子可以看出,对象是被视为特殊的结构体,因此它们的类型编码和结构体是类似的,都是用大括号{}表示
对于Person类,按照先后顺序依次为类对象Class,实例对象#,静态类型即NSString *类型声明的对象@, int类型i,double类型d
获取类所有成员变量的类型编码
通过Objective-C运行时库提供的一个函数ivar_getTypeEncoding(Ivar),用于获取指定实例变量(ivar)的类型编码
@interface Person : NSObject {
int myIntVar;
float myFloatVar;
NSString *myStringVar;
}
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
- (void)doSomething;
@end
#import <objc/runtime.h>
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([Person class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivars[i];
ptrdiff_t offset = ivar_getOffset(ivar);
const char *name = ivar_getName(ivar);
const char *encoding = ivar_getTypeEncoding(ivar);
NSLog(@"ivar: %s, offset: %td, encoding: %@", name, offset, encoding);
}
free(ivars);
/**
打印结果如下:
ivar: myIntVar, offset: 8, encoding: i
ivar: myFloatVar, offset: 12, encoding: f
ivar: myStringVar, offset: 16, encoding: @"NSString"
ivar: _name, offset: 24, encoding: @"NSString"
ivar: _age, offset: 32, encoding: q
*/
常见的类型编码
| 编码(Code) | 含义(Meaning) |
|---|---|
| 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 | 表示 bool 类型的值 |
| v | 表示 void 类型的值 |
| * | 表示 NSString 类型的值 |
| @ | 表示对象(无论是静态类型还是 id 类型)的值 |
| # | 表示 类对象 类型的值 |
| : | 表示 SEL 类型的值 |
| [array type] | 表示 数组NSArray 类型的值 |
| {name=type... } | 表示 结构体 Struct 类型 的值 |
| (name=type... ) | A union |
bnum | A bit field of num bits |
^type | 表示指向某个类型的指针 |
? | 未知类型(除此之外,还可用户表示函数指针) |
Objective-C不支持long double类型。@encode(long double)返回d,其类型编码与double相同。