您的位置:首页 > 移动开发 > IOS开发

IOS内存管理机制

2016-05-25 22:21 603 查看
内存管理:如何正确释放堆上的空间

内存压根儿就没释放---------内存泄露

在使用之前,内存被释放了 ------提前释放

释放过后又释放内存---------重复释放

















非自动管理内存的使用原则:

alloc retain new 以copy开头的方法 以mutableCopy开头的方法,都要相应的使用release
autorelease

自己收拾的自己工作

//retainCount 专门用来计数 引用计数
//retain,copy,new,mutableCopy 给计数器加1 (方法)
//release 给计数器减1(方法) 当retainCount=0,内存释放

int main()
{
Car *car = [[Car alloc] init];

Person *p1 = [[Person alloc] init];
[p1 setCar:[car retain]];
NSLog(@"%ld",[[p1 car] retainCount]);

Person *p2 = [[Person alloc] init];
[p2 setCar:[car retain]];
NSLog(@"%ld",[[p2 car] retainCount]);

[[p2 car] release];

NSLog(@"car = %ld",[car retainCount]);
[p2 release];

[[p1 car] release];
NSLog(@"car = %ld",[car retainCount]);

[p1 release];

[car release];
}


在ARC情况下使用规则





property属性访问声明

readwritereadonlyassignretaincopynonatomic属性的作用

@property是一个属性访问声明,扩号内支持以下几个属性:
1,getter=getterName,setter=setterName,设置setter与getter的方法名
2,readwrite,readonly,设置可供访问级别
2,assign,setter方法直接赋值,不进行任何retain操作,为了解决原类型与环循引用问题
3,retain,setter方法对参数进行release旧值再retain新值,所有实现都是这个顺序(CC上有相关资料)
4,copy,setter方法进行Copy操作,与retain处理流程一样,先旧值release,再Copy出新的对象,retainCount为1。这是为了减少对上下文的依赖而引入的机制。

copy是在你不希望a和b共享一块内存时会使用到。a和b各自有自己的内存。
5,nonatomic,非原子性访问,不加同步,多线程并发访问会提高性能。注意,如果不加此属性,则默认是两个访问方法都为原子型事务访问。锁被加到所属对象实例级(我是这么理解的...)。

atomic和nonatomic用来决定编译器生成的getter和setter是否为原子操作。在多线程环境下,原子操作是必要的,否则有可能引起错 误的结果。加了atomic,setter函数会变成下面这样:

property 与@synthesize

当你定义了一系列的变量时,需要写很多的getter和setter方法,而且它们的形式都是差不多的,,所以Xcode提供了@property和@synthesize属性,@property用在 .h 头文件中用作声明,@synthesize用在.m 文件中用于实现。
如下,新建一个基于“Command Line Tool”的项目,名为“property”,再新建一个Student类,
传统的写法是:
Student.h
[cpp] view plaincopyprint?
//
//  Student.h
//  property
//
//  Created by Rio.King on 13-8-25.
//  Copyright (c) 2013年 Rio.King. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface Student : NSObject
{
int age;
int no;
}

//age的getter和setter方法声明
- (int)age;
- (void)setAge:(int)newAge;

//no的getter和setter方法声明
- (int)no;
- (void)setNo:(int)newNo;

@end

Student.m
[cpp] view plaincopyprint?
//
//  Student.m
//  property
//
//  Created by Rio.King on 13-8-25.
//  Copyright (c) 2013年 Rio.King. All rights reserved.
//

#import "Student.h"

@implementation Student

//age的getter和setter方法的实现
- (int)age
{
return age;
}
-(void)setAge:(int)newAge
{
age = newAge;
}

//no的getter和setter方法的实现
- (int)no
{
return no;
}
- (void)setNo:(int)newNo
{
no = newNo;
}

@end

main.m
[cpp] view plaincopyprint?
//
//  main.m
//  property
//
//  Created by Rio.King on 13-8-25.
//  Copyright (c) 2013年 Rio.King. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Student.h"

int main(int argc, const char * argv[])
{

@autoreleasepool {

// insert code here...
Student *stu = [[Student alloc] init];
stu.age = 100;//这句相当于setter方法
NSLog(@"age is %i", stu.age);//这里的 stu.age 相当于getter方法

[stu release];

}
return 0;
}

------------------------------------------------------------------------------------------------------------------------
用@property和@synthesize的写法是:
Student.h
[cpp] view plaincopyprint?
//
//  Student.h
//  property
//
//  Created by Rio.King on 13-8-25.
//  Copyright (c) 2013年 Rio.King. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface Student : NSObject
{
int age;
int no;
}

//当编译器遇到@property时,会自动展开成getter和setter的声明
@property int age;
@property int no;

@end

Student.m
[cpp] view plaincopyprint?
//
//  Student.m
//  property
//
//  Created by Rio.King on 13-8-25.
//  Copyright (c) 2013年 Rio.King. All rights reserved.
//

#import "Student.h"

@implementation Student

