ObjC中的TypeEncodings

OC中有一个经常使用到的概念叫做类型编码(type encodings).这个编码规则在方法的类型编码以及类型的动态动态转化中都有着非常重要的作用.

1. ObjC 的 type encodings 列表

编码意义
cchar 类型
iint 类型
sshort 类型
llong 类型,仅用在 32-bit 设备上
qlong long 类型
Cunsigned char 类型
Iunsigned int 类型
Sunsigned short 类型
Lunsigned long 类型
Qunsigned long long 类型
ffloat 类型
ddouble 类型,long double 不被 ObjC 支持,所以也是指向此编码
Bbool 或 _Bool 类型
vvoid 类型
*C 字串(char *)类型
@对象(id)类型
#Class 类型
:SEL 类型
[array type]C 数组类型(注意这不是 NSArray)
{name=type...}结构体类型
(name=type...)联合体类型
bnum位段(bit field)类型用 b 表示,num 表示字节数,这个类型很少用
^type一个指向 type 类型的指针类型
?未知类型

2.基础数据类型的type encodings

2.1 整型和浮点型数据

 

NSLog(@"char           : %s, %lu", @encode(char), sizeof(char));
NSLog(@"short          : %s, %lu", @encode(short), sizeof(short));
NSLog(@"int               : %s, %lu", @encode(int), sizeof(int));
NSLog(@"long            : %s, %lu", @encode(long), sizeof(long));
NSLog(@"long long.   : %s, %lu", @encode(long long), sizeof(long long));
NSLog(@"float            : %s, %lu", @encode(float), sizeof(float));
NSLog(@"double        : %s, %lu", @encode(double), sizeof(double));
NSLog(@"NSInteger.  : %s, %lu", @encode(NSInteger), sizeof(NSInteger));
NSLog(@"CGFloat      : %s, %lu", @encode(CGFloat), sizeof(CGFloat));
NSLog(@"int32_t        : %s, %lu", @encode(int32_t), sizeof(int32_t));
NSLog(@"int64_t        : %s, %lu", @encode(int64_t), sizeof(int64_t));

在IA-32位系统上,输出结果:

char           : c, 1
short          : s, 2
int               : i, 4
long            : l, 4
long long.   : q, 8
float            : f, 4
double        : d, 8
NSInteger   : i, 4
CGFloat      : f, 4
int32_t        : i, 4
int64_t        : q, 8

在IA-64位系统上进行同样的操作,输出结果:

char           : c, 1
short          : s, 2
int               : i, 4
long            : q, 8
long long.   : q, 8
float             : f, 4
double         : d, 8
NSInteger    : q, 8
CGFloat        : d, 8
int32_t          : i, 4
int64_t          : q, 8

所以在IA-32和IA-64位操作系统上,long和CGFloat类型的类型编码发生了变化,所以在使用long和CGFloat类型时应该注意数据本身的长度需求,最高不好直接使用这两这种类型以免在不同的操作系统上造成数据的溢出.

2.2 布尔类型

NSLog(@"bool         : %s, %lu", @encode(bool), sizeof(bool));
NSLog(@"_Bool       : %s, %lu", @encode(_Bool), sizeof(_Bool));
NSLog(@"BOOL      : %s, %lu", @encode(BOOL), sizeof(BOOL));
NSLog(@"Boolean  : %s, %lu", @encode(Boolean), sizeof(Boolean));
NSLog(@"boolean_t: %s, %lu", @encode(boolean_t), sizeof(boolean_t));

在IA-32位操作系统上,输出结果为:

bool         : B, 1
_Bool       : B, 1
BOOL      : c, 1
Boolean  : C, 1
boolean_t: i, 4

在IA-64位操作系统上,输出结果为:

bool         : B, 1
_Bool       : B, 1
BOOL      : B, 1
Boolean  : C, 1
boolean_t: I, 4

可以看出不同的布尔表示编码还是比较作妖的.

2.3 void、指针和数组

NSLog(@"void         : %s, %lu", @encode(void), sizeof(void));
NSLog(@"char *      : %s, %lu", @encode(char *), sizeof(char *));
NSLog(@"short *     : %s, %lu", @encode(short *), sizeof(short *));
NSLog(@"int *          : %s, %lu", @encode(int *), sizeof(int *));
NSLog(@"char[3]    : %s, %lu", @encode(char[3]), sizeof(char[3]));
NSLog(@"short[3]   : %s, %lu", @encode(short[3]), sizeof(short[3]));
NSLog(@"int[3]        : %s, %lu", @encode(int[3]), sizeof(int[3]));

在IA-32位操作系统上:

void         : v, 1
char *      : *, 4
short *    : ^s, 4
int *         : ^i, 4
char[3]   : [3c], 3
short[3] : [3s], 6
int[3]      : [3i], 12

在IA-64位操作系统上:

void         : v, 1
char *      : *, 8
short *    : ^s, 8
int *         : ^i, 8
char[3]   : [3c], 3
short[3] : [3s], 6
int[3]      : [3i], 12

指针类型与操作系统位数相同,所以在不同位长的操作系统上,指针类型有所不同.

2.4 结构体

NSLog(@"CGSize: %s, %lu", @encode(CGSize), sizeof(CGSize));

NSLog(@"CGPoint: %s, %lu", @encode(CGPoint), sizeof(CGPoint));

在IA-32位操作系统上:

CGSize: {CGSize=ff}, 8

CGPoint: {CGPoint=ff}, 8

在IA-64位操作系统上:

CGSize: {CGSize=dd}, 16

CGPoint: {CGPoint=dd}, 16

