Type Encodings

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

在这种精神下,本周的 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
编码 意义
c A char
i An int
s A short
l A longl is treated as a 32-bit quantity on 64-bit programs.
q A long long
C An unsigned char
I An unsigned int
S An unsigned short
L An unsigned long
Q An unsigned long long
f A float
d A double
B A C++ bool or a C99 _Bool
v A 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
bnum A bit field of num bits
^type A 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)));

结果:

类型 编码
int i
float f
float * ^f
char c
char * *
BOOL c
void v
void * ^v
NSObject * @
NSObject #
[NSObject] {NSObject=#}
NSError ** ^@
int[] [5i]
float[] [3f]
struct {_struct=sqQ}

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

  • 指针的标准编码是加一个前置的 ^,而 char * 拥有自己的编码 *。这在概念上是很好理解的,因为 C 的字符串被认为是一个实体,而不是指针。
  • BOOLc,而不是某些人以为的 i。原因是 charint 小,且在 80 年代 Objective-C 最开始设计的时候,每一个 bit 位都比今天的要值钱(就像美元一样)。BOOL 更确切地说是 signed char (即使设置了 -funsigned-char 参数),以在不同编译器之间保持一致,因为 char 可以是 signed 或者 unsigned
  • 直接传入 NSObject 将产生 #。但是传入 [NSObject class] 产生一个名为 NSObject 只有一个类字段的结构体。很明显,那就是 isa 字段,所有的 NSObject 实例都用它来表示自己的类型。

方法编码

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

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

Objective-C Method Encodings
编码 意义
r const
n in
N inout
o out
O bycopy
R byref
V oneway

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

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

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


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

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

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

作者 Mattt
Mattt

Mattt (@mattt) is a writer and developer in Portland, Oregon. He is the founder of NSHipster and Flight School, and the creator of several open source libraries, including AFNetworking and Alamofire.

翻译者
Ricky Tan

Ricky Tan 是一位来自杭州的 iOS 开发者,毕业于浙江大学,早期参与开发浙大校园应用 iZJU iOS 版。另外开源了一个反响不错的 Xcode 插件 RTImageAssets,及一个导航器 RTRootNavigationController 。更多其他内容请点我

下一篇文章

NSURLCache 为您的应用的 URL 请求提供了内存中以及磁盘上的综合缓存机制。作为基础类库 URL 加载系统的一部分,任何通过 NSURLConnection 加载的请求都将被 NSURLCache 处理。