Runtime Programming Guide再次研究

这次准备好好研究下runtime programming, 先从encodings开始了解,这个对于理解oc的类也是很有帮助的。 废话不多说直接开始。

Type Encodings

compiler 编码encode 每个method的返回值,参数类型,将这些信息保存在一个字符串里。然后将这个string和selector关联起来。

这套编码也适用于其他场景,所以这个@encode编译器指令是设计成公开的。当给定一个type的规范声明,@encode()返回相对应的字符串类型编码值。

1
2
3
char *buf1 = @encode(int **);
char *buf2 = @encode(struct key);
char *buf3 = @encode(Rectangle);

type encodings 更多的类型可以查看https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100-SW1

数组的type code是一个中括号里面,然后紧接着一个数组个数值和相对应的数组元素的类型,如一个包含12个float指针的数组的编码为:[12^f]

structures 是一个大括号里,里面是显示紧跟着structure tag(struct的结构名称)然后一个等号(equal sign)=,接着就是struct里各个类型排列在一起,比如

1
2
3
4
5
typedef struct example {
    id   anObject;
    char *aString;
    int  anInt;
} Example;

的type code值为 {example=@*i}, 如果是一个结构体的指针,那么encode为^{example=@*i},如果是一个结构体的指针的指针,则是^^{example}.

