代码编织梦想

  • iOS内存管理(上)简单的说了下retain、release和dealloc。不过关于内存管理还有个比较重要的东西autoreleasepool,也是兄弟们常说的自动释放池

作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群:196800191,加群密码:112233,不管你是小白还是大牛欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!

1. 自动释放池autoreleasepool

1.1. autoreleasepool 结构分析

  1. 兄弟们在main.m常常会看到过
int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
  1. 我们首先通过clang看下他的结构是啥样子的
    xcrun -sdk iphonesimulator clang -rewrite-objc main.m

  2. 得到main.cpp文件,定位位置

  3. 我们看下他的结构

他就是个结构体,有自己的构造和析构

1.2. autoreleasepool 压栈追踪

  1. 我们看下构造函数
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
  1. 然后找到他的压栈处理
void *
objc_autoreleasePoolPush(void)
{
    return AutoreleasePoolPage::push();
}

这个时候,我们遇到了个AutoreleasePoolPage,到这了,那我们就看看这个的结构

1.2.1. 拓展 AutoreleasePoolPage

  1. 我追寻的过程
  2. 有个比较官方的名词解释,咱们先看一下
* magic 用来校验 AutoreleasePoolPage 的结构是否完整;

* next 指向最新添加的 autoreleased 对象的下一个位置,初始化时指向begin() ;

* thread 指向当前线程;

* parent 指向父结点,第一个结点的 parent 值为 nil ;

* child 指向子结点,最后一个结点的 child 值为 nil ;

* depth 代表深度,从 0 开始,往后递增 1;

* hiwat 代表 high water mark 最大入栈数量标记

好吧,到了这一步,大家可能和我第一次过来一样,比较懵逼。。这个是干啥用呢,咋还有父节点和子节点

  1. 这个时候看下自动释放池实现的官方文档
/***********************************************************************
   Autorelease pool implementation

   A thread's autorelease pool is a stack of pointers.
   Each pointer is either an object to release, or POOL_BOUNDARY which is
      an autorelease pool boundary.
   A pool token is a pointer to the POOL_BOUNDARY for that pool. When
      the pool is popped, every object hotter than the sentinel is released.
   The stack is divided into a doubly-linked list of pages. Pages are added
      and deleted as necessary.
   Thread-local storage points to the hot page, where newly autoreleased
      objects are stored.
**********************************************************************/

  1. 我来翻译一波了,😆(展示下我的大白话)

一个线程的自动释放池 是一个栈 --> 由好多指针组成(说白了就是栈结构,所以有后面的出栈和压栈。)

一个池子里的标记指向POOL_BOUNDARY(边界/哨兵),当这个池子被释放的时候,比POOL_BOUNDARY 还hot的进行 release

这个栈 划分为 双向链接的页,页 在必要的时候进行增加或者删除

本地线程 存储的是 新进来的,并把它设成 hot page(聚焦页面)

这样的话,这个页结构就好懂了些。由于是双向链接的页,所有有了parent和child

