iOS的内存管理

作者: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的成员变量!
修改:dealloc 不是retainCout为0之后执行! 是retaincount为1时再执行release的话 就去执行dealloc然后执行完retainCount为0
上述例子retainCount为1时 没有再执行release方法 那么就走不到dealloc

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

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

2018年10月11日补充:
前阵子有一个需求是关于语音聊天的,其中在聊天界面有个缩小按钮,点击缩小按钮会出现一个浮窗,然后可以从present聊天窗口界面的viewcontroller点击返回,但是浮窗还是一直保留,直到点击浮窗之后聊天窗口出现点击关闭按钮。
其中聊天几面我们暂且叫VoiceViewController,浮窗叫FloatWindow
代码大概这样

VoiceViewController * vc = [VoiceViewController new]; 
FloatWindow *fw = [FloatWindow new];
vc.floatWindow = fw;
fw.delegate = vc;

按照我们一般逻辑,遇到delegate肯定声明为weak来保证双方的释放问题,但是这里的需求,当VoiceViewController pop出去之后 浮窗对象要求依然存在,只要浮窗对象存在,点击它就要再动画放大出现聊天界面,故只有当浮窗对象执行其close方法释放其所有资源如定时器等时才真正结束通话,继而释放掉VoiceViewController
所以这里我们把delegate声明为strong类型

FloatWindow.m 里
- (void)close {
  [timer invalite];
  _delegate = nil;
 }

我们看一下这俩对象如何释放的

{
VoiceViewController * vc = [VoiceViewController new];  //viewcontroller对象被vc引用 retaincout:1
FloatWindow *fw = [FloatWindow new];// floatwindow对象被fw引用 retaincout:1
vc.floatWindow = fw; //floatwindow对象被vc的_floatWindow引用 retaincout:2
fw.delegate = vc;//viewcontroller对象被fw的_delegate引用 retaincout:2
[fw close];
}

因为FloatWindow 的close方法 _delegate = nil; viewcontroller对象retaincout变为1
再然后出了这个函数调用区域其中的临时变量会各自执行一次release
那么viewcontroller对象:0
floatwindow对象:1
然后VoiceViewController执行dealloc
dealloc里_floatWindow=nil;那么floatwindow对象retaincout变为0 走dealloc
完美释放~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值