内存管理-最新版

oc中变量的声明周期

1.static变量。oc中只有静态全局变量和静态局部变量,都是初始化一次,生命周期与app运行周期一至

  1. 局部变量:出了作用域就会release一次 3,类的属性,成员与类的生命周期一致

    oc中类没有静态成员变量

内存管理本质

在ios 中数据分为两类,oc对象和非oc对象,oc对象继承与NSObject存储在堆上有程序员管理内存,非oc对象存储与栈上有系统管理内存

ios中内存管理无论arc还是mrc均指的是堆上oc对象的内存管理

引用计数

ios中,每一个oc对象都有一个与之对应的整数“引用计数器”,引用计数器用来表示它被引用的次数。ios就是采用这种引用计数的方法来管理内存

引用计数遵守的规则是

1.当创建一个对象并获取使用权(Alloc/new)是引用计数为1

2、当其他对象获得对象的所有权(retain)后,对象的引用计数加1

3.当其他对象方法释放对对象的所有权(release),对象的引用计数减1

4.当对象的引用计数为0时,系统会自动调用它的dealloc方法来收回内存

MRC内存管理的必须遵守的黄金法则

1.谁创建,谁释放

2.返回对象为autorelease

3.对象alloc,retain,copy, mutable后均需要release一次

4.release后要手动设置为nil,来防止野指针的出现

ARC (自动内存管理)原理:

ARC实际上是编译器的一个属性,可以在xcode的build setting来设置是否使用。 arc遵守手动内存管理的原则,通过评估对象的生命周期,在编译时期,编译器会在适当的位置添加retain和release操作来确定对象只要引用计数不为0就保存在内存中,一旦引用计数为0就自动释放

ARC下内存泄漏有哪些?

1.重复性Timer 需要自动invalid才会释放内存 2.非oc对象,ARC只管理oc对象的内存,如core Foundation的对象需要手动释放内存 3.循环引用会出现内存泄露

循环引用概念

对象a 强引用对象b。对象b强引用对象a。这就叫循环引用,会导致ab都不能释放内存,造成内存泄露,需要把其中一条引用改为weak引用来解决循环引用

循环引用出现的场景

1.storyboard或者xib上outlet控件与视图控制器viewcontroller的关系 视图控制器肯定强引用(strong) xib或者storeboard 所以outlet 一般用weak 但如果是自定义的outlet 需要使用strong

1.Delegate 导致的循环引用,使用weak解决, 比如tableview 与ViewController 是strong 那viewController对tableView的Delegate必须用weak

3.block 为了确保进入作用于的对象可以正确访问,block会隐士的对其进行retain操作, 如果对象(strong)持有了block,那在block内部有访问对象(self.),就会 导致循环引用,解决办法 block 外面声明weak 的weakself, block内部使用strong 声明一个self来解决 如果在block中使用的对象没有持有strong block就不会产生循环引用

NSTimer的block形式,Notification的Block形式,GCD的block形式 都有可能产生循环引用

3.Timer 如果NSTImer是使用Selector的形式声明的,需要使用一个中间类NSProxy来打破循环引用

什么是Block,Block的本质是什么?block作为属性为什么用copy修饰

block的本质是封装了函数调用和函数调用上下文环境的oc对象,也叫代码块,类似于一个方法。因为代码块的内存是系统自动管理的,所以block必定存储在栈区

当我们使用block作为一个对象的属性,因为对象是存储在堆区,所以需要把block从栈区拷贝到堆区,否则就是超出block的作用域访问,会导致崩溃, 一般使用retain修饰block会让对象有block属性的所有权,并保证block不会提前消失, 但block 的retain实现和copy实现是一样的,所以block作为属性用copy修饰

block中如何解决循环引用(retain cycle)问题

block:无论MRC 还是ARCblock修饰的变量(对象NSString等和基本数据类型int等)都是可以修改的

MRC 模式 下采用block解决循环引用,因为MRC下BLOCK所指的对象不会做retain操作

