iOS底层-alloc方法之旅

7 篇文章 0 订阅
订阅专栏

前言

通过汇编调试和源码分析,介绍iOS开发当中alloc方法到底做了什么。

追踪 alloc

实例化一个对象往往是通过 [[xxx alloc] init] 那么alloc和init的区别是什么?将两个方法分开调用,并用2个指针引用

NS_ASSUME_NONNULL_BEGIN

@interface FFPhone : NSObject

@property (nonatomic, copy) NSString * name;

@end

NS_ASSUME_NONNULL_END

#import "FFPhone.h"

@implementation FFPhone

-(instancetype)init {
    if (self = [super init]) {
        self.name = @"init iPhone";
    }
    return self;
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"Hello World!");
    
        FFPhone *phone = [FFPhone alloc];
        FFPhone *p1 = [phone init];
        FFPhone *p2 = [phone init];
      
    }
    return 0;
}

进行调试:发现这两内存地址居然是一样的。

objc_C1_001

这说明init方法不会去开辟内存空间。在alloc方法这行断点,通过汇编调试看一下初始化的方法调用。重新运行:

汇编简介:机器语言只有0和1, 汇编语言用符合代替0/1,增加可读性
b和bl都是跳转指令,理解为函数的调用
ret: 函数的返回
分号’;’ 代表注释

; symbol stub for: objc_alloc 意思是该地址保存的是objc_alloc方法的符号。

objc_C1_002

查看objc源码(地址见文末),搜索 objc_alloc 发现,fixupMessageRef 调用 alloc 时,使用的实现是 objc_alloc 。(sel代表方法名)

image-20220416165223525

汇编断点刚才的callq,进入 objc_alloc 方法内部。step over调试的时候,居然跳过了_objc_rootAllocWithZone直接到objc_msgSend,先不管。打印一下寄存器x0和x1,确实是alloc方法。

image-20220416204421176

接着增加一个 [NSObject alloc] 的符号断点

image-20220416204958584

并通过step into 跳转到方法 _objc_rootAlloc 内部:这一次step over能执行到 _objc_rootAllocWithZone 了;

image-20220416205359271

鉴于不知道这是啥,再查源码看一下:

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

方法返回的是id类型,return调用的是 _class_createInstanceFromZone ,跳转该方法:

/***********************************************************************
* 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)

可以看到返回的是obj:也就是说该方法返回实例对象。

image-20220416205856959

怎么去验证这个对象就是通过该方法返回的呢?回到挂起的汇编调试,通过step into进入:

image-20220416210337831

这里就用到前面提到ret汇编指令(这里遇到的是retab,类似的还有retaa,都代表函数返回),找到并打印寄存器验证,返回的对象如上图。

目前的结论就是,alloc方法就返回实例对象了

追踪 init

增加init方法的符号断点 [NSObject init] ,进入汇编调试:

image-20220416212401586

这里可以看到只有一个孤独的 ret 。打印 x1 ,确实是 init 方法,那这是啥也没干?找找源码:这里 _objc_rootInit 也只是返回了obj。

// Replaced by CF (throws an NSException)
+ (id)init {
    return (id)self;
}

- (id)init {
    return _objc_rootInit(self);
}

// 跳转_objc_rootInit
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 作为工厂方法,目的只是让子类继承并重写。比如NSArray继承NSObject,重写了init方法。

追踪 new

重写init方法,给默认值:

#import "FFPhone.h"

@implementation FFPhone

-(instancetype)init {
    if (self = [super init]) {
        self.name = @"init iPhone";
    }
    return self;
}

@end

调试发现通过new方法出来的对象也调用了init:

image-20220421093810384

源码NSObject类里找到new方法:也是调用了calloc,加上init方法。至于calloc没传第二个参数?默认是false;

+ (id)new {
    return [callAlloc(self, false/*checkNil*/) init];
}
// callAlloc方法
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)

汇编调试new方法,实际调用是objc_opt_new方法:

image-20220421104119109

源码搜索可以看到,如果不是__OBJC2__,也会通过objc_msgSend转发给源码中的new方法

