美文网首页
OC中的栈对象和堆对象

OC中的栈对象和堆对象

作者: 苍眸之宝宝 | 来源:发表于2022-01-07 09:24 被阅读0次

简介:

  在OC中我们说到对象通常指的是堆对象,因为对象都是alloc出来,而alloc都是在堆中分配对象的内存。但是我看到一片文章《Stack and Heap Objects in Objective-C》及文章的标题,我看到后大吃一斤,还可以有栈对象啊。

堆和栈:

  简要说明一下堆和栈:

  • 栈又叫堆栈,是一个存储局部变量、内部临时值和管理的内存区域。在现代系统中,每个执行线程都有一个栈。当调用一个函数时,一个栈帧被推送到栈上,并且函数本地数据存储在那里。当函数返回时,它的栈帧被销毁。所有这一切都是自动发生的,除了调用函数之外,程序员无需采取任何显式操作。
  • 堆本质上就是内存中的所有东西。内存可以在任何时候分配到堆上,也可以在任何时候销毁。您必须显式地请求从堆中分配内存,如果不使用垃圾收集,也必须显式地释放它。这是存储需要比当前函数调用存活时间长一些的内容的地方。当调用 mallocfree 函数时,您将访问是堆上的内存空间。

栈对象和堆对象:

  那么什么是堆对象,什么是栈对象呢?
  在Objective-C(和许多其他语言)中,一个对象只是一个具有特定布局的连续内存块;且对象通常是在堆中创建的:

NSObject *obj = [[NSObject alloc] init];

  上面的代码表示,创建一个类NSObject的对象赋值给指针变量 obj,其中obj是指针变量,地址存储在栈中,而[NSObject alloc]分配的内存是在堆中的。
  栈对象就是在栈上为该对象分配内存的对象;同样的堆对象即是在存储在堆内存上的对象。Objective-C没有任何直接的支持,但你可以手动构造一个这样的函数:

    struct {
        Class isa;
    } fakeNSObject;
    fakeNSObject.isa = [NSObject class];
    
    NSObject *obj = (NSObject *)&fakeNSObject;
    NSLog(@"%@", [obj description]);

栈对象的优点:

  栈对象显然是可行的。除了上面的例子,真正的语言,比如c++,都有对栈对象的语言支持。在c++中,你可以在栈或堆上创建对象:

    std::string stackString;
    std::string *heapString = new std::string;

  为什么c++中,会支持两种对象呢?这里不得不说一下堆对象的优点:

  • 速度快:在栈上分配和释放内存非常快。当您构建程序时,所有的分配内存和计算偏移量等工作都由编译器完成。在运行时,函数prolog只是为所有局部变量划分出它所需的空间,并且代码知道这些空间的位置,因为它们都是提前计算的。栈分配基本上是免费的,而堆分配可能非常昂贵。
  • 简单性:栈对象有一个已定义的生命周期,分配和释放都由编译器决定,不需要程序员手动管理;且因为它总是在声明它的作用域的末尾被销毁,不会造成内存泄漏。

栈对象的缺点:

  1. 严格定义的栈对象的生存期也是一个缺点,也是一个主要缺点:
      在Objective-C(和c++,以及许多其他语言)中,对象创建后不可能移动对象。这样做的原因是,可能有很多指向该对象的指针,而这些指针没有被跟踪。它们都需要更新以跟踪移动,但没有办法实现这一点。
    (注意:一般来说,这并不是不可能的,许多语言将对象移动作为一件理所当然的事情,通常作为垃圾收集方案的一部分。)
      在Cocoa中使用的一样,Objective-C使用一个引用计数机制来进行内存管理。这个系统的优点是,任何一个对象都可以有多个“ 所有者”(及多个指针指向对象),并且在所有所有者放弃所有权之前,系统不会允许该对象被销毁。
      栈分配的对象天生就有一个所有者,即创建它们的函数。如果Objective-C有栈对象,如果你把它传递给其他代码会发生什么?它们试图保留它?当创建对象的函数返回时,没有办法防止对象被销毁,所以retain不能工作。试图保留对象的代码将会失败,最终会产生空引用,并崩溃。

  2. 另一个问题是栈对象不是很灵活:
      在Objective-C中,实现初始化器销毁原始对象并返回一个新对象的情况并不少见。如何使用栈对象来实现呢?你真的不能这样做。Objective-C在运行时的灵活性很大程度上依赖于拥有堆对象,如果这样做就会舍弃这些灵活性。

