美文网首页
alloc、init; new

alloc、init; new

作者: QYCD | 来源:发表于2021-03-26 17:42 被阅读0次

macOS Big Sur
11.0.1
Xcode 12.4
objc4-818.2
Apple Open Source

alloc调用底层源码:
+ (id)alloc {
    return _objc_rootAlloc(self);
}
// Base class implementation of +alloc. cls is not nil.
// Calls [cls allocWithZone:nil].
id
_objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
// Call [cls alloc] or [cls allocWithZone:nil], with appropriate
// shortcutting optimizations.
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));
}
cls->ISA()->hasCustomAWZ()

fastpath中的cls->ISA()->hasCustomAWZ() 时判断一个类是否有自定义的 +allocWithZone 实现,如果没有自定义allocWithZone实现,则会执行if条件语句,即: _objc_rootAllocWithZone方法

_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源码的核心操作:
  • cls->instanceSize() 计算需要开辟的内存大小
  • calloc() 申请内存
  • obj->initIsa() 将类与isa关联
/***********************************************************************
* class_createInstance
* fixme
* Locking: none
*
* Note: this function has been carefully written so that the fastpath
* takes no branch.
**********************************************************************/
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;
    
    //计算需要开辟的内存大小 根据上一步调用可知道 传入的extraBytes为0
    size = cls->instanceSize(extraBytes);
    if (outAllocatedSize) *outAllocatedSize = size;

    id obj;
    if (zone) {
        obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
    } else {
        //申请内存
        obj = (id)calloc(1, size);
    }
    if (slowpath(!obj)) {
        if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
            return _objc_callBadAllocHandler(cls);
        }
        return nil;
    }

    if (!zone && fast) {
        //将cls类与obj指针(isa)关联
        obj->initInstanceIsa(cls, hasCxxDtor);
    } else {
        // Use raw pointer isa on the assumption that they might be
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
    }

    if (fastpath(!hasCxxCtor)) {
        return obj;
    }

    construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
    return object_cxxConstructFromClass(obj, cls, construct_flags);
}

init
- (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;
}

发现init方法实际就是返回了当前对象,这是一种抽象工厂设计模式的体现。


new
+ (id)new {
    return [callAlloc(self, false/*checkNil*/) init];
}

可以看到new类方法,同样调用了callAlloc方法,只是相比alloc调用callAlloc少了一个参数(allowWithZone),并且new类方法默认包装了init实例方法。


.h
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject

@end

NS_ASSUME_NONNULL_END

.m
#import "Person.h"

@implementation Person

//+ (instancetype)allocWithZone:(struct _NSZone *)zone {
//    static id instance = nil;
//    @synchronized (self) {
//        if (instance == nil) {
//            instance = [super allocWithZone:zone];
//        }
//    }
//    return instance;
//}

@end

新建一个Person类,以下针对4种情况进行测试:

  • Person未重写allocWithZone: & 通过alloc init方式创建实例对象

    1. 先跳入callAlloc方法,执行最后一句 即:return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
    2. 调用alloc类方法 即
      + (id)alloc {
          return _objc_rootAlloc(self);
      }
      
    3. 接着上一步 执行_objc_rootAlloc
      / / Base class implementation of +alloc. cls is not nil.
      // Calls [cls allocWithZone:nil].
      id
      _objc_rootAlloc(Class cls)
      {
         return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
      }
      
    4. 调用callAlloc方法中的if判断
      if (fastpath(!cls->ISA()->hasCustomAWZ())) {
        return _objc_rootAllocWithZone(cls, nil);
      }
      
  • Person重写了allocWithZone: & 通过alloc init方式创建实例对象

    1. 前三步与上面情况一致,但因为重写了allocWithZone:方法,就不会执行if (fastpath(!cls->ISA()->hasCustomAWZ()))条件语句,而是会继续往下走,由于alloc调用callAlloc(cls, false/checkNil/, true/allocWithZone/);最后一个参数为true,则会执行下面语句:
    if (allocWithZone) {
        return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
    }
    
  • Person未重写allocWithZone: & 通过new方式创建实例对象

    1. 调用new类方法
    + (id)new {
        return [callAlloc(self, false/*checkNil*/) init];
    }
    
    1. 同Person *person = [[Person alloc] init]; 的调用顺序一样,也进入了下面的if判断中
        if (fastpath(!cls->ISA()->hasCustomAWZ())) {
          return _objc_rootAllocWithZone(cls, nil);
        }
    
  • Person重写了allocWithZone: & 通过new方式创建实例对象

    1. 调用new类方法
    2. 由于new类方法 底层[callAlloc(self, false/checkNil/) init]; allocWithZone布尔值未传,则不会执行fastpath(!cls->ISA()->hasCustomAWZ())语句中代码也不会执行if (allocWithZone),会直接执行return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc)); 实际又调用了alloc,由于重写了allocWithZone: 又会调用下面方法:
    if (allocWithZone) {
        return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
    }
    

综上: [Class new] 就相当于[[Class alloc] init],前者是隐式调用,后者为显式调用。

如果使用new的话,初始化方法就被固定只能调用init了,但如果用alloc init方式的话就不一定要用init了,还可以用initXXX等方法了。

参考:
iOS底层探究-alloc/init做了什么?
Object-C alloc 底层实现原理
iOS 中的 alloc init和new的区别

相关文章

  • alloc init != new

    当我在做环信透传消息封装的时候,遇到了这个问题。 封装的类主要实现两个功能1:body构建,消息的构建,会话的构建...

  • alloc init and new

    1.在实际开发中很少会用到new,一般创建对象咱们看到的全是[[className alloc] init] 但是...

  • alloc init new

    alloc:分配内存。 init:初始化。 new:代替上面两个函数:分配内存,并且初始化。 注意: 1.在实际开...

  • alloc、init; new

    macOS Big Sur11.0.1Xcode 12.4objc4-818.2Apple Open Source...

  • 单例模式的使用

    new和alloc/init的区别 从_alloc和_zoneAlloc中可以看出,alloc 使用了_zone...

  • alloc 、init、new详解

    先做两个实验,猜测打印结果:实验1: 打印结果: 实验2: 打印结果: 实际上,将NSString换成其他的类型,...

  • alloc init 和 new

    alloc:分配内存。init:初始化。 new:代替上面两个函数:分配内存,并且初始化。 上代码 【 Summa...

  • new与alloc、init

    开发中,创建对象时,基本使用[[ClassName alloc] init]方式初始化实例(分配内存并初始化);有...

  • iOS alloc init new

    alloc :开辟内存空间,初始化实例init: 默认的构建函数重载new :相当于[[Class alloc] ...

  • OC底层-alloc&init探索

    前言 我们创建一个对象经常用到alloc、init或者new,也大概知道alloc做了分配内存,init做了初始化...

网友评论

      本文标题:alloc、init; new

      本文链接:https://www.haomeiwen.com/subject/rwyhhltx.html