iOS的内存管理

来源:互联网 发布:asp.net windows认证 编辑:程序博客网 时间:2024/05/15 13:14

作者:Love@YR
链接:http://blog.csdn.net/jingqiu880905/article/details/51317114
请尊重原创,谢谢!

此文源于以前我给同事做的一次技术分享的PPT。

首先我们来——
认识 IOS 框架
请查看:http://blog.csdn.net/GooHong/article/details/28911301

红色部分是uikit , 其他, foundation, core data。
其中最重要最核心的框架是Foundation和UIKit。这两个框架能满足一个简单的app的开发。
最基础的NSObject UIView UIViewController(分别在foundation和UIKit里 mvc模式)

像图像读取 image i/o 在媒体层,我们无法控制,(如图片读取方式使得内存增加)
Block 块对象在core services 层,分配到栈上我们无法控制它的释放,但可以retain到堆上。
javascript我们也无法控制。

我们需要知道数据存储的区域:

  1. 栈区:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。(程序结束释放 加到autorelease pool里)
  2. 堆区:亦称动态内存分配。程序在运行的时候用alloc, copy, new, mutableCopy申请任意大小的内存,程序员自己负责释放。(出了方法作用域就释放)
  3. 静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。在程序结束时释放。它主要存放静态数据、全局数据和常量。(程序结束释放 加到autorelease pool里)
    eg:NSObject *a=[[NSObject alloc]init];
    变量a保存在栈上 指向等号后面被分配到堆上的内存。
    eg:NSString *str=@”abc”; 打印str的类型看到是NSCFConstantString 地址不在堆上(所以release直接崩溃),被放在常量区

如果指针置为了nil而对象还存在就会内存泄漏。
栈对象创建速度快,有严格的生命周期但不灵活,由哪个方法创建的,持有者就一直是它,不像堆对象可有n个owner。

什么内存空间是我们可控的

自己用alloc/new/copy/mutablecopy 生成的对象
非自己alloc copy new生成的对象都是autorelease的,程序结束时释放,就不要自己释放。
要自己释放的话也可以先自己retain变成自己持有再release
eg: NSArray *arr=[NSArray array];
eg:block copy

ARC和MRC
mrc(Mannul )arc(auto) Reference Counting
调用alloc new(alloc+init) copy mutablecopy生成并自己持有对象(自己的意思是开发者 而非系统)调用这些方法生成的对象retain cout 初始为1
dealloc 废弃对象
retain去持有 release减掉自己的持有(即释放,注意释放和废弃的区别)
retain assign copy 的实现原理
(ARC 下)Strong=retain weak=assign +nil
@property @systhesize getter setter

// assign-(void)setTestObject :(id)newValue{testObject= newValue;}// retain-(void)setTestObject :(id)newValue{if (testObject!= newValue) {[testObject release];testObject= [newValue retain];}}// copy-(void)setTestObject :(id)newValue{if (testObject != newValue) {[testObject release];testObject = [newValue copy];//新对象retain cout为1}}

ARC能解决一切内存问题?
答案:NO!
ARC只能保证我们自己创建的那部分对象逻辑上不存在内存泄漏或者过度释放的情况(如果用MRC就太需要小心了)不能保证运行时,也不能保证由框架提供的方法不会引起内存的增长
如循环引用(编译时不会提示你,只能靠自己)
如:tableView的重用机制(选择不重用在运行时内存猛增)
如图片加载方式(UIImage )
如通知NSNotification 没有在dealloc时remove观察者

内存管理的思考方式
自己生成的对象,自己所持有
非自己生成的对象,自己也能持有
不再需要自己持有的对象,释放
非自己持有的对象自己不能释放
当一块内存没有被任何strong/retain的指针指向时即可被废弃掉
(去计算一个非自己生成的对象的retain cout是没有意义的也是不需要的)