// Calls [cls new]
id
objc_opt_new(Class cls)
{
#if __OBJC2__
    if (fastpath(cls && !cls->ISA()->hasCustomCore())) {
        return [callAlloc(cls, false/*checkNil*/) init];
    }
#endif
        // 消息转发给new方法
    return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(new));
}

结论就是,new = alloc + init

优化等级

之前查看源码的发现的 _class_createInstanceFromZone 似乎没有出现在汇编调试里。这涉及到编译器的优化。在xcode找到以下设置:optimization Level

image-20220416213508251

debug模式优化等级默认是none。先试一下以下代码:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    int a = 99;
    int b = 256;
    // 此处断点
}

汇编调试可以看到数字:

image-20220416214507265

接下来优化等级调整和release一样,汇编调试下:(对应Target一定要选对在改设置,免得弄错了以为没效果)

image-20220416215039721

发现少了那两个w8寄存器存储代码的变量值;这个就是编译器优化的,代码中声明了变量却没有使用,编译时就被干掉了。没使用的函数也是同理。


- (void)viewDidLoad {
    [super viewDidLoad];
    
    int result = mul(5, 20);
    // 断点
}

int mul(int a, int b) {
    return a * b;
}

没优化:

image-20220416220215391

优化后:虽然代码里调用了 mul 方法,但是如果返回值没使用到,就会被优化。

image-20220416220305527

接下来添加一行打印代码:

NSLog(@"result = %d", result);

再调试:其中 0x64 = 6 * 16 + 4 = 100; 也就是 5 * 20 的计算结果;这说明编译器直接在编译时把结果计算好了。相当于把函数里的实现直接替换到代码中。

image-20220416220714877

源码调试 alloc

接下来通过在源码项目中运行测试代码,并断点调试验证完整的过程:

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>
#import "FFPhone.h"

int logPhone (FFPhone *phone) {
   
    NSLog(@"class: %zu", class_getInstanceSize(phone.class));
    NSLog(@"malloc_size: %zu", malloc_size((__bridge const void *)(phone)));
    
    return 0;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"Hello World!");
    
        FFPhone *phone = [FFPhone alloc]; // 断点
        logPhone(phone);
    }
    return 0;
}

步骤1:objc_alloc

image-20220416230808875

步骤2:callAlloc ,此时走的是底下的分支,说明前面分支的条件不满足。

image-20220416231050071

步骤3:alloc

image-20220416231303361

步骤4:_objc_rootAlloc ;参数cls不为nil

image-20220416232115505

步骤5:callAlloc ,这回走的是另一个分支了

image-20220416232343127

步骤5:_objc_rootAllocWithZone ,这回走的是另一个分支了

image-20220416232917633

步骤5:_class_createInstanceFromZone ,运行结束。

2022-04-16 23:41:59.574433+0800 FFObjcDebug[98337:7503737] Hello World!
2022-04-16 23:41:59.575150+0800 FFObjcDebug[98337:7503737] class: 32
2022-04-16 23:41:59.575291+0800 FFObjcDebug[98337:7503737] malloc_size: 32
Program ended with exit code: 0

但是,汇编调试的时候没看到 callalloc_class_createInstanceFromZone 的符号调用。这依旧是编译器优化的功劳,哪怕优化水平还是none。

通过源码了解alloc调用顺序:

开始
代码调用 alloc
方法转换为 objc_alloc
callAlloc
通过objc_msgSend调用 alloc
_objc_rootAlloc
第二次调用 callAlloc
走了另一个分支 _objc_rootAllocWithZone
最终依靠 _class_createInstanceFromZone 返回对象
结束

对象的创建

目前为止的方法调用都没有看到什么实质性的代码,直接来到_class_createInstanceFromZone方法源码:


