美文网首页
所有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