dealloc方法的大文章
nil和release的区别和谁先谁后?
为什么废弃对象之后要把指针置nil(防止悬垂指针)
当然是先[_xxx release];再 _xxx=nil;
只release不设置nil的话如果再用到这个指针就会崩溃 bad access
直接置为nil然后不release的话 内存还在那儿呢 内存泄漏

成员变量 release和[super dealloc]谁先谁后?
当然是先release本类的再[super dealloc]
因为你所创建的每个类都是从父类,根类继承来的,有很多实例变量也会继承过来,这部分变量有时候会在你的程序内使用,它们不会自动释放内存,你需要调用父类的 dealloc方法来释放,然而在此之前你需要先把自己所写类中的变量内存先释放掉,如果你先释放了父类,再去访问本类的变量,若此变量与其父类某变量有关,父类的内容已经不存在了,可能会崩掉。

这里有篇好文章:http://blog.sina.com.cn/s/blog_71715bf80101ioth.html

所以说dealloc里你会看到这样的代码:

//@property (readwrite, nonatomic, strong) NSTimer *activityIndicatorVisibilityTimer; //ARC下的dealloc - (void)dealloc {    [[NSNotificationCenter defaultCenter] removeObserver:self];//移除广播    [_activityIndicatorVisibilityTimer invalidate];//停止timer    [self removeObserver:self forKeyPath:keyPath context:AFHTTPRequestSerializerObserverContext];//移除观察者    if (_framesetterRef) {        CFRelease((__bridge_retained  CTFramesetterRef)(_framesetterRef));//CoreFoundation对象        _framesetterRef = NULL;    }    //void* buffer = malloc(bufferSize);    free(buffer);//malloc的对象需要手动free    [super dealloc];}

循环引用介绍
这里写图片描述

循环引用示例

Son类里有个成员变量mother #import "Mother.h"@interface Son : NSObject@property(nonatomic,assign,readwrite)Mother *mother;@end@implementation Son@synthesize mother;-(void)dealloc{//    [mother release];    NSLog(@"son dealloc");    [super dealloc];}@end
Mother里有个成员变量son@class Son;@interface Mother : NSObject@property(nonatomic,retain,readwrite) Son *son;@end@implementation Mother@synthesize son;-(void)dealloc{   //   [son release];    NSLog(@"mother dealloc");    [super dealloc];}@end
Mother *mm=[[Mother alloc]init];// mother对象m1 Son *er=[[Son alloc]init];//son对象s1 mm.son=er; er.mother=mm; [mm release];  [er release]; 
  1. 成员变量属性都为retain 循环引用
    m1:1—–s1:1—–s1:2—-m1:2—-m1:1—-er:1 双方的dealloc都不执行 两块内存m1和s1都不释放 内存泄漏
  2. son为retain mother为assign
    A: Mother dealloc里释放成员变量son,Son dealloc里释放成员变量mother
    m1:1—–s1:1—–s1:2—-m1:1—-m1:0—m1执行dealloc 释放son 所以s1:1
    —-s1:0—–s1执行dealloc 释放mother可是m1已经为0了 崩溃
    B: Mother dealloc里释放成员变量son,Son dealloc里不释放成员变量mother
    m1:1—–s1:1—–s1:2—-m1:1—-m1:0—m1执行dealloc 释放son 所以s1:1
    —-s1:0—–s1执行dealloc 完美执行 两块内存都恰当释放
    C: Son dealloc里释放成员变量mother,Mother dealloc里不释放成员变量son
    m1:1—–s1:1—–s1:2—-m1:1—-m1:0—m1执行dealloc不释放son 所以s1仍然为2
    —-s1:1 最后m1释放了s1没有释放 内存泄漏

结论: dealloc里需要释放retain 的成员变量!不要在dealloc里release assign的成员变量!

容易引起循环引用的3种场景

Delegate:如tableView的delegate
Block 解决方法:block里self, self的成员变量都转成weak
NSTimer timer设置target为self会retain self self又有timer这个strong属性 造成循环引用,解决方法在viewWillDisappear里把timer invalidate

0 0