/***********************************************************************
* 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;

    // 计算需要的大小/分配内存空间
    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);
}

通过 size = cls->instanceSize(extraBytes); 分配内存空间,实现如下:

    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,并且最小为16字节;alignedInstanceSize 是字节对齐(64位系统下是8字节)。

通常8字节对齐只需要得到8的倍数:先除8再乘8,对齐还需要计算多出的部分:

// 模拟8字节对齐
int align_8 (int byte) {
    // byte + 7 : 为了得到超过8字节的部分,
    // 例如 (9 + 7) / 8 = 2, 返回 2 * 8 = 16字节
    return (byte + 7) / 8 * 8;
}

8的倍数在计算器中是如何表示的?在二进制中,8的倍数前三位永远是0,比起除法再乘法,位运算效率肯定更高。

image-20220417005232006

对应代码就是先右移再左移3位 >> 3 << 3

内存对齐

苹果官方的实现是:根据64或32位系统,进行8字节或4字节对齐。

#ifdef __LP64__
#   define WORD_SHIFT 3UL
#   define WORD_MASK 7UL // 0111,用于8字节对齐
#   define WORD_BITS 64
#else
#   define WORD_SHIFT 2UL
#   define WORD_MASK 3UL // 0010,用于4字节对齐
#   define WORD_BITS 32
#endif

static inline uint32_t word_align(uint32_t x) {
    return (x + WORD_MASK) & ~WORD_MASK;
}
static inline size_t word_align(size_t x) {
    return (x + WORD_MASK) & ~WORD_MASK;
}
static inline size_t align16(size_t x) {
    return (x + size_t(15)) & ~size_t(15);
}

这里 & ~WORD_MASK 怎么理解呢,例如64位系统下,先进行非运算:

~WORD_MASK = ~7UL = ~0111 = 1000;

再进行与运算:0与上任何数都是0,只有1 & 1 = 1。例如23对齐后是24:

(23 + WORD_MASK) & ~WORD_MASK = 0001 1110 & 1000 = 0001 1000 = 24;

通过一个函数兼容64位和32位系统下的字节对齐,这就是值得学习的地方。回到上文方法 instanceSize ,在字节对齐前,如果有缓存,会进入另一个方法 return cache.fastInstanceSize(extraBytes); ,实现如下:

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

可以看到 align16,也就是进行16字节对齐(前面8字节对齐源码里有对应实现)。令人疑惑的是,究竟以多少字节进行对齐呢?这里只是计算出需要的大小,最终的创建顺着 _class_createInstanceFromZone 源码接着往下看到 obj = (id)calloc(1, size); ;这里传入size去计算。callocobjc 源码中只到这一步:

void    *calloc(size_t __count, size_t __size) __result_use_check __alloc_size(1,2);

calloc

接下来要到 libmalloc 源码中查找实现:

// 查找 calloc
void    *calloc(size_t __count, size_t __size) __result_use_check __alloc_size(1,2);
// 跳转 calloc
void *
calloc(size_t num_items, size_t size)
{
    return _malloc_zone_calloc(default_zone, num_items, size, MZ_POSIX);
}
// 跳转 _malloc_zone_calloc
MALLOC_NOINLINE
static void *
_malloc_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size,
        malloc_zone_options_t mzo)
{
    MALLOC_TRACE(TRACE_calloc | DBG_FUNC_START, (uintptr_t)zone, num_items, size, 0);

    void *ptr;
    if (malloc_check_start) {
        internal_check();
    }

    ptr = zone->calloc(zone, num_items, size);

    if (os_unlikely(malloc_logger)) {
        malloc_logger(MALLOC_LOG_TYPE_ALLOCATE | MALLOC_LOG_TYPE_HAS_ZONE | MALLOC_LOG_TYPE_CLEARED, (uintptr_t)zone,
                (uintptr_t)(num_items * size), 0, (uintptr_t)ptr, 0);
    }

    MALLOC_TRACE(TRACE_calloc | DBG_FUNC_END, (uintptr_t)zone, num_items, size, (uintptr_t)ptr);
    if (os_unlikely(ptr == NULL)) {
        malloc_set_errno_fast(mzo, ENOMEM);
    }
    return ptr;
}

关键方法在于 segregated_size_to_fit

image-20220417014420368

这也是个字节对齐的算法:

#define NANO_MAX_SIZE           256 /* Buckets sized {16, 32, 48, ..., 256} */
#define SHIFT_NANO_QUANTUM      4
#define NANO_REGIME_QUANTA_SIZE (1 << SHIFT_NANO_QUANTUM)   // 16 = 1 << 4 = 0001 0000
#define NANO_QUANTA_MASK        (NANO_REGIME_QUANTA_SIZE - 1)
#define NANO_SIZE_CLASSES       (NANO_MAX_SIZE/NANO_REGIME_QUANTA_SIZE)

