美文网首页
iOS中的内存管理

iOS中的内存管理

作者: 文小猿666 | 来源:发表于2021-03-19 14:04 被阅读0次

本节主要理解:
1.定时器的种类与注意事项(NSTimer循环引用/)
2.内存布局
3.Tagged Pointer
4.引用计数的原理
5.weak引用的原理
6.深拷贝与浅拷贝
7.自动释放池

一.读写安全

1.atomic与noatomic

2.pthead_rwlock

3.dispatch_barrier_async 栅栏函数
多读单写

dispatch_queue_t dispatchQueue = dispatch_queue_create("hyq.queue.next", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, block1_for_reading)  
dispatch_async(queue, block2_for_reading) 

dispatch_async(queue, block_for_writing)

dispatch_async(queue, block3_for_reading)  
dispatch_async(queue, block4_for_reading)

以上可能导致数据混乱
使用栅栏函数

dispatch_async(queue, block1_for_reading)  
dispatch_async(queue, block2_for_reading)

dispatch_barrier_async(queue, block_for_writing)

dispatch_async(queue, block3_for_reading)  
dispatch_async(queue, block4_for_reading) 

dispatch_barrier_async会把队列的运行周期分为这三个过程:

  • 首先等目前追加到并行队列中所有任务都执行完成
  • 开始执行dispatch_barrier_async中的任务这时候即便向并行队列提交任务,也不会执行
  • dispatch_barrier_async中任务执行完成后,并行队列恢复正常。

总的来说,dispatch_barrier_async起到了承上启下的作用。它保证此前的任务都先于自己执行,此后的任务也迟于自己执行。正如barrier的含义一样,它起到一个栅栏或者分水岭的作用。

使用并行队列和diapatch_barrier_async方法,就可以高效的进行数据和文件读写了。

二.Tagged Pointer

■从64bit开始, iOS引入了Tagged Pointer技术,用于优化NSNumber. NSDate. NSString等小对象的存储
■在没有使用Tagged Pointer之前,NSNumber等对象需要动态分配内存、维护引用计数等, NSNumber指针存储的是堆中NSNumber对象的地址值
■ 使用Tagged Pointer之后, NSNumber指针里面存储的数据变成了: Tag + Data ,也就是将数据直接存储在了指针中
当对象指针的最低有效位是1 ,则该指针为Tagged Pointer,当对象指针的最低有效位是0时,为普通OC对象
■当指针不够存储数据时 ,才会使用动态分配内存的方式来存储数据
■objc_ msgSend能识别Tagged Pointer ,比如NSNumber的intValue方法,直接从指针提取数据,节省了以前的调用开销

特点
1.我们也可以在WWDC2013的《Session 404 Advanced in Objective-C》视频中,看到苹果对于Tagged Pointer特点的介绍:
Tagged Pointer专门用来存储小的对象,例如NSNumber和NSDate
2.Tagged Pointer指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象了,它只是一个披着对象皮的普通变量而已。所以,它的内存并不存储在堆中,也不需要malloc和free。
3.在内存读取上有着3倍的效率,创建时比以前快106倍。
由此可见,苹果引入Tagged Pointer
,不但减少了64位机器下程序的内存占用,还提高了运行效率。完美地解决了小内存对象在存储和访问效率上的问题。

三.copy和mutableCopy

1.iOS提供了2个拷贝方法
copy,不可变拷贝,产生不可变副本
mutableCopy,可变拷贝,产生可变副本

深拷贝和浅拷贝
深拷贝---内容拷贝,有产生新对象
浅拷贝---指针拷贝,未产生新对象

总结
不可变对象NSString ,NSArray,NSDictionary,copy是浅拷贝,mutableCopy是深拷贝
可变对象NSMutableString,NSMutableArray,NSMutableDictionary,不管copy还是mutableCopy都是深拷贝

图片.png

为什么NSString使用copy修饰,NSMutableString使用strong修饰?
1.当原字符串是NSString时,由于是不可变字符串,所以,不管使用strong还是copy修饰,都是指向原来的对象,copy操作只是做了一次浅拷贝。
2.当源字符串是NSMutableString时,strong只是将源字符串的引用计数加1,而copy则是对原字符串做了次深拷贝,从而生成了一个新的对象,并且copy的对象指向这个新对象。

所以,如果源字符串是NSMutableString的时候,使用strong只会增加引用计数。但是copy会执行一次深拷贝,会造成不必要的内存浪费。而如果原字符串是NSString时,strong和copy效果一样,就不会有这个问题。
但是,我们一般声明NSString时,也不希望它改变,所以一般情况下,建议使用copy,这样可以避免NSMutableString带来的错误。

四.引用计数的存储

在64bit中,引用计数可以直接存储在优化过的isa指针中,也可能存储在SideTable类中

struct SideTable {
  spinlock_t slock;
  RefcountMap refcnts;
  weak_table_ t weak_table;
};

refcnts是一个存放着对象引用计数的散列表

五.autoreleasepool 自动释放池

■自动释放池的主要底层数据结构是 :_ AtAutoreleasePool. AutoreleasePoolPage
■调用了autorelease的对象最终都是通过AutoreleasePoolPage对象来管理的
源码分析

0 clang重写@autoreleasepool

0 objc4源码 : NSObject.mm