ARC 模式 下采用weak解决循环引用,但被weak修饰的对象在block中无法修改,

__weak ,strong, block循环引用三种的关系是什么?

在ARC模式下, 如果对象强引用(strong)block,在block内部直接访问对象self,会产生循环引用的问题,这是使用__weak修饰对象self。可以解决循环引用,但存在这个对象在外部被释放后block内部无法访问的情况,所以在block内部用strong修饰self来保证内部可以正确访问,有因为strong时在block内部属于局部变量,block结束时自动释放,所以不会造成内存泄露

这用__weak strong 就能保证对象既可以正确访问,有不会产生循环引用和内存泄露

系统如何保证timer在执行某个方法时这个方法时有效的

为了保证timer在执行的时候依然的有效的,timer的接受者target会对timer 进行retain操作,timer有一次性和重复性两者,一次性timer调用后自动invalidate ,而重复性timer时永生的,需要手动调用invalidate才会释放内存,所以我们要在合适的时机调用invalidate,

timer =nil能停止timer吗?

不能,系统停止timer的方式是invalidate,而且invalidate停止timer完成了两件事1,讲timer的targer设置为nil,相当于timer =nil的作用,2 解决runloop对timer的强引用,这样timer才能真正的停止,所以单独的timer= nil不能停止timer

自动释放池

自动释放池是使用@autoreleasepool修饰的代码块,自动释放池中用autorelease的属性会在代码块的作用域结束时自动调用release方法

在ios的app主线程启动时会自动创建一个自动释放池。在使用array的等方法的block版本也会自动创建自动释放池,但是当使用大量内存的地方需要手动创建自动释放池来避免内存吃紧

自动释放池的使用原则:

1.多个自动释放池是以栈的形式存储,最近创建的肯定在栈顶。autorelease对象会自动加入离它最近的自动释放池 2.自动释放池会在其作用域结束时给池中的每一个autorelease对象发送一条release消息

autorelease对象什么时候释放

1.自动释放池会在其作用域结束时给池中的每一个autorelease对象发送一条release消息 2.为加入自动释放池的autorelease对象会在对象所属线程的runloop迭代结束时释放

因为一个线程对应一个runloop。每一个runloop都是隐式的创建一个自动释放池,runloop结束时会销毁它的自动释放池,而自动释放池销毁时会给池中的每一个autorelease对象发送一条release消息。

release是否一定释放内存

调用对象的release方法,只是对象的引用计数减一并不保证会释放内存。因为释放对象内存需要等到引用计数为0,所以如果引用计数为2的情况下调用release也不会释放内存。

autorelease和release区别:

release后对象的引用计数会立马减一。

autorelease后对象的引用计数不会立马减一,而是在合适的时机才减一。

自动释放池.线程.runloop 的关系

一个线程对应一个runloop,系统为每一个runloop隐式的的创建一个自动释放池,所有的自动释放池构成一个栈式结构,在每个runloop结束时,该runloop的autoreleasepool会被销毁,自动释放池销毁时池 中的autorelease对象会调用一次release来释放

属性关键字assign。Weak, retain,strong,copy, nonatiomic,atomic, dynamic,synthesize,

unsafeunretained __autoreleasing autorelease 区别和联系

ARC模式下使用weak strong copy unsafeunretained
MRC 模式下使用assigh retain

retain strong 均为强引用,会让对象的引用计数加1 assign weak为弱引用,对象的引用计数不变, assign用于基本数据类型,weak用于oc对象,而且weak会保证对象在消失时指针自动赋值为nil unsafeunretained 不安全的弱引用 ,等价于assign

copy 不会增加对象的引用技术,主要针对于copy array等,赋值时使用的是传入的一份拷贝,来保证传入对象和对象的不互相影响,这个属性只针对实现NSCopying协议的对象有效