1.3. autoreleasepool压栈实现

  1. 那我们看下压栈的处理
	static inline void *push() 
    {
        id *dest;
        if (slowpath(DebugPoolAllocation)) {
            // Each autorelease pool starts on a new pool page.
            dest = autoreleaseNewPage(POOL_BOUNDARY);
        } else {
            dest = autoreleaseFast(POOL_BOUNDARY);
        }
        ASSERT(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
        return dest;
    }

每一个自动释放池 会在一个新页面上启动

  1. 我们追踪下
  2. 如果刚开始加
	//上面还有些乱起把遭的。。太长了,我就不沾了    
    // Install the first page.
        AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
        setHotPage(page);
        
        // Push a boundary on behalf of the previously-placeholder'd pool.
        if (pushExtraBoundary) {
            page->add(POOL_BOUNDARY);
        }
        
        // Push the requested object or pool.
        return page->add(obj);

简单来说就是。如果没有页面,就给他个。并设置成hotpage,第一个页面的话会给个 POOL_BOUNDARY,然后add

  1. 看下add代码
	id *add(id obj)
    {
        ASSERT(!full());
        unprotect();
        id *ret = next;  // faster than `return next-1` because of aliasing
        *next++ = obj;
        protect();
        return ret;
    }

这个就是他核心的push代码。(就是一步一步压,然后平移next指针)

  1. 如果页面满了
	id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
    {
        // The hot page is full. 
        // Step to the next non-full page, adding a new page if necessary.
        // Then add the object to that page.
        ASSERT(page == hotPage());
        ASSERT(page->full()  ||  DebugPoolAllocation);

        do {
            if (page->child) page = page->child;
            else page = new AutoreleasePoolPage(page);
        } while (page->full());

        setHotPage(page);
        return page->add(obj);
    }

由于它是双向链接结构的页。所以页面满了,会找下一个页。通过child,然后把最新也设置成hotpage,在add

  1. 补充

上面其实就是他的压栈。

补充知识:听说面试有人问到过:就是一个自动释放池只有一个哨兵/边界,一页最大容量是505,第一页是哨兵加上504(当然这个大家也可以通过源码知道)

1.4. autoreleasepool 出栈

  1. autoreleasepool的出栈其实就是压栈的反向操作。然后通过parent结点release
  2. 首先看下我们刚才的的出来的析构
//构造
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
//析构  参数是构造得出的对象
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
  1. 兄弟们看下我的探究过程

我只是直接把找的核心代码,这些代码兄弟们可以去看看。会更加清晰。(我感觉有注释,人家写的代码又好,就不做过多的解释了,😆)

  1. 我们看下releaseUntil
    void releaseUntil(id *stop) 
    {
        // Not recursive: we don't want to blow out the stack 
        // if a thread accumulates a stupendous amount of garbage
        
        while (this->next != stop) {
            // Restart from hotPage() every time, in case -release 
            // autoreleased more objects
            AutoreleasePoolPage *page = hotPage();

            // fixme I think this `while` can be `if`, but I can't prove it
            while (page->empty()) {
                page = page->parent;
                setHotPage(page);
            }

            page->unprotect();
            id obj = *--page->next;
            memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
            page->protect();

            if (obj != POOL_BOUNDARY) {
                objc_release(obj);
            }
        }

        setHotPage(this);

#if DEBUG
        // we expect any children to be completely empty
        for (AutoreleasePoolPage *page = child; page; page = page->child) {
            ASSERT(page->empty());
        }
#endif
    }



这就是objc源码的好处,注释让人很舒服。 总结下就是:这个就是通过parent寻找,然后next进行- -操作,
不是哨兵的话objc_release

好了,希望对大家有帮助吧。我又要加班了~~😿o(╥﹏╥)o
原文作者:小谷先森
原文地址:https://juejin.cn/post/6899718676585545742

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接: https://blog.csdn.net/iOSxiaodaidai/article/details/111083468

图片跟着鼠标飞-爱代码爱编程

相信很多小伙伴在浏览很多网页的时候,鼠标的样式发生了改变,或者说是有一张图片跟着鼠标一起动,那么这个跟着鼠标动的图片是如何实现的呢,今天教大家来实现这个动画。 选择图片 因为我们要一张图片跟着鼠标一起动。那么第一步肯定是选择一张好看的照片,最好是选择一张gif可以动的照片 我选择的就是这个会飞的小天使 搭好css的内容 .图片选好之后,我们需要搭建

selenium处理12306出发地value值修改不成功-爱代码爱编程

不知道你们在使用ui框架编写12306时,有没有遇到过这样的问题. 在使用selenium去编写场景时发现出发地这个input标签,每次都没办法按照你的预期去修改值 例如: 首先在浏览器里使用document发现完全可以修改掉输入框的值.然后兴致勃勃的 使用webdriver来加载js语法 from_stat_js = """ var a

H5如何实现电子签名并生成PDF文档的?-爱代码爱编程

作者:coyota666 来源:https://juejin.cn/post/6901273585428463624 前言 电子签名通俗来说就是通过技术手段实现在电子文档上加载电子形式的签名,其作用类似于纸质合同上的手写签名或加盖的公章。虽然电子签名多年来合法性一直遭到质疑,但其在企业工作流审批、请柬、单据保全等场景应用广泛,最近的

使用Arthas神器,定位解决SpringBoot接口超时问题(附诊断流程)-爱代码爱编程

来源:segmentfault.com/a/1190000020383866 背景 公司有个渠道系统,专门对接三方渠道使用,没有什么业务逻辑,主要是转换报文和参数校验之类的工作,起着一个承上启下的作用。 最近在优化接口的响应时间,优化了代码之后,但是时间还是达不到要求;有一个诡异的100ms左右的耗时问题,在接口中打印了请求处理时间后,和调

英语阅读里正确答案与错误答案的特征,不认识单词也可以蒙对!-爱代码爱编程

说起考研英语,大多数同学们最早准备的不是阅读,不是完型,而是单词。但是,天天记忆单词,效果却并不明显,今天就带大家看看考研英语阅读理解正确答案与错误答案的特征,希望在阅读上可以得更多的分~ 正确选项特点 首先说正确选项命制特点,英语二多来源于原词复现,在定位句中会出现相同词汇或意群表达,若来自同一意群且满足修饰搭配关系,则初步认为选项正确;英

自动驾驶中车辆的如何使用点云定位?-爱代码爱编程

点击上方“3D视觉工坊”,选择“星标” 干货第一时间送达 标题:Review on 3D Lidar Localization for Autonomous Driving Cars 作者:Mahdi Elhousni and Xinming Huang 翻译:particle 欢迎各位加入免费知识星球,获取PDF文档,欢迎转发朋友圈,

iOS开发实战-第4节-进一步完善微博页面-爱代码爱编程

本节内容 为上一节创建的微博页面增加插图 知识点 为重复的方法新建一个函数,抽象出创建图片的方法。 按照 4:3 的宽高比切分图片。 使用 Divider() 绘制一个细分割线 Post 结构体中仅保存于数据有关的内容,而将View相关的只读属性存放在extension中。 添加评论和点赞按钮。 修改TableView默认样式,不显示默认的

iOS开发实战-第3节-解析json-爱代码爱编程

本节内容 Codable 解码以解析json swift 的异常处理方法 知识点 Codable 可编码 A type that can convert itself into and out of an external representation. 一个可以将自己转换为和脱离外部表示的类型。 通常是创建一个实例对象,在实例对象中创

iOS 边学边记 深入了解weak底层实现详解-爱代码爱编程

iOS开发者都知道,当一个对象被释放时,所有对这个对象弱引用的指针都会释放并置为nil,那么系统是如何存储这些弱引用对象的呢?又是如何在一个对象释放时,将这些指向即将释放对象的弱引用的指针置为nil的呢?下面我们通过分析SideTable的结构来进一步了解内存管理的弱引用存储细节。 weak结构 在runtime中,有四个数据结构非常重要,分别是Sid

IOS 中json解析异常Domain=NSCocoaErrorDomain Code=3840之诡异事件-爱代码爱编程

前言 就在今天遇到一个很诡异的技术问题,Unity调用Quick苹果SDK支付。Quick 支付需要游戏(Unity)传入一些数据到原生OC中,格式是json,在OC中打印的日志看着数据是没问题的,但是解析json却一直显示失败 。问题最终还是解决了,用倒推法给大家说一下解决的思路。 场景重现 下面代码是OC中定义的支付方法,void *payD

iOS OC动态运行时(runtime)~动态特性表现-爱代码爱编程

一、概念: 因为运行时 runtime 是 必须到运行时(run time)才会做一些事情; 所以OC的动态特性表现为了三个方面:动态类型、动态绑定、动态加载; 动态:主要是将数据类型的确定由编译时,推迟到了运行时。之所以叫做动态, 二、iOS OC动态运行时~动态类型 1.动态类型,就是id类型。 2.动态类型是跟静态类

微软将开始强制部分用户升级 Windows 10 | 新闻拍一拍-爱代码爱编程

  导读:更多:• WPS 进入全国计算机二级考试 • Brave 被迫剔除了 iPad/iOS 版本的广告奖励项目 本文字数:935,阅读时长大约:1分钟 作者:硬核老王 微软将开始强制部分用户升级 Windows 10 在微软宣布结束对 Windows 10 版本 1903 的支持之后,从本月开始,微软将开始强制部分用户升级