美文网首页
底层原理:内存管理

底层原理:内存管理

作者: 飘摇的水草 | 来源:发表于2022-08-05 17:00 被阅读0次
定时器

下面这种用法会存在内存泄露

#import "ThirdViewController.h"

@interface ThirdViewController ()
@property (nonatomic, strong) CADisplayLink *link;
@end

@implementation ThirdViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
 
    //__weak typeof(self) weakSelf = self; 加了这句也不管用
    CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(linkTest)];
    [link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    self.link = link;
}

- (void)dealloc
{
    [self.link invalidate];
}
@end

NSTimer用下面的方式也会引起内存泄露

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
   
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(linkTest) userInfo:nil repeats:YES];
    self.timer = timer;
}

而使用下面的方式则不会出现内存泄露

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    __weak typeof(self) weakSelf = self;
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
        [weakSelf linkTest];
    }];
}
内存布局

static变量只有一份,放在数据段区

Tagged Pointer
  • 从64bit开始,iOS引入了 Tagged Pointer 技术,用于优化NSNumber、NSDae、NSString等小对象的存储
  • 在没有使用Tagged Pointer之前,NSNumber等对象需要动态分配内存、维护引引用计数等,NSNumber指针存储的是堆中NSNumber对象的地址值
  • 使用 Tagged Pointe 后,NSNumber指针里面存储的数据变成了:Tag +Data,也就是将数据直接存储在了指针中
  • 当对象指针的最低有效位是1,则该指针为Tagged Pointer
  • 当指针不够存储数据时,才会使用动态分配内存的方式来存储数据
  • objc_msgSend能识别 Tagged Pointer ,比如 NSNumberintValue 方法,直接从指针提取数据,节省了以前的调用开稍
  • 如何判断一个指针是否为Tagged Pointer?
    • iOS平台,最高有效位是1(第64bit)
    • Mac平台,最低有效位是1
OC对象的内存管理
  • 在iOS中,使用引用计数来管理OC对象的内存
  • 一个新创建的OC对象引用计数默认是1,当引用计数减为0,OC对象就会销毁,释放其占用的内存
  • 调用retain会让OC对象的引用计数+1,调用release会让OC对象的引用计数-1
  • 内存管理的经验总结
    • 当调用alloc、new、copy、mutableCopy方法返回了一个对象,在不需要这个对象时,要调用release释放它
    • 想拥有某个对象,就让它的引用计数+1;不想再拥有某个对象,就让它的引用计数-1
    • 可以通过以下私有函数来查看自动释放池的情况:
extern void _objc_autoreleasePoolPrint(void);

使用时,先在文件上方声明一下:extern void _objc_autoreleasePoolPrint(void);,使用时这样即可:_objc_autoreleasePoolPrint()

MRC中的set方法

在MRC环境下,正确的 set 方法如下:

- (void)setPerson:(Person *)person
{
   if (_person != person)
   {
      [_person release];
      _person = [person retain];
   }
}

在MRC环境下常用的内存管理例子如下:

    //系统方式
    self.array = [NSMutableArray array];
    self.array = [[[NSMutableArray alloc]init] autoRelease];
    
    self.array = [[NSMutableArray alloc]init];
    [self.array release];
    
    NSMutableArray *array = [[NSMutableArray alloc]init];
    self.array = array;
    [array release];

或者用下面的方式给自定义对象创建工厂方法:

@interface Person : NSObject
+ (instancetype)person;
@end

@implementation Person
+ (instancetype)person
{
    return [[[self alloc]init]autoRelease];
}
@end

synthesis 关键字的用法,@synthesize person = _person 这句话表明生成一个 person 的成员变量,来代替 _person 的变量
在 MRC 环境下,需要在子类的 dealloc 方法里调用父类的 dealloc 方法

copy用法
  • 拷贝的目的:产生一个副本对象,跟源对象互不影响
  • 修改了源对象,不会影响到副本对象
  • 修改了副本对象,不会影响到源对象

