美文网首页
所有OC对象都会调用alloc吗?

所有OC对象都会调用alloc吗?

作者: 笑破天 | 来源:发表于2022-08-13 00:00 被阅读0次

起因:

Matrix中OC对象的类型获取不到,详见https://github.com/Tencent/matrix/issues/514
原因:nsobject_hook_alloc_method是hook了NSObject alloc方法进行记录类名信息,但是一些case下OC对象创建并不会走alloc

+ (id)event_logging_alloc {
    id object = [self event_logging_alloc];

    if (is_thread_ignoring_logging()) {
        return object;
    }
    nsobject_set_last_allocation_event_name(object, class_getName(self.class));
    return object;
}

打符号断点测试如下:

    NSObject *obj0 = [[NSObject alloc] init]; //不走alloc和allocWithZone,走_objc_rootAllocWithZone
    NSObject *obj0_1 = [NSObject new]; //同上
    
    NSObject *obj1 = [[UIView alloc] init]; //走_objc_rootAllocWithZone,走CALayer allocWithZone+_objc_rootAllocWithZone,走NSObject alloc+allocWithZone
    NSObject *obj1_1 = [UIView new]; //同上

    NSObject *obj2 = @"sfjksafsdsfdsf"; //不走alloc、allocWithZone、_objc_rootAllocWithZone
    NSObject *obj2_1 = [NSString stringWithString:@"sfjksafsdsfdsf"]; //不走alloc、_objc_rootAllocWithZone,走allocWithZone
    NSObject *obj2_2 = [NSString new]; //不走alloc、_objc_rootAllocWithZone,走allocWithZone
    
    NSObject *obj3 = [NSData data]; //不走alloc、_objc_rootAllocWithZone,走NSData、_NSZeroData allocWithZone
    NSObject *obj3_1 = [NSData new]; //同上
    
    NSObject *obj4 = [NSArray array]; //走alloc,走allocWithZone,不走_objc_rootAllocWithZone
    NSObject *obj4_1 = @[]; //不走alloc、allocWithZone、_objc_rootAllocWithZone
    
    NSObject *obj5 = [NSDictionary dictionary]; //走alloc,走allocWithZone,不走_objc_rootAllocWithZone
    NSObject *obj5_1 = @{}; //不走alloc、allocWithZone、_objc_rootAllocWithZone

不走alloc的case:

1.顶级类NSObject:走_objc_rootAllocWithZone
2.字面量(@"sfjksafsdsfdsf"、@[]、@{}):直接objc_retain
3. NSString/NSData类方法:走allocWithZone(NSArray/NSDictionary走alloc和allocWithZone)

探究 [NSData data]

//不走alloc、_objc_rootAllocWithZone,走NSData、_NSZeroData allocWithZone
Foundation`+[NSData(NSData) data]:
    0x18b310894 <+0>:  stp    x29, x30, [sp, #-0x10]!
    0x18b310898 <+4>:  mov    x29, sp
    0x18b31089c <+8>:  adrp   x8, 228993
    0x18b3108a0 <+12>: add    x1, x8, #0xc03            ; =0xc03 
    0x18b3108a4 <+16>: mov    x2, #0x0
    0x18b3108a8 <+20>: bl     0x18ad3a080               ; objc_msgSend
->  0x18b3108ac <+24>: adrp   x8, 228993
    0x18b3108b0 <+28>: add    x1, x8, #0x874            ; =0x874 
    0x18b3108b4 <+32>: mov    x2, #0x0
    0x18b3108b8 <+36>: mov    x3, #0x0
    0x18b3108bc <+40>: bl     0x18ad3a080               ; objc_msgSend
    0x18b3108c0 <+44>: ldp    x29, x30, [sp], #0x10
    0x18b3108c4 <+48>: b      0x18ad58080               ; objc_autorelease
// allocWithZone -> NSAllocateObject -> class_createInstance -> calloc -> malloc_zone_calloc

给0x18ad3a080下断点,po $x0、x/s $x1可知 [NSData data]方法内部直接调用了[NSData allocWithZone:]方法

// objc4源码调试调用栈为
  * frame #0: 0x00000001986c3894 Foundation`+[NSData(NSData) allocWithZone:]
    frame #1: 0x000000010035a738 libobjc.A.dylib`objc_allocWithZone [inlined] callAlloc(cls=0x00000002026d9450, checkNil=true, allocWithZone=true) at NSObject.mm:1935:16
    frame #2: 0x000000010035a6c0 libobjc.A.dylib`objc_allocWithZone(cls=0x00000002026d9450) at NSObject.mm:1960:12
    frame #3: 0x000000019872f4d4 Foundation`__19+[_NSZeroData data]_block_invoke + 24
    frame #4: 0x0000000100471dcc libdispatch.dylib`_dispatch_client_callout + 20
    frame #5: 0x0000000100473d34 libdispatch.dylib`_dispatch_once_callout + 156
    frame #6: 0x000000019872f4b8 Foundation`+[_NSZeroData data] + 64
    frame #7: 0x00000001986c3978 Foundation`-[_NSPlaceholderData initWithBytes:length:copy:deallocator:] + 124
    frame #8: 0x000000019872f460 Foundation`+[NSData(NSData) data] + 36

总结:

没有自定义的allocWithZone方法就走_class_createInstanceFromZone(nil)逻辑,调用instanceSize、calloc、initInstanceIsa等逻辑。
有自定义allocWithZone方法就走自定义allocWithZone方法。
注:iOS断点调试和objc4参考源码因内敛函数编译优化和平台差异会有一定不同,大体逻辑一致。

// 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));
}

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;

    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) {
        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);
}

参考:
iOS 探索alloc
iOS-底层原理 02:alloc & init & new 源码分析

相关文章

网友评论

      本文标题:所有OC对象都会调用alloc吗?

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