由于在IA-32位和IA-64位操作系统上对于CGFloat的定义不一样,所以关于这两个结构体的类型编码会不一致.

3.  ObjC 数据类型的 type encodings

3.1 基本类型

NSLog(@"Class       : %s", @encode(Class));
NSLog(@"NSObject: %s", @encode(NSObject));
NSLog(@"NSString: %s", @encode(NSString));
NSLog(@"id            : %s", @encode(id));
NSLog(@"Selector : %s", @encode(SEL));

输出结果为:

Class       : #
NSObject: {NSObject=#}
NSString : {NSString=#}
id             : @
Selector: :

可以看到对象的类名称的编码方式跟结构体相似,等于号后面那个 # 就是Class指针了,是一个 Class类型的数据。

3.2 属性和成员变量

  • 属性
objc_property_t property = class_getProperty([NSObject class], "description");
if (property) {
    NSLog(@"%s - %s", property_getName(property), property_getAttributes(property));
} else {
    NSLog(@"not found");
}


输出结果为:

description - T@"NSString",R,C


其中:R 表示 readonlyC 表示 copy,是属性的修饰词;T表示类型Type.

  • 成员变量
@interface TestClass : NSObject {
    int testInt;
    NSString *testStr;
}
@end
 
Ivar ivar = class_getInstanceVariable([TestClass class], "testInt");
if (ivar) {
    NSLog(@"%s - %s", ivar_getName(ivar), ivar_getTypeEncoding(ivar));
} else {
    NSLog(@"not found");
}
ivar = class_getInstanceVariable([TestClass class], "testStr");
if (ivar) {
    NSLog(@"%s - %s", ivar_getName(ivar), ivar_getTypeEncoding(ivar));
} else {
    NSLog(@"not found");
}

输出结果:

testInt - i
testStr - @"NSString"

3.3 方法

Method method = class_getInstanceMethod([UIView class], @selector(setFrame:));
if (method) {
    NSLog(@"%@ - %s", NSStringFromSelector(method_getName(method)), method_getTypeEncoding(method));
} else {
    NSLog(@"not found");
}

输出结果:

setFrame: - v48@0:8{CGRect={CGPoint=dd}{CGSize=dd}}16

编码代表的意义:

  • 第一个字符 v 是表示函数的返回值是 void 类型;
  •  48 表示方法参数表的字节长度;
  • @ 表示一个对象,在 ObjC 里这里传递的是 self,后续的 0 代表参数self在结构体的位置,即对应的 offset;
  •  : 表示一个 selector,用来指出要调用方法选择器,后续的 8 代表是 selector 参数在结构体中的位置,即selector对应的offset;
  • {CGRect={CGPoint=dd}{CGSize=dd}} 是CGRect 结构体的 type encoding,从这里也可以看出结构体嵌套使用时对应的 type encoding 是这种格式的,这个结构体包含 4 个 double 类型的数据,所以总长度应该是 32,最后的 16 是最后一个参数的在结构体中的offset,加上刚刚的参数长度 32 正好是整个函数参数表的长度.

然后我们再看一下类方法的方法编码

    Method method = class_getInstanceMethod(object_getClass([UIView class]), @selector(alloc));
    if (method) {
        NSLog(@"%@ - %s", NSStringFromSelector(method_getName(method)), method_getTypeEncoding(method));
    } else {
        NSLog(@"not found");
    }

输出结果为:

alloc - @16@0:8

编码代表的意义:

  • @ 代表返回值是id类型;
  • 16代表方法参数表的字节长度;
  • @代表方法的第一个参数self,0代表self在结构体中的位置;
  • :代表方法选择器,即sel,8代表sel在该结构体中的位置(即偏移量offset).

3.4 Block的编码

关于block的方法编码,我们可以借助于Aspects中重写的block结构体来获取:

typedef NS_OPTIONS(int, AspectBlockFlags) {
    AspectBlockFlagsHasCopyDisposeHelpers = (1 << 25),
    AspectBlockFlagsHasSignature          = (1 << 30)
};
typedef struct _AspectBlock {
    __unused Class isa;
    AspectBlockFlags flags;
    __unused int reserved;
    void (__unused *invoke)(struct _AspectBlock *block, ...);
    struct {
        unsigned long int reserved;
        unsigned long int size;
        // requires AspectBlockFlagsHasCopyDisposeHelpers
        void (*copy)(void *dst, const void *src);
        void (*dispose)(const void *);
        // requires AspectBlockFlagsHasSignature
        const char *signature;
        const char *layout;
    } *descriptor;
    // imported variables
} *AspectBlockRef;

    id(^testBlock)(NSString *, NSArray *) = ^id(NSString *str, NSArray *arr){
        return str;
    };
    
    AspectBlockRef layout = (__bridge void *)testBlock;
    void *des = layout->descriptor;
    des += 2 * sizeof(unsigned long int);
    if (layout->flags & AspectBlockFlagsHasCopyDisposeHelpers) {
        des += sizeof(void *);
    }
    if (layout->flags & AspectBlockFlagsHasSignature) {
        const char *signature =  *(void **)des;
        NSLog(@"-----%s", signature);
    }

输出结果为:

-----@24@?0@"NSString"8@"NSArray"16

所以在block的方法编码中,由于没有selector这个参数,所以第一个传递进来的参数其实就是block自己.

  • @ 返回值为id类型;
  • @?block本身, 0是block在结构体中的位置偏移量;
  • @"NSString" 代表第一个参数,8是NSString这个参数在结构体中的偏移量;
  • @"NSArray"16代表第二个参数,16是第二个参数在结构体中的偏移量.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值