iOS用户态内存管理分为五大区:栈,堆,全局区,常量区,代码区
如果程序占用内存过大,系统可能会强制关闭程序,造成程序崩溃,闪退现象。 所以,内存管理是进行合理的内存分配,内存回收,释放不需要再使用的对象的管理过程。 从内存分布图可以看出,只有堆区存放的数据需要由程序猿分配和释放,其他区的数据由系统分配和释放,通常所说的IOS内存管理是对堆区的内存管理。 学习链接:juejin.cn/post/707263…
涉及相关点:
--沙盒目录:通过操作NSFileManager打点每个资源目录下的size, 对缓存进行统计检测.
--内存缓存
区别:
沙盒目录:
iOS中每个应用都有自己对应的沙盒。互相独立,互相不能访问 bundle就是通常所说的应用程序在手机里边的安装路径,是一个目录:这个目录就是程序的main bundle 沙盒目录: Documents:用于存放程序中的文件数据,应用程序在运行时生成的一些需要长久保存的数据 Library: (Cache):存放缓存文件,从网络上下载的文件或者数据(如:音乐缓存、图片缓存等),此目录下的文件不会在应用退出时自动删除 ,需要程序员手动清除改目录下的数据。iTunes、iCloud 备份时不会备份此目录下的数据。主要用于保存应用在运行时生成的需要长期使用的数据,一般用于存储体积较大,不需要备份的非重要数据。 (Preferences):存放的是基于NSUserDefaults的设置数据,文件格式为 “plist”。设置应用的一些功能会在该目录中查找相应设置的信息,iTunes、iCloud备份时会备份此目录下的数据。该目录由系统自动管理,通常用来储存一些基本的应用配置信息。比如账号密码、自动登录等。 tmp:存放应用运行时产生的一些临时数据和文件,当应用程序退出、系统磁盘空间不足、手机重启时,都会自动清除该目录的数据。无需程序员手动清除该目录中的数据,iTunes、iCloud备份时不会备份此目录 .app文件:包含程序中的nib⽂件、图片、音频等资源。
获取程序的根目录:
**
NSString *homeDirectory = NSHomeDirectory();
NSLog(@"homeDirectory = %@",homeDirectory);
获取document目录:
**
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSLog(@"documentPath = %@",documentPath);
获取Caches目录:
**
NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSLog(@"cachesPath = %@",cachesPath);
获取Library目录:
**
NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
NSLog(@"libraryPath = %@",libraryPath);
获取Tmp目录:
**
NSString *tmpDirectory = NSTemporaryDirectory();
NSLog(@"tmpDirectory = %@",tmpDirectory);
获取xxx.app程序包:
**
NSBundle *myBundle = [NSBundle mainBundle];
NSLog(@"app程序包 = %@",myBundle);
获取程序的根目录:
**
NSString *homeDirectory = NSHomeDirectory();
NSLog(@"homeDirectory = %@",homeDirectory);
获取document目录:
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSLog(@"documentPath = %@",documentPath);
获取Caches目录:
NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSLog(@"cachesPath = %@",cachesPath);
获取Library目录:
NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
NSLog(@"libraryPath = %@",libraryPath);
获取Tmp目录:
NSString *tmpDirectory = NSTemporaryDirectory();
NSLog(@"tmpDirectory = %@",tmpDirectory);
获取xxx.app程序包:
程序启动以后,会根据需要从该目录动态加载代码或资源到内存
在越狱设备上如果更改了目录内容,对应的签名就会被改变,这种情况下苹果官网描述的后果是应用程序将无法启动,我没实践过
NSBundle *myBundle = [NSBundle mainBundle];
NSLog(@"app程序包 = %@",myBundle);
2.礼物资源下载及存储以及使用
3.爆屏礼物资源管理,内存占用过高 如何解决?
可能的问题:
1.什么是ARC:
就是代码中自动加入了retain/release, 原先需要手动添加的用来处理内存管理的引用计数的代码可以自动的由编译器完成了。ARC是编译器特性,不是iOS运行时特性,他也不是其他语言的垃圾回收器,因此ARC和手动内存管理性能是一样的,有些时候还能更加快速,因为编译器还可以执行某些优化
2.为什么weak指针指向的对象在废弃之后会被自动设置为nil:
weak的原理是在底层维护了一张weak_table_t结构的hash表,key是所指对象的地址,value是weak指针的地址数组。
weak关键字的作用是弱引用,所引用对象的计数器不会+1,在引用对象被释放的时候,weak指针自动设置为nil
对象释放时,根据对象地址获取所有weak指针数组,然后遍历这个数组,把其中的数据置nil.然后把这个从weak表中删除,最后清理对象的记录
3.autoReleasePool是怎么实现的:
定义:是OC中的一种内存自动回收机制,它可以延迟加入AutoReleasePool的对象的release时.
由若干个autoreleasePoolpage以双向链表的形式组合组成的栈结构,一个autoreleasePoolpage的空间被占满的时候会新建一个autoreleasePoolpage对象,
---通过代码添加的autoreleasepool,在作用域结束时,随着pool的释放,就会释放pool中的对象,这种情况是及时的,不依赖于runloop
---系统在runloop开始的时候创建一个pool,结束的时候会对pool中的对象执行release操作:由系统自动进行释放
iOS在主线程的Runloop中注册了两个Observer,第一个Observer监听了KCFRunLoopEntry事件,会调用objc_autoreleasepush(),这个Observer的order优先级是-2147483647,优先级最高,保障创建释放池发生在其他回调之前;第二个Observer监听了KCFRunloopWaiting事件,会调用objc_autoreleasepop(),objc_autoreleasepush(),监听了KCFRunloopExit事件,会调用objc_autoreleasepop()这个Observer的order优先级是2147483647,优先级最低,保障其释放池子发生在其他回调之后
在主线程执行的代码,通常是写在诸如时间回调,timer回调,这些回调会被Runloop创建好的Autoreleasepool环绕,所以不会出现内存泄漏,所以开发者也不需要显式创建pool了
原理:
- 自动释放池的`本质是一个AutoreleasePoolPage结构体`对象,是一个`栈结构`存储的页,每一个`AutoreleasePoolPage`都是以`双向链表的形式连接`。
- 自动释放池的`压栈`和`出栈`主要是通过结构体的构造函数和析构函数调用底层的`objc_autoreleasePoolPush`和`objc_autoreleasePoolPop`,实际上是调用`AutoreleasePoolPage`的`push`和`pop`两个方法。
调用`push操作`其实就是创建一个新的`AutoreleasePoolPage`,而`AutoreleasePoolPage`的具体操作就是插入一个`哨兵POOL_BOUNDARY`,并返回插入`哨兵POOL_BOUNDARY`的内存地址。而`push`内部调用`autoreleaseFast`方法处理,主要有以下三种情况:
- a.当`page存在,且不满`时,调用`add`方法将对象添加至`page`的`next`指针处,并将`next`指向下一位;
- b.当`page存在,且已满`时,调用`autoreleaseFullPage`初始化一个新的`page`,然后调用`add`方法将对象添加至`page`栈中;
- c.当`page不存在`时,调用`autoreleaseNoPage`创建一个`hotPage`,然后调用`add`方法将对象添加至`page`栈中。
调用`pop操作`时,会传入一个值,这个值就是`push`操作的返回值,即`哨兵POOL_BOUNDARY`的`内存地址token`。所以`pop`内部的实现就是根据`token`找到`哨兵对象`所处的`page`中,然后使用 `objc_release` 释放 `token`之前的对象,并把`next` 指针到正确位置。
4.什么是循环引用,你遇到的循环引用是怎么样的,是怎么解决的
形成一个循环引用时,对象之间的持有关系形成一个向环
对于堆内存的持有方式有两种:
栈内指针指向堆内存:不会产生环
堆内指针指向堆内:可能会产生环
1>NSTimer https://juejin.cn/post/6844903968250789896
2>delegate 代理使用weak修饰
3>block 在block外部对弱化self,再在block内部强化已经弱化的weakSelf
4>WKWebView
5.内存泄露监测与解决
内存泄漏是指程序中已动态分配的堆内存由于某种原因程序未释放或者无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃
循环引用
OOM
6.CoreFoundation:CoreFoundation属于MRC方式的管理,用完申请的内存后进行手动free
7.检测方法
手动检测
工具检测
静态分析
动态分析
自动化检测
MLeaksFinder
FBRetainCycleDetector
8.野指针