// 方法与宏定义不在同个文件中
static MALLOC_INLINE size_t
segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey)
{
    size_t k, slot_bytes;

    if (0 == size) {
        size = NANO_REGIME_QUANTA_SIZE; // Historical behavior
    }
  // size + 15, 然后右移4位,
    k = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM; // round up and shift for number of quanta
    // 再左移4位
  slot_bytes = k << SHIFT_NANO_QUANTUM;                         // multiply by power of two quanta size
    *pKey = k - 1;                                                  // Zero-based!

    return slot_bytes;
}

size + 15, 然后右移4位,再左移4位,也就是进行16字节对齐。

在对象内部是8字节对齐,系统分配内存的时候却以16字节进行?这里涉及到以空间换时间的概念:

字节是内存的基本调度单位,但是CPU在读取内存的时候,并不是以字节为单位,而是以为单位。块的大小就是内存读取的粒度。如果不进行字节对齐,当CPU在频繁的读取内存时,要花费大量时间去计算需要分配多少字节。要想读取高效,就要约定一个规范,也就是字节对齐。

苹果采取16字节对齐是因为对象固定的isa指针占8字节,如果以8字节对齐,块是连续的内存,CPU读取的时候,读完8字节需要计算下一个块中的内容是否属于同个对象,这样计算量增加了。而16字节内存消耗大,换来时间消耗小。16字节是平衡时间和空间的后得出来的。比如32字节的对象在一般开发中比较少,而8~16之间比较多。字节对齐越大,浪费内存也就越多。(早期32位系统就是8字节对齐)。

对象的本质

当然是结构体了!新建一个项目:

#import <Cocoa/Cocoa.h>

@interface FFPhone : NSObject

// 价格
@property (nonatomic, assign) int price;
// 型号
@property (nonatomic, copy) NSString *type;

- (void)testPhone;

@end

@implementation FFPhone

- (void)testPhone {}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
    }
    return NSApplicationMain(argc, argv);
}

打开终端,cd到该目录。输入指令:

clang -rewrite-objc main.m

就会将main.m文件编译成main.cpp文件(c++语言的)。

#ifndef _REWRITER_typedef_FFPhone
#define _REWRITER_typedef_FFPhone
typedef struct objc_object FFPhone;
typedef struct {} _objc_exc_FFPhone;
#endif

extern "C" unsigned long OBJC_IVAR_$_FFPhone$_price;
extern "C" unsigned long OBJC_IVAR_$_FFPhone$_type;
struct FFPhone_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int _price;
    NSString *_type;
};

可以看到对象是 objc_object 结构体类型的。自定义的类,系统会自动生成类名+_IMPL 的结构体,并且包含一个 NSObject_IMPL 结构体类型的属性,包含isa属性:

struct NSObject_IMPL {
    Class isa;
};

也就是 objc_object 存储了isa 和 成员变量的值(接下来理解为数据成员,因为结构体成员保存的不是变量本身,只有值)。

结构体的对齐方式

结构体内存对⻬分为3个部分:

1.基本类型数据成员:结构体的第一个数据成员放在偏移量为0的地址,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小的整数倍开始(int为4字节,则要从4的整数倍地址开始存储,哪怕前面有一点空间浪费)。

2.结构体类型数据成员:结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a 包含成员 struct b,struct b 的数据成员有有char、int 、double等类型,那b应该从8的整数倍(double的大小)开始存储)。

3.整体字节对齐:结构体的总大小,也就是sizeof的结果必须是其内部最大成员的整数倍,不足的要补⻬。

