iOS开发之内存管理

iOS开发之内存管理

  • 一、垃圾回收机制
  • 二、内存管理的概念
  • 三、OC内存管理注意事项
  • 四、MRC相关语法

一、垃圾回收机制
与Java语言相同Objective-c 2.0之后,也提供了垃圾回收机制。OC是支持垃圾回收机制的(Garbage collection简称GC),macOS开发中是支持的。但是在iOS移动终端设备中,并不支持垃圾回收机制。因此,iPhone并不能对内存进行自动垃圾回收处理(autorelease),并且他与GC的机制是不一样的。
ARC是在IOS5之后推出的新技术,在Xcode4.2及之后的版本中由于引入了ARC(Automatic Reference Counting)机制,程序编译时Xcode可以自动给你的代码添加内存释放代码,如果编写手动释放代码Xcode会报错。因此需要注意垃圾回收机制并不是ARC,ARC也是需要管理内存的,只不过是隐式的管理内存,编译器会在适当的地方自动插入retain,release和autorelease.
知识拓展:在Java中,程序员不需要去关心内存动态分配和垃圾回收的问题,这一切都交给了JVM来处理。Java中标记垃圾的算法主要有两种, 引用计数法和可达性分析算法。

二、内存管理中的几个概念:
引用计算器:既retainCount,每个OC对象内部都有1个8字节空间用来存储retainCount,表示有多少”人”正在使用;
对象刚被创建时,默认计数值就为1,当计数值为0时,系统会自动调用dealloc方法将对象销毁
引用计数器的用法:给对象发送相应的技术操作来改变计数器的值
retain消息:使计数器+1
release消息:使计数器-1
retainCount消息:得到当前当前retainCount的值

三、OC内存管理开发中需要事项
其一野指针,其二内存泄漏。
1)野指针:即指针所指的对象已经被销毁,但后续还在使用该指针,此时指针指向了一个什么都不是的东西,我们称它为野指针,那么如何防止野指针的,一般处理的方式是对象进行release操作后,在赋值对象nil值。
2)内存泄漏:在操作对象是没有遵循内存配对原则,创建了对象了,却未对对象进行销毁,此时这个未被销毁的对象就是我们所谓的内存中泄漏的对象,这种行为也就是所谓的内存泄漏,内存泄漏不会影响对象的正常运行,但会影响程序的效率。
除此之外还有1)提前释放:如果没有使用空间直接free 。2)重复释放,如果你对一个空间进行了多次free 。

四、MRC(Manual Reference Counting)相关语法
1.关闭ARC
target -》 build setting - 》搜索 gar YES to NO

这里写图片描述

2.ARC和MRC混编
[工程]—>[Build Phases]—>[Compile Sources]—>
双击不想参与ARC的文件-fno-objc-arc
这里写图片描述

3.MRC管理黄金法则
1.凡是使用alloc,retain ,new ,copy(开头),mutableCopy(开头)的方法,都必须使用release 或者 autorelease 方法来【释放】
2.谁写alloc 谁负责release 那个类alloc 那个类release

4.一个简单的例子说明MRC
这里有一个Person类
这里写图片描述
这里是打印结果
这里写图片描述
我们在看看下面这一个例子
这里写图片描述
当执行release操作时,一个对象的引用计数为0时,它就会被释放掉。

AutoReleasePool

AutorealeasePool结构:
AutorealeasePool就是由AutoreleasePoolPage构成的双向链表,AutoreleasePoolPage是双向链表的节点
每一个AutorealeasePool都是由一系列的 AutoreleasePoolPage 组成的,并且每一个 AutoreleasePoolPage 的大小都是 4096 字节

什么是autorelease?
autorelease类似于C语言中Automatic variable自动变量,程序执行时,若某自动变量超出其作用域,该自动变量将被自动废弃。

autorelease何时释放?
1.当创建了局部释放池时,会在@autoreleasepool{}的右大括号结束时释放,及时释放对象大幅度降低程序的内存占用。
2.@autoreleasepool的情况下,Autorelease对象是在当前的runloop迭代结束时释放的(每个线程对应一个runloop),而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池。

AutoreleasePool能否嵌套使用?
可以嵌套使用,其目的是可以控制应用程序的内存峰值,使其不要太高 可以嵌套的原因是因为自动释放池是以栈为节点,通过双向链表的形式连接的,且是和线程一一对应的 自动释放池的多层嵌套其实就是不停的push哨兵对象,在pop时,会先释放里面的,在释放外面的。

AutoreleasePool的释放时机是什么时候?
App 启动后,苹果在主线程 RunLoop 里注册了两个 Observer,其回调都是_wrapRunLoopWithAutoreleasePoolHandler()。
第一个 Observer 监视一个事件:
监听 Entry(即将进入 Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池,优先级最高,保证创 建释放池发生在其他所有回调之前。
第二个 Observer 监视了两个事件:
BeforeWaiting(准备进入休眠) 时调用 _objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;
Exit(即 将退出 Loop) 时调用_objc_autoreleasePoolPop()来释放自动释放池。这个 Observer 的 order 是 2147483647,优先级最低,保证其释放池子发生在其他所有回调之后。

RunLoop 和 AutoreleasePool的关系
主程序的RunLoop在每次事件循环之前之前,会自动创建一个 autoreleasePool 并且会在事件循环结束时,执行drain操作,释放其中的对象

// MRC

NSAutoreleasePool *pool = [NSAutoreleasePool alloc] init];

