1.五大内存区域
栈区,堆区,全局区,常量区,代码区
五大内存区域之外还有 自由存储区也称之五大区域之外区
1.1栈区:
创建临时变量时由编译器自动分配,在不需要的时候自动清除的变量的存储区。和堆一样,用户栈在程序执行期间可以动态地扩展和收缩。
@interface TestObject()
@end
@implementation TestObject
- (void)testMethodWithName:(NSString *)name
{
//方法参数name是一个指针,指向传入的参数指针所指向的对象内存地址。name是在栈中
//通过打印地址可以看出来,传入参数的对象内存地址与方法参数的对象内存地址是一样的。但是指针地址不一样。
NSLog(@"name指针地址:%p,name指针指向的对象内存地址:%p",&name,name);
//*person 是指针变量,在栈中, [Person new]是创建的对象,放在堆中。
//person指针指向了[Person new]所创建的对象。
//那么[Person new]所创建的对象的引用计数器就被+1了,此时[Person new]对象的retainCount为1
Person *person = [Person new];
}
1.2堆区:
就是那些由 new alloc 创建的对象所分配的内存块,它们的释放系统不会主动去管,由我们的开发者去告诉系统什么时候释放这块内存(一个对象引用计数为0是系统就会回销毁该内存区域对象)。一般一个 new 就要对应一个 release。在ARC下编译器会自动在合适位置为OC对象添加release操作。会在当前线程Runloop退出或休眠时销毁这些对象,MRC则需程序员手动释放。
堆可以动态地扩展和收缩
//alloc是为Person对象分配内存,init是初始化Person对象。本质上跟[Person new]一样。
Person *person = [[Person alloc] init];
2.2 nonatomic与atomic
nonatomic(非原子性):在调用用nonatomic声明的对象属性时是非线程安全性的,因为采用的nonatomic,不同操作可以同时执行,而不需要等前面的操作完成后在进行下一步操作。所以称之为非线程安全。非原子性的执行效率更高不会阻塞线程
atomic(原子性):相反与非原子性,atomic是具有线程安全性的。他会在getset方法中加入线程操作。每当对用atomic声明的对象属性操作时,会根据操作加入线程的顺序一步一步完成操作,而不是非原子性的同时操作。执行效率较低一般来说很少用atomic。
readOnly表示属性仅能读不能设置其值。告诉编译器只生成getter方法不生成setter方法。
readWrite默认值,表示属性可读可写。编译器会自动生成getter setter方法
__weak
主要用于解决循环引用,用__weak修饰的变量 当对象释放后,指针自动设置为nil,当后面继续使用该指针变量的时候不会造成crash,更不会造成强引用使该释放的对象无法释放,造成内存泄露。
__weak typeof(self) weakSelf = self;
__strong
相反与__weak,主要用于当使用某个对象是,希望它没有提前被释放。强引用该对象使其无法释放。例如在block内部,希望block调用时该对象不会被提前释放造成错误。可以使用强引用。
TestAlertView *alertView = [TestAlertView new];
alertView = ^()
{
//当block内部需要使用本身这个局部对象时,需要用强引用方式,让alertView在传递完block后不会被释放依然可以执行setTitle操作
__strong typeof(alertView) strongAlertView = alertView;
[strongAlertView setTitle:@"1234"];
}
[alertView show];
3 MRC与ARC区别
3.1 MRC手动内存管理
引用计数器:在MRC时代,系统判定一个对象是否销毁是根据这个对象的引用计数器来判断的。
1.每个对象被创建时引用计数都为1
2.每当对象被其他指针引用时,需要手动使用[obj retain];让该对象引用计数+1。
3.当指针变量不在使用这个对象的时候,需要手动释放release这个对象。 让其的引用计数-1.
4.当一个对象的引用计数为0的时候,系统就会销毁这个对象。
NSMutableArray *array = [NSMutableArray array];//[NSMutableArray array]创建后引用计数器为1
NSLog(@"array的对象地址:%p,array的retainCount:%zd",array,[array retainCount]);
[array release];//调用release后[NSMutableArray array]创建的对象引用计数-1.
//当程序执行到[array addObject:@"1234"];这里是就会崩溃。因为此时array指针指向的内存地址中没有任何对象,该指针是一个野指针。
//因为release后[NSMutableArray array]创建的对象引用计数变为了0.系统就会销毁这个内存地址的对象。
[array addObject:@"1234"];
NSLog(@"array的对象地址:%p,array的retainCount:%zd",array,[array retainCount]);
NSLog(@"%@",array);
在MRC模式下必须遵循谁创建,谁释放,谁引用,谁管理
在MRC下使用ARC
在Build Phases的Compile Sources中选择需要使用MRC方式的.m文件,然后双击该文件在弹出的会话框中输入 -fobjc-arc
3.2 ARC自动内存管理
WWDC2011和iOS5所引入自动管理机制——自动引用计数(ARC),它不是垃圾回收机制而是编译器的一种特性。ARC管理机制与MRC手动机制差不多,只是不再需要手动调用retain、release、autorelease;当你使用ARC时,编译器会在在适当位置插入release和autorelease;ARC时代引入了strong强引用来带代替retain,引入了weak弱引用。
在ARC下使用MRC方法
在ARC工程中如果要使用MRC的需要在工程的Build Phases的Compile Sources中选择需要使用MRC方式的.m文件,然后双击该文件在弹出的会话框中输入 -fno-objc-arc
3.3 autoreleasepool自动释放池
自动释放池始于MRC时代,主要是用于 自动 对 释放池内 对象 进行引用计数-1的操作,即自动执行release方法。
在MRC中使用autoreleasepool必须在代码块内部手动为对象调用autorelease把对象加入到的自动释放池,系统会自动在代码块结束后,对加入自动释放池中的对象发送一个release消息。无需手动调用release
int main(int argc, const char * argv[])
{
Person *father = [[Person alloc] init];//引用计数为1
@autoreleasepool {//这里创建自动释放池
int a = 10; // a在栈,10在常量区
int b = 20; //b在栈,20在常量区
// p : 栈
// [[Person alloc] init]创建的对象(引用计数器==1) : 在堆
Person *p = [[Person alloc] init];
//MRC下需要手动让对象加入自动释放池
[p autorelease];
Person *pFather = father;
[father retain];//father指针指向的对象被pFather引用,在MRC下需要手动让被引用对象引用计数+1
NSLog(@"pFather对象内存地址:%p,pFather的引用计数:%zd",pFather,[pFather retainCount]);
NSLog(@"father对象内存地址:%p,father的引用计数:%zd",father,[father retainCount]);
}//这里释放 自动释放池
// 当autoreleasepool内部代码块执行完毕后上面代码块后, 栈里面的变量a、b、p 都会被回收
// 但是堆里面的Person对象还会留在内存中,因为它是计数器依然是1。当autoreleasepool代码块执行完毕后,会对释放池内部的所有对象执行一个release消息。如果发送release消息后,对象引用计数为0了,那么就会被系统回收。
NSLog(@"father对象内存地址:%p,father的引用计数:%zd",father,[father retainCount]);
return 0;
}
在ARC中对@autoreleasepool的使用相比MRC不太多。主要用于一些大内存消耗对象的重复创建时,保证内存处于比较优越的状态。常用于创建对象较多的for循环中。在ARC下不要手动的为@autoreleasepool代码块内部对象添加autorelease,ARC下自动的把@autoreleasepool代码块中创建的对象加入了自动释放池中。
for (int i = 0; i < 10000000; i++)
{
@autoreleasepool{
NSMutableArray *array = [NSMutableArray new];
NSMutableDictionary *dic = [NSMutableDictionary new];
NSMutableArray *array1 = [NSMutableArray new];
NSMutableDictionary *dic1 = [NSMutableDictionary new];
NSMutableArray *array2 = [NSMutableArray new];
NSMutableDictionary *dic2 = [NSMutableDictionary new];
NSData *data = UIImageJPEGRepresentation([UIImage imageNamed:@"testimage"], 1);
NSError *error;
NSURL *url = [NSURL URLWithString:@"www.baidu.com"];
NSString *fileContents = [NSString stringWithContentsOfURL:url
encoding:NSUTF8StringEncoding
error:&error];
}
}
使用autoreleasepool时
可以从上图看出,使用autoreleasepool时,内存一直保持稳定状态
image.png因为没有使用 autoreleasepool,随着for循环一直执行下去,内存一直在上升。
网友评论