class AutoreleasePoolPage
{
  magic_ t const magic;
  id*next; 
  pthread_ _t const thread ; 
  AutoreleasePoolPage * const parent;
  AutoreleasePoolPage *child;
  uint32_ ,t const depth;
  uint32_ .t hiwat;
}
面试题

1.使用CADisplayLink、 NSTimer(基于runloop)有什么注意点 ?
循环引用
CADisplayLink、 NSTimer 会对taget产生强引用,如果target又对他们强引用,就会造成循环引用

此种方式会造成循环引用
self. timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target : self selector :@selector(timerTest) userInfo:nil repeats:YES];

(1)最简单的解决办法如下

_ weak typeof(self) weakSelf = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer *_ Nonnull timer) [
  [weakSelf timerTest];
});

(2)或者使用NSProxy,(专门用来做转发,是一个基类,与NSObject并列,方法调用时会省去第一阶段-消息发送阶段,直接进入第三阶段-消息转发阶段)自行了解
不准时
因为依赖于runloop,如果runloop任务过于繁重(如线程中有滚动视图正在滚动,NSTimer会暂停),可能会导致NSTimer不准时,可以使用GCD定时器更加准时

//创建-个定时器
dispatch_ source_ t timer = dispatch_ source_ create (DISPATCH_ SOURCE_ TYPE_ TIMER, 0, 0, queue); 
//没置吋囘(start是几秒后幵始抗行, interval是吋囘囘隔)
dispatch_ source_ ,set_ timer(timer,
dispatch_ time (DISPATCH_ TIME_ NOW, (int64_ t)(start * NSEC_ PER_ SEC)),
(uint64_ t)(interval *NSEC_ PER_ SEC),
0);
//设置回调
dispatch_ source_ ,set_ event_ handler(timer, ^{
));
//启动定时器
dispatch_ resume (timer);

2.介绍下内存的几大区域
地址从低到高布局

图片.png
■代码段: 编译之后的代码
■数据段
0字符串常量:比如NSString *str = @"123"
0已初始化数据:已初始化的全局变量、静态变量等
0未初始化数据:未初始化的全局变量、静态变量等
■堆: 通过alloc、malloc. calloc等动态分配的空间,分配的内存空间地址越来越大
■栈: 函数调用开销,比如局部变量。分配的内存空间地址越来越小

3.讲一下你对iOS内存管理的理解

4.autorelease在什么时机会被释放
① 如果有@autoreleasepool{},所以autoreleasepool里面调用了autorelease方法的对象会在{}结束之后释放。
② 如果没写@autoreleasepool{},由于整个程序没有退出,autoreleasepool里面调用了autorelease方法的对象会在RunLoop休眠之前被释放。

5.方法里有局部对象 ,出了方法后会立即释放吗
会立即释放,因为就相当于在方法的最后加一行release代码。

6.ARC 都帮我们做了什么?
实际上"引用计数式内存管理"的本质在ARC中并没有改变,ARC只是自动的帮助我们处理"引用计数"的相关部分

7.weak指针的实现原理

  • 在64bit中,引用计数可以直接存储在优化过的isa指针中,也可能存储在SideTable类中
struct SideTable {
spinlock_t slock;
RefcountMap refcnts;     refcnts是一个存放着对象引用计数的散列表
weak_table_t weak_table;   弱引用表
};
图片.png

当对象引用计数为0时,会自动调用dealloc方法。
苹果将弱引用都存在一个哈希表中,当对象调用dealloc方法时,系统会取出对象的地址,作为key到哈希表中找到对应的弱引用,并将其销毁掉。

相关文章

  • iOS 内存管理 部分三

    主要讲解日常开发中定时器的选择; iOS 内存管理 部分一iOS 内存管理 部分二iOS 内存管理 部分三i...

  • OC中内存管理

    在OC中内存管理MRC手动内存管理和ARC自动内存管理,ARC是从iOS 4.0开始,在iOS 4.0之前...

  • iOS之从MRC到ARC内存管理详解

    概述 在iOS中开发中,我们或多或少都听说过内存管理。iOS的内存管理一般指的是OC对象的内存管理,因为OC对象分...

  • iOS内功篇:内存管理

    iOS内功篇:内存管理 iOS内功篇:内存管理

  • ios内存管理

    ios内存管理 一.前言 在ios中,系统对每个程序运行时内存的占有...

  • iOS/OS X内存管理(二):借助工具解决内存问题

    上一篇博客iOS/OS X内存管理(一):基本概念与原理主要讲了iOS/OSX 内存管理中引用计数和内存管理规则,...

  • iOS总结内存管理

    说到iOS的内存管理,需要明白如下几个问题: 1、iOS内存管理的机制 内存管理是程序设计中很重要的一部分,程序在...

  • OC语法_IOS内存管理

    目录: 1、内存的定义 2、内存管理的基础概念 3、IOS系统中的内存管理 1、内存的定义 1.1. 内存是计算...

  • iOS夯实:ARC时代的内存管理

    iOS夯实:ARC时代的内存管理 iOS夯实:ARC时代的内存管理

  • 基本知识摘录

    一:内存管理的理解首先iOS中数据是存贮在堆和栈中的。内存管理需要管理堆上的内存,栈上的内存并不需要我们管理。非O...

网友评论

      本文标题:iOS中的内存管理

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