iOS原理(一)----OC对象结构
创建一个普通的NSObject
对象如下:
NSObject *obj = [[NSObject alloc] init];
NSObject
的声明如下:
@interface NSObject {
Class isa;
}
查看其变异生成的C++代码为:
struct NSObject_IMPL {
Class isa;
};
而Class
为一个结构体的指针:
typedef struct objc_class *Class;
在64位系统上,指针的大小为8个字节.所以class_getInstanceSize([NSObject class])
获取的大小为8.
但实际obj
对象为多少?用malloc_size((__bridge const void *)(obj))
函数得到大小为16.
查看runtime
源码得知class_getInstanceSize()
实现如下:
size_t class_getInstanceSize(Class cls)
{
if (!cls) return 0;
// 返回对齐过的实例大小
return cls->alignedInstanceSize();
}
// 返回成员变量的大小
uint32_t alignedInstanceSize() {
return word_align(unalignedInstanceSize());
}
由上面可以发现class_getInstanceSize()
是返回成员变量的大小,obj
对象只有一个Class
类型的指针,在64位系统上是8个字节,所以上面打印输出为8.
查看alloc
调用了一个函数如下:
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
指出所有对象创建出来至少为16字节,虽然函数中这里size
为8,所以说malloc_size()
函数得到的值是16字节.可以看出``class_getInstanceSize()函数是对象理论上需要的内存,
alloc出来的大小是对象实际对象需要的内存.从xcode调试可以看出
obj`分配了16字节内存,实际只用了8个字节.
再新建一个Animal
类,一个int
类型的age
成员变量.
@interface Animal : NSObject {
@public
int _age;
}
@end
@implementation Animal
@end
其生成的c++代码为:
struct Animal_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age;
};
据此我们可以推测出class_getInstanceSize()
函数得到的大小为12字节,malloc_size()
函数为16字节,但实际回事多少呢?打印如下:
发现都是16字节,这是因为结构体大小有个内存对齐
规则,用class_getInstanceSize()
得到的大小就为16字节.
在Animal
增加两个成员变量,heigt
和weight
都是int
类型,查看其编译生成C++源码为:
struct Animal_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age;
int _height;
int _weight;
};
根据推测结构体大小,推测class_getInstanceSize()
函数得到Animal
对象应该为24字节,malloc_size()
也应该为24字节.实际打印如下:
实际用malloc_size()
函数得到为32字节,查看源码有这样一句:
有Buckets sized东西,系统分配内存的时候,只会分配16字节的倍数的内存,所以上面的的到大小为32字节.
OC中的对象分为三种对象:
- 实例对象,就是前面创建的对象,里面存放isa指针(指向类对象)和成员变量的值信息.
- 类对象,Class,存放isa指针(需要
& ISA_MASK
,指向元类对象),superclass指针(指向父类),协议信息,属性信息,成员变量描述信息,对象方法信息... - 元类对象,isa指针(需要
& ISA_MASK
,指向根原类对象),superclass指针(指向父元类对象),类方法信息...
类对象结构如下:
如果一个类方法层层网上找,如果直到根源类都没有此方法时,就是就根类去找对象方法,如果还找不到,就会抛出错误.我们NSObject添加一个对象方法,给Animal类声明一个类方法,并没有实现它,然后调用Animal的eat类方法,调用成功
@interface NSObject (Eat)
- (void)eat;
@end
@implementation NSObject (Eat)
- (void)eat {
NSLog(@"NSObeat - eat");
}
@end
@interface Animal : NSObject {
@public
}
+ (void)eat;
@end
@implementation Animal
@end