atomic:默认的属性修饰,原子的,提供多线程的保护操作,是说属性的读和写事原子性的,可以保证set 和get方法是线程安全的,但不是说整个属性是线程安全的,只是提高多线程安全的几率 nonatomic:非原子的,操作速度快,但不提高读写的多线程保护

synthesize 默认的属性修饰,除非开发人员做了,否则系统会自动实现setter 和getter方法(意思是开发实现了setter和getter系统就不会再自动实现。开发没做,系统会自动提供) dynamic 意识是有开发者实现setter 和getter方法,如果不实现就会崩溃

__autoreleasing ARC下自动释放。用它修饰的对象会自动加入自动释放池中 autorelease MRC 下的自动释放

retain与copy区别 NSString 为什么使用copy

retain 相当于ARC的strong,实在指针拷贝,2个对象公用一个内存,会导致对象的引用计数加1 copy 是内容拷贝,对实现了NSCopying协议对象如NSString等有效, 会产生新的副本,老对象的引用计数不变,新旧对象不会互相影响

NSString 使用copy是为了防止传入的NSString参数被无意间修改, 如果传入的参数不是共享的,都建议使用copy修饰

深浅拷贝 copy mutablcopy

浅拷贝:是内存地址的复制,新旧对象指向同一片内存空间,旧对象引用计数加1

深拷贝: 是对象内容的复制,新旧对象指向不同的内存空间,二者内容相同,但不互相一样,新对象引用计数为1,旧对象引用计数不变, 深拷贝可以减少对上下文的依赖

copy是前拷贝, mutableCopy是深拷贝

不同对象的深浅拷贝

类型转化bridge(桥接)

类型转化 bridge,也叫桥接,是指oc对象和cf对象之间的类型转化。oc对象只Foundation对象 CF对象指CoreFoundation对象

桥接的原理: 每一个能到进行类型转化的oc对象都是一个类簇。类簇是一个公开的抽象类,实际使用功能的都是它的私有子类。创建一个oc对象就说获取类簇的一个私有子类,而这个私有子类有与之对应CF类。而且二者内存结构相同,所以可以互相转换

举例说明:NSString 是一个类簇。oc中创建对象获取的是子类NSCFString,在cF中有内存结构相同的CFString,所以NSString 与CFString 可以互相进行类型转化

ARC下的类型转化修饰符 __bridge 仅仅做类型转化,不转化内存管理方式

——bridge_retained 添加retain操作防止对象的提前释放,用于oc对象转cf对象

_bridge_transfer 转移对象的所有权,用于cf对象转为oc对象

使用atomic一定是线程安全的吗

No.atimaic是对属性的修饰词,是说属性的读和写是原子性的, 也就是说getter 和setter 方法是安全,但不代表线程安全,只是增加了线程安全的几率,

举例说明:a线程写操作时,其他线程会因为原子属性而等待,a写完后。b写,此时若a要读取就必须等待b完成,但b完成后a读取的是b修改后的值,这样就已经破坏了线程安全,如果此时有一个c在a读取前release 更会导致程序崩溃,要想线程安全必须给线程添加lock。

一句话,atomic只是保证了getter 和setter方法的线程安全,并不能保证整个属性对象的线程安全。

自动释放池使用场景

1.在需要大量内存的地方需建立独立的自动释放池,来保证对象的及时释放.避免内存吃紧

2.oc的内置类(比如NSString)的类方法都是autorelease类型的会加入自动释放池中,

3.容器的block版本的枚举会自动添加一个自动释放池

ARC使用注意事项:

1.arc内存管理法则适用于oc对象及其变量 属性,局部变量,其中oc会自动初始化为nil

2.非oc对象比如CoreFoundation框架的CF开头的对象需要手动释放内存。

3.对于静态变量(全局变量和静态变量)是存放在静态数据区,生命周期就是app的生命周期.

4.arc下的autoreleasing.相当于mrc下的autorealse, 而且arc下引用传入参数(对应的参数类型是**)会自动转化为autoreleasing.