验证代码:(这里简化了内存编号,实际例如 0x7abc001 等)


struct FFStructOne {
    // 8字节,0~7 ,
    double a;
    // 1字节,9
    char b;
    // 4字节,12~15
    int c;
    // 2字节,16~17
    short d;
    // 总的大小按最大成员所占的8字节进行对齐,所以占24字节
}structOne;

struct FFStructTwo {
    // 8字节,0~7
    double a;
    // 4字节,8~11
    int b;
    // 1字节,12
    char c;
    // 2字节,14~15
    short d;
    // 总的大小按最大成员所占的8字节进行对齐,所以占16字节
}structTwo;

struct FFStructThree {
    // 8字节,0~7
    double a;
    // 4字节,8~11
    int b;
    // 1字节,12
    char c;
    // 2字节,14~15
    short d;
    // 4字节,16~21
    int e;
    // 24字节,且最大成员占8字节,24~47
    struct FFStructOne ffStruct;
    // 总的大小按最大成员所占的8字节进行对齐,所以占48字节
}structThree;

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        NSLog(@"\n structOne'size = %lu;\n structOne'size = %lu;\n structOne'size = %lu;\n",
              sizeof(structOne),sizeof(structTwo),sizeof(structThree));
    }
    return NSApplicationMain(argc, argv);
}

/*
 structOne'size = 24;
 structOne'size = 16;
 structOne'size = 48;
 */

Tips: 为什么要从倍数大小开始读取?整数倍内存读取效率更快。

扩展

objc源码下载

objc源码地址

image-20220417173239308

点开12.2之后再搜索源码版本:

image-20220417173301261

xcode添加符号断点

image-20220416201943817

例如方法的符号 objc_alloc,每次进入符号都会进入汇编调试。