//@synthesize 会自动生成getter和setter的实现
//@synthesize 默认会去访问age,no,height同名的变量,,
//如果找不到同名的变量,会在内部自动生成一个私有同名变量age,no,height,,
//因此Student.h 中的这几个变量也可以省略不写。
@synthesize age,no;

@end

main.m
[cpp] view plaincopyprint?
//
//  main.m
//  property
//
//  Created by Rio.King on 13-8-25.
//  Copyright (c) 2013年 Rio.King. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Student.h"

int main(int argc, const char * argv[])
{

@autoreleasepool {

// insert code here...
Student *stu = [[Student alloc] init];
stu.age = 100;
NSLog(@"age is %i", stu.age);

[stu release];
}
return 0;
}

几点说明:
1.在Xcode4.5及以后的版本中,可以省略@synthesize ,编译器会自动帮你加上getter 和 setter 方法的实现,并且默认会去访问
_age这个成员变量,如果找不到_age这个成员变量,会自动生成一个叫做 _age的私有成员变量。
2.建议变量名用"_"前缀作为开头,但我看big Nerd 那本书里是不用的,个人也比较习惯 big Nerd 的那种写法,所以变量名就不加前缀了。Y^o^Y


自动释放池是什么,如何工作
当您向一个对象发送一个autorelease消息时,Cocoa就会将该对象的一个引用放入到最新的自动释放池。它仍然是个正当的对象,因此自动释放池定义的作用域内的其它对象可以向它发送消息。当程序执行到作用域结束的位置时,自动释放池就会被释放,池中的所有对象也就被释放。
1. ojc-c 是通过一种"referring counting"(引用计数)的方式来管理内存的, 对象在开始分配内存(alloc)的时候引用计数为一,以后每当碰到有copy,retain的时候引用计数都会加一, 每当碰到release和autorelease的时候引用计数就会减一,如果此对象的计数变为了0, 就会被系统销毁.
2. NSAutoreleasePool 就是用来做引用计数的管理工作的,这个东西一般不用你管的.
3. autorelease和release没什么区别,只是引用计数减一的时机不同而已,autorelease会在对象的使用真正结束的时候才做引用计数减一.

ARC

1. ojc-c 是通过⼀一种 "referringcounting"( 引⽤用计数 ) 的⽅方式来管理内存的 , 对象在开始分配内存 (alloc) 的时候引⽤用计数为⼀一 , 以后每当碰到有 copy,retain 的时候引⽤用计数都会加⼀一 , 每当碰到 release 和 autorelease 的时
候引⽤用计数就会减⼀一 , 如果此对象的计数变为了 0, 就会被系统销毁

ARC 的规则非常简单:只要还有一个变量指向对象,对象就会保持在内存中。使用ARC后,不需要再手动调用retain,
release, autorelease, 因为编译器处理了一切。

ARC property关键字

新关键字strong,__strong, weak, __weak(“__”为双下划线,(unsafe_unretained,__unsafe_unretained为iOS4.0不支持weak而使用,等同于assign,目前可以不考虑)strong
强引用等同于非ARC的retain

@property (nonatomic, retain) 改为使用@property (nonatomic, strong)

成员变量默认也为strong属性,可写作__strong NSString *str; 或省略直接写为NSString *str;

方法中的strong变量,在方法后会自动释放,类的strong成员变量在类释放时会自动释放。

weak 弱引用

与assign类似,但不全相同。当对象释放时会自动设置为nil而不需显式的设置。

如:

__weak NSString *testStr;- (void)test

{

NSString *abc = [NSString stringWithFormat:@"abc"];testStr = abc;

abc = [NSString stringWithFormat:@"def"];

// testStr会自动等于nil, 即使没有立刻变为nil,在下一个程序循环中也会变为nil,这点与assign不同,assign仍然会有指向空地址的指针,需要手动设为nil,否则再使用就造成crash

}

delegate及xib的outlet 的属性应该设置为@property @property (nonatomic, weak)

copy和之前的copy一样,复制一个对象并创建strong关联

注意点只要还有一个变量指向对象,对象就会保持在内存中。处理不好就会造成对象不会释放。



比如:
NSTimerNSTimer *timer;
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:selfselector:@selector(timerAction) userInfo:nil repeats:YES];
timer的repeat会对self有引用,而self也对timer有引用,即使页面返回了,self及timer也不会释放。
解决方法是,在页面返回前停止timer [timer_ invalidate],可视实际情况放在viewWillDisapper里执行。(放在dealloc没用,页面没有释放不会执行dealloc)
延时执行
[self performSelector:@selector(delayFunc) withObject:nilafterDelay:5];
即使页面返回,也必须等此延时方法执行后才会释放。因此需要在页面返回前执行
[NSObject cancelPreviousPerformRequestsWithTarget:self];
(当然这里指的是一般情况,必须要此方法执行的话可以不用写这句,特殊情况特殊处理:)

不过 ARC 只能作用于 Objective-C 对象,不能释放 Core Foundation对象。 因此这里你仍然需要调用 CFRelease()来释放该对象。

ARC下__strong和__weak的区别
和使用strong 合适用weak

先用Xcode新建一个命令行工程



新建一个Person类