id obj = [NSObject alloc] init];

[obj autorelease];

[pool drain];

// ARC

@autoreleasepool {

id obj = [NSObject alloc] init];

}

将main.m文件通过$ clang -rewrite-objc main.m重新编译生成发现aotuoreleasepool 被转换为为一个 __AtAutoreleasePool 结构体:

struct __AtAutoreleasePool {
  __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
  ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
  void * atautoreleasepoolobj;
};

这个结构体会在初始化时调用 objc_autoreleasePoolPush() 方法,会在析构时调用 objc_autoreleasePoolPop 方法。

这表明,main函数实际工作的时候,是这样的:


int main(int argc, const char * argv[]) {
    {
        void * atautoreleasepoolobj = objc_autoreleasePoolPush();
        
        // do things you want
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        
        objc_autoreleasePoolPop(atautoreleasepoolobj);
    }
    return 0;
}

所以autoreleasepool的实现主要靠objc_autoreleasePoolPush()和objc_autoreleasePoolPop() 来实现
那么 objc_autoreleasePoolPushobjc_autoreleasePoolPop又是什么呢?

void *objc_autoreleasePoolPush(void) {
    return AutoreleasePoolPage::push();
}

void objc_autoreleasePoolPop(void *ctxt) {
    AutoreleasePoolPage::pop(ctxt);
}

上面的方法看上去是对 AutoreleasePoolPage 对应静态方法 push 和 pop 的封装

AutoreleasePoolPage的定义如下

class AutoreleasePoolPage 
{
    //magic用来校验AutoreleasePoolPage的结构是否完整
    magic_t const magic;                   // 16字节
    //指向最新添加的autoreleased对象的下一个位置,初始化时指向begin();
    id *next;                              // 8字节
    //thread指向当前线程
    pthread_t const thread;                // 8字节
    //parent指向父节点,第一个节点的parent指向nil;
    AutoreleasePoolPage * const parent;    // 8字节 
    //child 指向子节点,第一个节点的child指向nil;
    AutoreleasePoolPage *child;            // 8字节
    //depth 代表深度,从0开始往后递增1;
    uint32_t const depth;                  // 4字节
    //hiwat 代表high water mark;
    uint32_t hiwat;                        // 4字节
    ...
}

如果我们的一个 AutoreleasePoolPage 被初始化在内存的 0x100816000 ~ 0x100817000 中,它在内存中的结构如下:
在这里插入图片描述其中有 56 bit 用于存储 AutoreleasePoolPage 的成员变量,剩下的 0x100816038 ~ 0x100817000 都是用来存储加入到自动释放池中的对象
begin() 和 end() 这两个类的实例方法帮助我们快速获取 0x100816038 ~ 0x100817000 这一范围的边界地址。
next 指向了下一个为空的内存地址,如果 next 指向的地址加入一个 object,它就会如下图所示移动到下一个为空的内存地址中。

POOL_SENTINEL(哨兵对象)
在每个自动释放池初始化调用 objc_autoreleasePoolPush 的时候,都会把一个 POOL_SENTINEL push 到自动释放池的栈顶,并且返回这个 POOL_SENTINEL 哨兵对象。

注:下篇文章博主会详细讲解各种属性修饰符,请持续关注;

### 将 Excel 表格转换Markdown 格式的两种方法 #### 方法一:通过在线工具实现转换 存在专门用于将Excel表格内容转换Markdown格式的在线平台[^1]。这些平台允许用户直接粘贴来自Excel的数据,随后会自动生成对应的Markdown语法表示形式。对于希望快速完成少量数据转换的任务来说非常便捷。 为了确保最终得到的是Markdown格式的结果,在输出设置部分应当确认选择了正确的选项卡——即“Markdown”。之后就可以轻松获取到适用于Markdown环境下的表格代码片段以便进一步利用或编辑。 ```markdown | index | 序号 | 姓名 | 生日 | 配偶工作 | 是否参见疗养 | 孩子出生日期 | 保险到期日 | |--| | 1 | 2 | 汤辟邦 | 1980.1.22 | 公务员 | 是 | 2006.6.1 | 2024.9.1 | | 2 | 3 | 周卓浩 | 1980.2.22 | 教师 | 否 | 2009.8.1 | 2024.9.1 | ``` #### 方法二:借助Python脚本自动化处理 另一种方式是编写简单的Python程序来读取Excel文件并将其中的信息转化为Markdown格式字符串。这种方法适合那些经常需要批量操作或是追求更高灵活性和技术挑战性的开发者们[^2]。下面给出了一段基础示例代码: ```python import pandas as pd def excel_to_markdown(file_path, sheet_name=0): df = pd.read_excel(file_path, sheet_name=sheet_name) markdown_table = df.to_markdown(index=False) return markdown_table if __name__ == "__main__": file_path = 'example.xlsx' result = excel_to_markdown(file_path) print(result) ``` 这段代码依赖于`pandas`库来进行Excel文件解析以及生成Markdown风格的表格结构。使用者只需提供目标Excel文件路径作为参数调用函数即可获得预期效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值