iOS第三方动画标签库内存泄露点的寻觅之旅
享受开发,颠倒银河
05-04 621
本博叙述了在使用第三库的过程中,从发现其中有泄露到定位到泄露,最后解决内存泄露的全过程。适合有一定经验的iOS开发者观赏。
Unity 如何使用KCP实现长连接
TxNet
01-16 5423
unity接入kcp 一.前言 KCP是一种基于UDP的上层协议,项目地址:KCP – A Fast and Reliable ARQ Protocol,以下是github上的介绍: KCP是一个快速可靠协议,能以比 TCP浪费10%-20%的带宽的代价,换取平均延迟降低 30%-40%,且最大延迟降低三倍的传输效果。纯算法实现,并不负责底层协议(如UDP)的收发,需要使用者自己定义下层数据包的发送方式,以 callback的方式提供给 KCP。 连时钟都需要外部传递进来,内部不会有任何一次系统调用。 整
iOS 底层原理-alloc流程
qq_37713953的博客
06-23 460
一.了解对象与指针 先看一张图: 这张图我们可知: imgv是指针,指针指向的是对象; [JPeople alloc]创建了一个对象; p1写在等号前面,等于把P1指向了那个对象的内存地址,所以p1是指针; 同理可知p2,p3也是指针,指向了P1指向了那个对象的同一块内存地址; 所以我们都是通过通过这个指针找到内存中的对象(通过指针来找到对象而不是表示对象) 二.底层探索的三种方式 2.1 下断点方式 按着control + in 进入真机调试(模拟器是x86架构,真机是arm64架构): in这
1、iOS底层分析 — alloc分析
shengdaVolleyball的博客
01-18 424
一、alloc init探索 简介: iOSalloc是为对象申请开辟内存的方发, 初始化的时候用的最多的就是如下形式。 [[xxx alloc] init]; 问题: 1、想要分析alloc从哪入手,怎么分析? 2、alloc底层都做了什么?在底层怎么实现的? 3、alloc的时候已经创建了对象,init里面什么都没有写(看过源码才知道的),那么init到底用来干什么呢? ...
iOS底层-alloc (1)
weixin_41632891的博客
09-07 1583
写在前面: iOS底层原理探究是本人在平时的开发和学习中不断积累的一段进阶之 路的。 记录我的不断探索之旅,希望能有帮助到各位读者朋友。 内容的总结专栏 iOS 底层原理探索 之 阶段总结 序 作为一名iOS开发人员,在平时开发工作中,所有的对象我们使用最多的是alloc来创建。那么alloc底层做了哪些操作呢?接下来我会一步一步探究alloc方法底层实现。 初探 我们先来看下面的代码 SMPerson *p1 = [SMPerson alloc]; SMPers
IOS底层原理(一)OC对象alloc原理
kyl282889543的博客
01-21 382
IOS底层原理(一)OC对象alloc原理
iOS 底层探究之 alloc
<sdffdsfsdfdfs>sfsfsfsdfsdffds</sdfsDS>Fsd
02-25 192
????????关注后回复“进群”,拉你进程序员交流群???????? 我们通过几个问题来探究下一个iOS如何获取到一个对象:alloc和init的区别?alloc方法做了哪些事情?alloc 和 init的区别从字面意思上,我们可以知道alloc是用来分配内存,init是用来初始化数据。下面我们通过代码来验证一下:NSObject*obj1=[NSObjectalloc]; NSObject*...
iOS开发--AVPlayer实现音乐播放器
热门推荐
李聪的博客
12-28 1万+
这是一篇教学Blog. 重点不完全在播放器上, 目的是通过这个过程掌握以下知识点: 单例block传值多线程代理传值通知观察者网络请求数据解析多控件布局开发模式和框架设计 今天敲一个音乐播放器, 音乐源我就不共享了, 涉及到版权保护, 别问我的源是哪儿来的. 不告诉你们
iOS基础-高级进阶面试题
花色1125的博客
08-03 1155
1、OC 语言的基本特点  OC 语言是 C 语言的一个超集,只是在 C 的基础之上加上了面向对象(oop) 的特性;  OC 与 Java 语言相同都是单继承,这一点与 C++语言不同(多重继承); OC 不支持命名空间机制,取而代之的是在类名之前添加前缀,以此来区分。  2、以下命名正确的是  (1)类 (Person、person、ObjectAndKeys、personAndOt
MMKV——基于 mmap 的高性能通用 key-value 缓存组件for iOS/macOS,Android ,Windows
小天空的博客
07-10 2130
MMKV——基于 mmap 的高性能通用 key-value 组件 https://github.com/tencent/mmkv MMKV 是基于 mmap 内存映射的 key-value 组件,底层序列化/反序列化使用 protobuf 实现,性能高,稳定性强。从 2015 年中至今在微信上使用,其性能和稳定性经过了时间的验证。近期也已移植到 Android / macOS / Windo...
IOS基础——alloc、init和new方法
weixin_30666753的博客
03-15 122
alloc:分配内存。 init:初始化。 new:代替上面两个函数:分配内存,并且初始化。 注意: 1.在实际开发中很少会用到new,一般创建对象时我们一般是 [[className alloc]init]; 2.区别只在于alloc分配内存的时候使用了zone它是给对象分配内存的时候,把关联的对象分配到一个相邻的内存区域内,以便于调用时消耗很少的内存,提升了程序处理...
iOS alloc 、retain、release函数
zzt5201314的专栏
12-24 956
alloc 函数是创建对象使用,创建完后计数器为1,只用一次 retain是对一个对象的计数器+1,可以调用多次 release是对一个对象计数器-1,减到0对象就会从内存中释放
iOS-对象实例化alloc方法
weixin_30897233的博客
06-27 389
写在最前:记录一个最近看到的一个问题,实例对象是alloc创建的还是init创建的?写的不好,还请看到的人轻喷! 有一个Person类,声明一个age属性 Person *p = [Person alloc]; p.age = 10; Person *p1 = [p init]; Person *p2 = [p init]; 复制代码首先有两个疑问: Q1: p1和...
iOS alloc到底怎么走
weixin_38016552的博客
06-21 162
alloc底层逻辑
iOS 底层原理之alloc探究(一)
weater1的博客
06-06 207
** iOS 底层原理之alloc 探究(一) 结论先行: 当初始一个对象[[Goods alloc] init] ,其中alloc方法主要通过_class_createInstanceFromZone 方法 做三件事 : 1.instanceSize (计算需要申请的内存大小) 2.calloc (向系统申请开辟内存,返回地址指针) 3.initInstanceIsa (通过isa 绑定相应的类) 说白了,就好比你是厂长,你告诉仓库管理员现在有一批货物要入库。 管理员首先看看这批货物有多少要多大的房间,
iOS alloc 底层原理
闫海强的博客
04-06 576
Created with Raphaël 2.2.0开始我的操作确认?结束yes 参考文档 Created with Raphaël 2.2.0开始我的操作确认?结束yesno
iOSalloc、init和new原理
最新发布
XY_Mckevince的博客
07-16 735
本文浅看了OC申请内存相关方法的源码,并作一流程图总结
iOS 底层探索alloc的原理01
lingjunjie的博客
04-18 268
运行场景,新建一个项目工程(我命名为zzz) ,创建一个新的类JSPerson,然后在view controller里面执行 代码:JSPerson * p = [JSPerson alloc]; 打上断点。 新建一个项目工程(我命名为zzz) 创建一个新的类JSPerson 在苹果的开源网站(https://opensource.apple.com/tarballs/)去download对应的源码。这边我们看的是alloc 所以找objc4-838. 在源码的main方法写上代码用来观察,alloc方法
iOS对象alloc流程和内存对齐
wywinstonwy的专栏
04-18 3677
在探索OC对象底层本质之前,先了解一下clang Clang clang是⼀个由Apple主导编写,基于LLVM的C/C++/Objective-C编译器 主要用于底层编译,将oc文件转换成c++文件,方便理解底层原理 OC是面向对象的语言,开发中一切的基础,首先需要一个对象,如果没有的话,可以alloc一个。本篇文章主要探索alloc的内部执行流程。 首先自定义Person继承与NSObject //创建对象 Person *person = [[Person alloc] init];
ip address dhcp-alloc
06-06
ip address dhcp-alloc是指通过DHCP服务器自动分配的IP地址。DHCP是动态主机配置协议,它允许网络管理员自动分配IP地址、子网掩码、默认网关等网络参数给客户端设备,从而简化了网络管理工作。当设备连接到网络时,...
写文章

热门文章

  • iOS底层-类的三顾茅庐(二) 732
  • iOS底层-消息发送机制 731
  • iOS底层-对象里都有什么 512
  • iOS底层-类的三顾茅庐(三) 497
  • iOS底层-alloc方法之旅 411

分类专栏

  • 清蒸鱼_iOS底层 7篇

最新文章

  • iOS底层-消息的转发
  • iOS底层-消息发送机制
  • iOS底层-类的三顾茅庐(三)
2022年7篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

玻璃钢生产厂家玻璃钢雕塑牛图片西藏玻璃钢门头雕塑苏州玻璃钢雕塑哪个公司好湖南玻璃钢人物雕塑北京周年庆典商场美陈批发价气球商场美陈装饰哪家质量好台州环保玻璃钢雕塑销售厂家商场内圣诞美陈图片嘉峪关玻璃钢植物雕塑镇江设计玻璃钢雕塑方案优质的玻璃钢雕塑麋鹿甘南彩色玻璃钢雕塑多少钱许昌标识玻璃钢人物雕塑惠州玻璃钢景观雕塑江苏玻璃钢雕塑供应商山西公园玻璃钢雕塑生产厂家景观玻璃钢花盆户外玻璃钢雕塑供应厂家龙岩园林玻璃钢雕塑批发百货商场春季美陈布置南明区玻璃钢雕塑价格河北主题商场美陈售价岑溪玻璃钢泡沫雕塑厂家昆明市玻璃钢玻璃钢雕塑公司濮阳玻璃钢人物雕塑厂家巴中动物玻璃钢雕塑长征玻璃钢雕塑甘南城市玻璃钢雕塑设计嘉峪关仿真玻璃钢雕塑制作张掖户外玻璃钢雕塑哪家好香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化