OC中的栈对象:

  在Objective-C中存在栈对象吗?答案是肯定的,就是Block代码块,在swift中叫做闭包。当你使用^{}语法在函数中写块时,表达式的结果是一个栈对象。

  但是我上面讨论的那些问题呢?
  运行时动态的问题并不存在于块中。块有一个被语言固定的布局,如果不破坏二进制兼容性就不能改变它。块对象的大小可以在编译时计算,事实上,整个对象是由编译器生成的代码构建的,因此编写一个复杂的初始化器的可能性是不存在的。
  对象生存期的问题确实存在于块中,但没有那么严重。这样做的原因是,因为block是一种新的对象,从未存在于语言和任何代码中,处理一个block就需要将它复制一份到堆上(及是在堆上创建一个副本,如block外部的指针会指向堆上的副本),而不是保留它,如果它想被外部指针引用。

Block栈对象的其它缺点:

  Block的栈特性确实存在一些缺陷。例如下面的代码:

    void (^block)();
    if(x)
    {
        block = ^{ printf("x\n"); };
    }
    else
    {
        block = ^{ printf("not x\n"); };
    }
    block();

  Block栈对象只在其封闭作用域的生命周期内有效,在这里,它们的封闭作用域在结束调用Block()之前停止存在。当你将Block传递给不知道它们是Block的代码时,还会发生其他错误:

[dictionary setObject: ^{ printf("hey hey\n"); } forKey: key];

  字典将保留该块对象而不是复制它,导致空指针错误。

总结:

  栈对象的速度和简单性是块的一大福音,但它也为粗心的程序员创造了一种全新的错误。
原文链接

相关文章

  • OC中的栈对象和堆对象

    简介:   在OC中我们说到对象通常指的是堆对象,因为对象都是alloc出来,而alloc都是在堆中分配对象的内存...

  • 内存管理

    1.只有OC对象才需要进行内存管理的本质原因 --1.OC对象存放于堆中 --2.非OC对象存在栈中(栈内存会被系...

  • Ios面试复习--MRC内存管理

    1.只有OC对象才需要进行内存管理的本质原因 --1.OC对象存放于堆中 --2.非OC对象存在栈中(栈内存会被系...

  • iOS 内存管理

    1、只有OC对象才需要进行内存管理 1、OC对象存在堆中 2、非OC对象存在栈中(内存会被系统自动收回) ...

  • 内存管理相关

    1.堆和栈 堆:存放OC对象,先进先出。(吃了拉) 栈:存放非OC对象,先进后出。(吃了吐) 2.@propert...

  • iOS 内存管理

    范围: 任何继承了NSObject的对象,对基本数据类型无效。OC对象是放在堆内存里,非OC对象是放在栈内存里,栈...

  • iOS 堆和栈的区别

    OC对象存放于堆里面(堆内存要程序员手动回收) 非OC对象一般放在栈里面(栈内存会被系统自动回收) 堆里面的内存是...

  • 内存管理--简单整理

    课堂笔记: 只有OC对象才需要进行内存管理的本质原因是OC对象存放在堆里面,非OC对象一般放在栈里面(栈内存会被系...

  • GeekBand oc课程笔记

    oc课程笔记 堆和栈的区别 指针是存放在“栈”上,而对象时存放在“堆”上的,访问对象职能通过指针的方式来访问。 栈...

  • 读《Effective Objective-C 2.0》

    1、OC语言的起源 对象所占内存空间是分配在“堆空间”中,绝不会分配在“栈”上,不能再栈中分配OC对象,所以不能有...

网友评论

      本文标题:OC中的栈对象和堆对象

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