arc的三不可原则:

不可使用new开头命名属性,

不可使用NSZone。

不可直接调用dealloc方法,重写dealloc时不许调用[super dealloc].

计算机组成部分

运算器,控制器,存储器,输入,输出

运算器和控制器是合在一起叫中央处理器,即CPU。

内存:指cpu可直接读写访问数据的地方,寄存器在cpu内不算内存

缓存:存储器是用来临时存储数据的,也叫缓存,为了提高cpu的效率,会在cpu中开辟一小块内存,作为缓存,运算时会将数据从内存复制到只临时存储区域内,运算就在此进行

寄存器,运算器 控制器是紧密的联系在一起的,频率一致,所以运算时不会因为数据的来回传输和设备的茶几导致性能的下降。

虚拟存储系统: 指的是物理地址和逻辑地址的相互转换

虚拟内存

虚拟内存是一个内存管理技术,是让应用以为自己有足够的连续内存来运行,实际上这些内存是不连续的,有的在内存中,有的在外部磁盘中,运行时,根据需要将内存不存中暂时不用的数据和外部磁盘的数据进行交换,是windows的内存管理技术

ios没有虚拟内存,当内存不足时会清退不活动的app,因为ios有推送服务,一般app没有挂后台的理由。按下home键。app挂起,并按使用频率排序,内存不足时,靠后的app会首先被压缩,如果还不够则会被清退(就是强制关闭)

ios进程管理机制

ios支持系统级别的多任务,比如自己需要后台ip电话,只要setting中选择voip即可,但这样的app一般一次只能运行一个。

ios 里面启动app。会产生一个启动进程(BSD进程)。BSD进程被ios ,按home键或者使用手势回到桌面,并不会杀死进程,而是保存上下文环境休眠了,可以相应ios 的APNS 推送或者本地推送来唤醒并作出相应

语言程序的内存分布

从低地址到高地址依次是: 1.text :代码区 存储程序的可执行代码,生命周期贯彻整个app的运行周期,只读 2.静态数据区(.data):存放程序已出初始化的全局变量,静态变量和常量 3.bss:存放未初始化的全局变量 static 变量

  1. heap(堆): 存放动态分配的内存段,内存由开发者管理 5.stack(栈):用于存放局部变量,内存由系统自动管理

未初始化的全局变量和静态变量存放在BSS区(.bss)。 已初始化的全局变量和静态变量存放在静态数据区(.data)。

ios数据分类

1.oc对象:使用指针来访问的对象,比如NSString NSNumber等,存放在堆上,内存需要开发者手动管理,ios内存管理说的就是堆上数据的内存管理

2.非oc对象,如CFString之类mac开发的数据类型, 基本数据类型,int bool 等,无需开辟内存。存放栈区或者静态数据区,有系统自动管理内存

ios的根据三种数据的内存管理方式

1.从静态存储区域分配

内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在,比如全局变量,static 变量

2.在栈上创建.有系统编译器自动管理内存,

如函数内的局部变量,在生命周期结束时也就是函数执行结束时自动释放,特点是效率高,但容量有限

3.在堆上创建,有程序员手动管理内存

就是动态内存分配,程序在运行时通过new 等申请,通过free等释放,有程序员自己管理.

崩溃如何处理

1.公司内部测试出现崩溃,直接把测试机拿来。连上打包测试版的电脑上xcode。打开崩溃日志,一般会自动翻译为代码形式,可以看出崩溃在哪一行

2.已经上线appstore,直接在xcode上下载崩溃日志windw-organizer 选择crash,就可以看到想要的崩溃日志,会直接翻译成代码可读的形式,点击右侧箭头就可以直接进入相应的代码行。修改即可

JSRUN前端笔记, 是针对前端工程师开放的一个笔记分享平台,是前端工程师记录重点、分享经验的一个笔记本。JSRUN前端采用的 MarkDown 语法 (极客专用语法), 这里属于IT工程师。