iOS提供了2个拷贝方法

  1. copy,不可变拷贝,产生不可变副本
  2. mutableCopy,可变拷贝,产生可变副本,都是深拷贝
引用计数的存储
  • iOS中,引用计数可以直接存储在优化过的 isa 指针中,也可能存储中SideTable类中
  • extra_rc 里面存储的值是引用计数减1
  • has_sidetable_rc 引用计数器是否过大无法存储在 isa 中,如果为1,那么引用计数会存储在一个叫 SideTable 的类的属性中。
weak指针的原理

本质上是将弱引用存放在一个散列表中,即哈希表,对象要销毁时,则从表中取出当前对象对应的弱引用表,把表中存储的那些弱引用清除掉。

  • ARC都帮我们做了什么?
    • LLVM + Runtime相互合作的结果,利用LLVM编译器自动帮我们生成了 releaseautoReleaseretain 等代码,像弱引用这样的存在则需要运行时 Runtime 支持的
autorelease 原理
  • 自动释放池的主要底层数据结构是:__AutoreleasePool、AutoReleasePoolPage
  • 调用了 autoRelease 的对象最终都是通过 AutoReleasePoolPage 对象来管理的
  • 每个 AutoreleasePoolPage 对象占用4096字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放 autorelease 对象的地址,即一旦对象被调用了 autoRelease 方法,则对象的地址会被添加到 AutoreleasePoolPage
  • 所有的AutoreleasePoolPage 对象通过双向链表的形式连接在一起
  • 在开始调用的时候调用了 objc_autoReleasePoolPush 方法,在结束调用的时候调用了 objc_autoReleasePoolPop 方法
  • 调用push时,会传入 POOLBOUNDARY 并返回一个地址,起到一个标识作用,用来分割不同的@autoreleasepool。
  • 调用pop时,会传入end的地址,并从后到前调用对象的 release 方法,直到POOLBOUNDARY 为止。如果存在多个page,会从child的page的最末尾开始调用,直到POOLBOUNDARY。

如果是多层@autoreleasepool的嵌套,会用同一个AutoreleasePoolPage对象,以下面的三个嵌套为例,在同一个page中的顺序是下图这样,不同的@autoreleasepool以POOL_BOUNDARY做分割。

相关文章

  • 底层原理:内存管理

    定时器 下面这种用法会存在内存泄露 NSTimer用下面的方式也会引起内存泄露 而使用下面的方式则不会出现内存泄露...

  • OC底层原理汇总

    OC底层原理(一).alloc实际调用流程分析OC底层原理(二).内存分配与内存对齐OC底层原理(三)、isa、对...

  • 内存管理解析

    前言 今天我们大致分析下内存管理相关的底层原理等知识点,分为包括内存布局和内存管理方案两大块,其中内存管理方案会重...

  • iOS--OC底层原理文章汇总

    OC底层原理01—alloc + init + new原理OC底层原理02—内存对齐OC底层原理03— isa探究...

  • Swift语法 Swift5 【01 - 基础语法】

    作者: Liwx 邮箱: 1032282633@qq.com iOS Swift 语法 底层原理 与 内存管理分析...

  • Swift语法 Swift5 【05 - 可选项】

    作者: Liwx 邮箱: 1032282633@qq.com iOS Swift 语法 底层原理 与 内存管理分析...

  • 2022-09-20

    Runloop runtime kvo kvo多线程sdwebimage afnetwork底层原理内存管理定时器...

  • Swift底层原理-内存管理

    Swift底层原理-内存管理 Swift语言延续了和Objective-C语言一样的思路进行内存管理,都是采用引用...

  • iOS底层原理-内存管理

    CADisplayLink、NSTimer使用注意点: 1.CADisplayLink、NSTimer会对targ...

  • 【iOS 底层原理】内存管理

    一.定时器 1.CADisplayLink、NSTimer CADisplayLink、NSTimer 会对 ta...

网友评论

      本文标题:底层原理:内存管理

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