Objects对象也被看成一个struct。@encode(NSObject)结果是{NSObject=#}, 因为NSObject class里就一个isa(class 类型)的变量。

尽管 @encode()不直接返回数据, 但运行时会使用额外的一个encoding列表来表示一些方法声明的后缀修饰符.

code Meaning
r const
i int
N inout
o out
O bycopy
R byref
V oneway

Declared Propertyies

当编译器碰到property声明时,它会生成描述性的带有关于class, category或者protocol的metadata元数据。

你能通过一些列的api函数来访问这个metadata,通过class,protocol上的name来查找property。可以通过@encode来获取property的encode string。可以copy property的attributes list(C strings)。

class和protocol都有一个properties list的值。

property type and functions

可以通过class_copyPropertyList和protocol_copyPropertyList来遍历class(包括loaded categories)和protocol的property list

通过property_getAttributes可以获取到一个property属性的@encode type的值。

比如一个@property (nonatomic, strong) NSString *login;的property @encode值为T@"NSString",&,N,V_login

每个逗号都是分割一个属性attribute,实际上当你获取到objc_property_attribute_t *attrs = property_copyAttributeList(prot, &outcount);(注意你还需要主动free掉attrs) outcount的值就是分割后的数组的个数。

attrs分别为:

property index name value
0 T @“NSString”
1 &
2 N
3 V _login

Property type string

文档里面有一段英文很重要,他描述了property type的string值的格式(这个值可以通过@encode或者通过property_getAttributes来获取)。

You can use the property_getAttributes function to discover the name, the @encode type string of a property, and other attributes of the property.

The string starts with a T followed by the @encode type and a comma, and finishes with a V followed by the name of the backing instance variable. Between these, the attributes are specified by the following descriptors, separated by commas:

这段英文很重要,说明了格式,首先是以一个T开头的,然后@encode type加一个逗号,以一个V后面带上存储支持的实例变量,然后在中间,每个attributes都是以逗号隔开的,这些attributes描述如下:

可以官场上面的那个例子@property (nonatomic, strong) NSString *login;的property @encode值为T@"NSString",&,N,V_login

Table 7-1 Declared property type encodings

Code Meaning
R The property is read-only (readonly).
C The property is a copy of the value last assigned (copy).
& The property is a reference to the value last assigned (retain).
N The property is non-atomic (nonatomic).
G The property defines a custom getter selector name. The name follows the G (for example, GcustomGetter,).
S The property defines a custom setter selector name. The name follows the S (for example, ScustomSetter:,)
D The property is dynamic (@dynamic).
W The property is a weak reference (__weak).
P The property is eligible for garbage collection.
t Specifies the type using old-style encoding.

这个链接里有相关的demo示例说明property declaration声明和描述description。 https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/TP40008048-CH101-SW5

这里只罗列少数几个:

property declaration description
@property(getter=intGetFoo, setter=intSetFoo:) int intSetterGetter; Ti,GintGetFoo,SintSetFoo:,V_intSetterGetter
@property int intSynthEquals;In the implementation block:@synthesize intSynthEquals=_intSynthEquals; Ti,V_intSynthEquals
@property int (*functionPointerDefault)(char *); T^?,V_functionPointerDefault
@property (strong, atomic) NSString * dataObject; T@“NSString”,&,V_dataObject

function/method type encode

先看例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//- (void)viewWillAppear:(BOOL)animated  -> v20@0:8B16
NSLog(@"%s", class_getInstanceMethod([self class], @selector(viewWillAppear:)));
//- (void) setSomething:(id) anObject  -> v24@0:8@16
NSLog(@"%s", method_getTypeEncoding(class_getInstanceMethod([self class], @selector(setSomething:))));
//- (BOOL) setSomething:(UInt8)animated aa:(id)anObject  -> B28@0:8C16@20
NSLog(@"%s", method_getTypeEncoding(class_getInstanceMethod([self class], @selector(setSomething:aa:))));
//- (BOOL) setSomething:(UInt16)animated aa:(id)anObject  -> B28@0:8s16@20
NSLog(@"%s", method_getTypeEncoding(class_getInstanceMethod([self class], @selector(setSomething:aa:))));
//- (BOOL) setSomething:(int)animated aa:(id)anObject  -> B28@0:8i16@20
NSLog(@"%s", method_getTypeEncoding(class_getInstanceMethod([self class], @selector(setSomething:aa:))));
//- (BOOL) setSomething:(long)animated aa:(id)anObject-> B32@0:8q16@24
NSLog(@"%s", method_getTypeEncoding(class_getInstanceMethod([self class], @selector(setSomething:aa:))));


typedef struct  {
    uint32_t a;
    uint16_t b;
    uint8_t c;
} __attribute__((packed)) MyStruct;
// - (void)strutMethod:(MyStruct)aa  -> v23@0:8{?=ISC}16
NSLog(@"%s", method_getTypeEncoding(class_getInstanceMethod([self class], @selector(strutMethod:))));

那么如何理解呢?

  • v means void return type
  • 20 means the size of the argument frame (20 bytes),整个方法参数占位的总长度
  • @0 means that there is an Objective-C object type at byte offset 0 of the argument frame (this is the implicit self object in each Objective-C method),这个表示在offset为0的地方有一个objective-c的对象,在objective-c method里,首个对象是self自身。
  • :8 means that there is a selector at byte offset 8 (this is the implicit _cmd in every method, which is the selector that was used to invoke the method). 在offset为8的地方有一个SEL,由于我测试的是64位机器上,所以之前的OC的对象指针占位8个字节。
  • B16 means 在offset 16的地方,有一个bool类型的参数,由于oc的对象和sel都是指针类型,64位机下所以都是占位8位,这里的bool也就出现在offset16的地方了。至于为什么bool参数占位了4个字节。主要原因是内存对齐的原因

从上面看起来,不管是bool,uint8,uint16,int都被认为4个字节。因为他们的内存实际占位都小于4个字节,由于内存对齐原因所以实际最后占位为4字节。这里当你看最后一个MyStruct就可以看出,当主动告诉编译器取消结构体的内存对齐,就发现最后一个参数事迹展位是7(4+2+1)个字节

1
2
3
4
5
6
注:packed属性:使用该属性可以使得变量或者结构体成员使用最小的对齐方式,即对变量是一字节对齐,对域(field)是位对齐.
typedef struct  {
    uint32_t a;
    uint16_t b;
    uint8_t c;
} __attribute__((packed)) MyStruct;

未完待续

至此先了解介绍完了关于type encodings, property type encodings,method encodings。稍后继续补充了解更多的运行时编程相关的知识点,runtime programming guide~~~.

参考链接

  1. http://lists.cs.uiuc.edu/pipermail/cfe-dev/2014-April/036654.html