1.通过简单的小例子看看 alloc 和 init的作用
1.代码输出
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
Person * p1 = [Person alloc];
Person * p2 = [p1 init];
Person * p3 = [p1 init];
NSLog(@"p1 == %@ p1 == %p &p1 == %p", p1, p1, &p1);
NSLog(@"p2 == %@ p2 == %p &p1 == %p", p2, p2, &p2);
NSLog(@"p3 == %@ p3 == %p &p3 == %p", p3, p3, &p3);
}
2.输出结果
p1 == <Person: 0x282448110> p1 == 0x282448110 &p1 == 0x16cf81bf8
p2 == <Person: 0x282448110> p2 == 0x282448110 &p1 == 0x16cf81bf0
p3 == <Person: 0x282448110> p3 == 0x282448110 &p3 == 0x16cf81be8
3.结论
p1、p2、p3三个指针指向相同的内存地址 0x2812fbe80 说明如下
1.init对开辟的内存空间没有做任何处理
2.开辟的内存地址来自于alloc方法
3.&p3 &p2 &p1依次累加,说明栈上的空间连读且指针为8字节
2. alloc源码调用流程
1.通过调试编译好的源码,alloc调用流程如下
alloc --> _objc_rootAlloc --> callAlloc --> _objc_rootAllocWithZone --> _class_createInstanceFromZone --> instanceSize、calloc、initIsa
2.alloc流程源码
- alloc源码
+ (id)alloc {
return _objc_rootAlloc(self);
}
- _objc_rootAlloc源码
id _objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
- callAlloc源码
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
if (slowpath(checkNil && !cls)) return nil;
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
return _objc_rootAllocWithZone(cls, nil);
}
#endif
// No shortcuts available.
if (allocWithZone) {
return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
}
return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}
4._objc_rootAllocWithZone 源码
id _objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
// allocWithZone under __OBJC2__ ignores the zone parameter
return _class_createInstanceFromZone(cls, 0, nil,
OBJECT_CONSTRUCT_CALL_BADALLOC);
}
- _class_createInstanceFromZone源码
整个流程调用下来最重要的三个方法如下,恰好诠释了alloc到底做了哪些事情
_class_createInstanceFromZone --> instanceSize、calloc、initIsa
1.instanceSize 先计算出需要的内存空间大小
2. calloc 向系统申请开辟内存,返回地址指针,并没有指向对应的类,仅仅开辟了内存空间
3. initIsa 关联到相应的类,和对应的class关联起来
static ALWAYS_INLINE id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
int construct_flags = OBJECT_CONSTRUCT_NONE,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
ASSERT(cls->isRealized());
// Read class's info bits all at once for performance
bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer();
size_t size;
// 1.计算出需要开辟多少字节的内存地址
size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (zone) {
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
// 2.开辟内存空间,返回地址指针,此时并没有关联到对应的类只是一个地址
// 2.这里自动开辟的内存就是16字节对齐的,malloc源码里面有 先右移4位小于16抹零,再左移4位
obj = (id)calloc(1, size);
}
if (slowpath(!obj)) {
if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
return _objc_callBadAllocHandler(cls);
}
return nil;
}
if (!zone && fast) {
obj->initInstanceIsa(cls, hasCxxDtor);
} else {
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
// 3.通过isa指针把上面开辟的内存地址关联到对应的类
obj->initIsa(cls);
}
if (fastpath(!hasCxxCtor)) {
return obj;
}
construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
return object_cxxConstructFromClass(obj, cls, construct_flags);
}
6.开辟的内存空间大小(字节对齐,8字节对齐,最少16个字节)
1.没有字节对齐的空间传递到函数里,返回有字节对齐的空间大小,且最少16个字节
2.alignedInstanceSize() ---> word_align(unalignedInstanceSize())
3.unalignedInstanceSize()--->类的数据段data()->ro()->instanceSize
4.字节对齐的算法(x + WORD_MASK) & ~WORD_MASK
8的倍数,WORD_MASK = 7 (x + WORD_MASK)任何一个数加上7,都给个机会~WORD_MASK = ~7 = 1000,即使8以下抹零 速度很快
5.(x + 7) >> 3 << 3
6. if (size < 16) size = 16; 以8字节对齐为基准,最少16个字节
7.对象需要的内存空间大小 8的倍数 8字节对齐
8.最少16个字节,为了安全预留8字节的空间,尽管不需要也最少16个字节
9.为什么8字节对齐,以空间换取时间,可长可短的话读起来效率非常低,8字节指针非常多,都是8字节效率很高
10.苹果早期版本是8字节对齐,新版是16字节对齐fastInstanceSize ---> align16
11.为什么16字节,方便速度便捷,16字节更加安全
inline size_t instanceSize(size_t extraBytes) const {
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(extraBytes);
}
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
size_t fastInstanceSize(size_t extra) const
{
ASSERT(hasFastInstanceSize(extra));
if (__builtin_constant_p(extra) && extra == 0) {
return _flags & FAST_CACHE_ALLOC_MASK16;
} else {
size_t size = _flags & FAST_CACHE_ALLOC_MASK;
// remove the FAST_CACHE_ALLOC_DELTA16 that was added
// by setFastInstanceSize
return align16(size + extra - FAST_CACHE_ALLOC_DELTA16);
}
}
static inline size_t align16(size_t x) {
return (x + size_t(15)) & ~size_t(15);
}
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() const {
return word_align(unalignedInstanceSize());
}
uint32_t unalignedInstanceSize() const {
ASSERT(isRealized());
return data()->ro()->instanceSize;
}
字节对齐的算法
#ifdef __LP64__
# define WORD_SHIFT 3UL
# define WORD_MASK 7UL
# define WORD_BITS 64
#else
# define WORD_SHIFT 2UL
# define WORD_MASK 3UL
# define WORD_BITS 32
#endif
static inline uint32_t word_align(uint32_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
7.对象属性在内存中的布局(读取对象属性的值)
1.对应类的属性
@interface Person : NSObject
@property (nonatomic, copy) NSString * name;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) long height;
@property (nonatomic, assign) NSString * hobby;
@end
2.创建类对象赋值
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
Person * person = [Person alloc];
person.name = @"daShen";
person.age = 18;
person.height = 185;
person.hobby = @"女";
NSLog(@"person == %@", person);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
3.读取情况
1.x person 16进制读取对象的内存空间
2.x/4gx person 读取4段内存地址,而且是排列好的
3.栈顶是isa指针
4.(lldb) x/4gx person
0x60000290c7e0: 0x0100000104759571 0x0000000000000012
0x60000290c7f0: 0x0000000104754028 0x00000000000000b9
(lldb) po 0x60000290c7e0
<Person: 0x60000290c7e0>

3.init和new到底做了什么
1.init 什么都没有做,直接返回了obj对象,只是方便重写,工厂设计模式,提供接口层,更符合真实需求去设计
1.init 源码
+ (id)init {
return (id)self;
}
- (id)init {
return _objc_rootInit(self);
}
id
_objc_rootInit(id obj)
{
// In practice, it will be hard to rely on this function.
// Many classes do not properly chain -init calls.
return obj;
}
2.new源码
new = [alloc init] 完全一致 new = [callAlloc() init]
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}
4.总结
1.alloc 的作用
1.1先计算出需要的内存空间大小
1.2向系统申请开辟内存,返回地址指针
1.3关联到相应的类
2.init的作用
2.1直接返回内存地址,构造方法,工厂设计模式,提供接口层
3.new的作用
3.1 new 完全等价于 [alloc init]
网友评论