在.m文件中重写dealloc函数

- (void)dealloc

{

NSLog(@"Person is dealloc");

}

先了解ARC的基本原理

/*

ARC的判断准则:只要没有强指针指向对象,就会释放对象

指针分2种:

1>强指针:默认情况下,所有指针都是强指针 __strong

2>弱指针: __weak

*/

打开main函数
第一个测试
int main(int argc, const char * argv[])
{

@autoreleasepool
{
Person *p = [[Person alloc]init]; //有一个强指针指向Perosn对象
//在这里 把p为空
p = nil; //这行代码过后 清空指针对象没有强指针指向 对象会被释放
/*会打印*/
/*  Person is dealloc */
NSLog(@"-------");
}
return 0;
}
第二个测试
int main(int argc, const char * argv[])
{

@autoreleasepool
{
Person *p = [[Person alloc]init]; //有一个强指针指向Perosn对象
Person *p2 = p; //p2也指向Person对象 就有两个强指针指向

p = [[Person alloc]init];//创建一个新对象让p来指向 但另外一个Person对象仍有强指针指向
NSLog(@"-------");
}
//所以对象会在程序结束后释放
return 0;
}

下面看弱指针的测试
int main(int argc, const char * argv[])
{

//接下来看若指针
//    __strong 声明强指针
//    __weak   弱指针

Person *p = [[Person alloc]init];
__weak Person *p2 = p;//声明一个弱指针 指向对象

p = nil; //把强指针指向变为空 对象没有强指针指向 会被释放

/*
只要强指针指向的对象不存在 ARC会自动给指向该对象的弱指针清空 这样就会防止野指针错误
*/
p2 = nil; //若指针不能决定对象的释放

NSLog(@"----------");
return 0;
}






关于ARC使用总结

一.OC的ARC就是自动引用计数
[提示]简单点说就是让编译器完成堆空间的引用计数加减,自动释放.程序员不再写retain和release方法
[注意]OC的自动内存管理,不同与JAVA的垃圾回收.而是在预处理的时候,直接在应该保留的地方,添加retain,在应该释放的地方,添加release,其实就是自动添加代码.

二.ARC的局限性
1.使用ARC,可能因为代码的不规范,导致内存提前释放.
2.导入一些第三方库,或者导入旧代码,这些代码不支持ARC.

三.解决ARC的局限性
1.将不使用ARC的代码转成ARC代码
Edit—>Refactor—>Convert to ARC
2.ARC非ARC混编
//同一个工程中,部分文件使用ARC,部分文件不使用ARC
Build phase —>Complie Source
-fno-objc-arc

四.使用ARC的技巧
1.四个关键字 修饰引用
__strong(强引用) 缺省属性,其修饰的对象指针,指向哪个对象,会对该对象retain,离开哪个对象,会对该对象release.
__weak(弱引用) 其修饰的对象指针,指向任何对象都不会retain. 这样的指针指向的对象随时可能消失.如果对象消失了,这个指针会自动变成nil.
//在IOS编程中,代理对象使用弱引用
__unsafe_unretained 其修饰的对象指针,指向任何对象都不retain. 当指向的对象消失,该指针不会变成nil,仍然指向已经释放的对象
__auotoreleasing 只用来修饰需要被传入地址的指针. 如:__autoreleasing NSError *error; &error;

2.属性的()参数,原则上,不能写retain copy了,只能写strong,如果不想写retain,可以写weak
[注意] 实际上ARC对这方面很宽松,写了retain也没关系

3.id指向对象,不能用void *p指向对象
int a;
void *p = &a;
id p = [[NSObject alloc] init];

4.C的结构体中,不能声明对象指针.否则这个指针不会进行内存管理.

5.不能(显示)手动调用父类的dealloc
-(void)dealloc
{
self.name = nil;
//自动调用父类的dealloc
}

ARC下的dealloc

众所周知,iOS开发的时候,使用ARC的话,dealloc函数是不需要实现的,写了反而会出错。

但有些特殊的情况,dealloc函数还是需要的。

比如,在画面关闭的时候,需要把ViewController的某些资源释放,

在viewDidDissppear不一定合适,viewDidUnload一般情况下只在memory warning的时候才被调用。

不用ARC的情况下,我们自然会想到dealloc函数。

其实ARC环境下,也没有把dealloc函数禁掉,还是可以使用的。只不过不需要调用[supper dealloc]了。

举个例子,画面上有UIWebView,它的delegate是该画面的ViewController,在WebView载入完成后,需要做某些事情,比如,把indicator停掉之类的。

如果在WebView载入完成之前关闭画面的话,画面关闭后,ViewController也释放了。但由于WebView正在载入页面,而不会马上被释放,等到页面载入完毕后,回调delegate(ViewController)中的方法,由于此时ViewController已经被释放,所以会出错。(message sent to deallocated instance)

解决办法是在dealloc中把WebView的delegate释放。

ARC也会有内存泄露
IOS中复制对象的用法及深拷贝和浅拷贝详解

iOS内存暴增问题追查与使用陷阱
IOS
内存优化和调试技巧
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: