Type EncodingsMattt Ricky Tan 🚩🌱

数字电台数学命理象形文字流浪汉码,找到看似平常的东西中隐藏的意思真是令人着迷。即使它们中隐藏的信息很少用到或者并不特别有趣,但正是那种寻找的快感激发着我们强烈的好奇心。

在这种精神下,本周的 NSHipster 我们来看看 Objective-C Type Encodings


上一周,在讨论 NSValue 时提到了 +valueWithBytes:objCType:,它的第二个参数需要用 Objective-C 的编译器指令 @encode() 来创建。

@encode@编译器指令 之一,返回一个给定类型编码为一种内部表示的字符串(例如,@encode(int)i),类似于 ANSI C 的 typeof 操作。苹果的 Objective-C 运行时库内部利用类型编码帮助加快消息分发。

这里有一个所有不同的 Objective-C 类型编码的概要:

Objective-C Type Encodings
编码意义
cA char
iAn int
sA short
lA longl is treated as a 32-bit quantity on 64-bit programs.
qA long long
CAn unsigned char
IAn unsigned int
SAn unsigned short
LAn unsigned long
QAn unsigned long long
fA float
dA double
BA C++ bool or a C99 _Bool
vA void
*A character string (char *)
@An object (whether statically typed or typed id)
#A class object (Class)
:A method selector (SEL)
[array type]An array
{name=type...}A structure
(name=type...)A union
bnumA bit field of num bits
^typeA pointer to type
?An unknown type (among other things, this code is used for function pointers)

当然,用图表很不错,但是用代码实践更好:

NSLog(@"int        : %s", @encode(int));
NSLog(@"float      : %s", @encode(float));
NSLog(@"float *    : %s", @encode(float*));
NSLog(@"char       : %s", @encode(char));
NSLog(@"char *     : %s", @encode(char *));
NSLog(@"BOOL       : %s", @encode(BOOL));
NSLog(@"void       : %s", @encode(void));
NSLog(@"void *     : %s", @encode(void *));

NSLog(@"NSObject * : %s", @encode(NSObject *));
NSLog(@"NSObject   : %s", @encode(NSObject));
NSLog(@"[NSObject] : %s", @encode(typeof([NSObject class])));
NSLog(@"NSError ** : %s", @encode(typeof(NSError **)));

int intArray[5] = {1, 2, 3, 4, 5};
NSLog(@"int[]      : %s", @encode(typeof(intArray)));

float floatArray[3] = {0.1f, 0.2f, 0.3f};
NSLog(@"float[]    : %s", @encode(typeof(floatArray)));

typedef struct _struct {
    short a;
    long long b;
    unsigned long long c;
} Struct;
NSLog(@"struct     : %s", @encode(typeof(Struct)));

结果:

类型编码
inti
floatf
float *^f
charc
char **
BOOLc
voidv
void *^v
NSObject *@
NSObject#
[NSObject]{NSObject=#}
NSError **^@
int[][5i]
float[][3f]
struct{_struct=sqQ}

这里有一些特别需要注意的:

方法编码

如苹果的 “Objective-C Runtime Programming Guide” 中所提到的,有一大把内部使用的类型编码无法用 @encode() 返回。

以下是协议中声明的方法的类型修饰符:

Objective-C Method Encodings
编码意义
rconst
nin
Ninout
oout
Obycopy
Rbyref
Voneway

对于那些熟悉 NSDistantObject 的人,你无疑会认出这些是 Distributed Objects 的残留。

尽管 DO (Distributed Objects) 在 iOS 时代已经不那么时髦了,它仍是用于 Cocoa 应用程序进程间通信的协议————甚至用于网络上的不同机器之间。在这些约束下,上下文里附加的内容就带来了很多好处。

例如,分页式的对象消息的参数默认是用代理传递的。在那些没必要用到低效的代理的情况下,增加一个 bycopy 修饰符以保证发送了一份完整的拷贝。同样,默认情况下,带用 inout 的参数表明它在发消息时对象即可传入又可传出。将参数特别标注为 inout,程序将避免一些来回的开销。


我们从对 Objective-C 的类型编码的全新理解上能得到什么呢? 不瞒您说,其实没多少(除非你在做一些疯狂的元编程)。

但是就如我们最开始所说的,在追求破译密文的过程中要用到不少智慧。

看看类型编码为我们展现的有关 Objective-C 内部的细节,这本身就是一种高尚的追求。如果刨根问到底的话,我们需要了解一下 Distributed Objects 神秘的历史以及那 至今仍然存在 的复杂的参数修饰符。


除非另有声明,本文采用知识共享「署名-非商业性使用 3.0 中国大陆」许可协议授权。