iOS 底层基础知识(1)

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

image.png

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循环一直执行下去,内存一直在上升。

iOS-MRC与ARC区别以及五大内存区

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 9,282评论 1 27
  • 内存管理概述 内存管理内存的作用:存储数据. 如何将数据存储到内存之中.声明1个变量.然后将数据存储进去. 当数据...
    指尖书法阅读 976评论 2 9
  • 内存管理的基本范围和概念. 程序运行过程中药创建大量的对象, 和其他高级语言类似,在ObjC中对象存储在堆区,程序...
    ValienZh阅读 549评论 0 1
  • 内存管理ARC处理原理ARC是Objective-C编译器的特性,而不是运行时特性或者垃圾回收机制,ARC所做的只...
    爱运动爱学习阅读 137评论 0 3
  • 现代人很注重养生,该吃什么,不该吃什么,三分寒七分饱,饭后百步走活到九十九,要注重身体酸碱平衡,阴阳平衡,药外不如...
    火爆龙阅读 436评论 0 8
  • ——她说这骄阳酷热似火
    墨子俞阅读 245评论 0 1
  • 你要先知道数据到底意味着什么。 如果不太了解它到底是什么,最简单的方法就是知乎搜一下big data/大数据,这类...
    金光闪闪耶阅读 262评论 5 4
  • 最近,我观赏了电影《肖申克的救赎》,这部电影给了我很大的感触。 这部电影主要讲述了受冤入狱的安迪·杜佛兰不屈不挠,...
    WSJ_阅读 141评论 0 1
  • 最近新学了一个词。“阈值” 百度给的官方回答是:临界值。 打个比方就是。有的人可能从超市买了些东西,提回家第二天胳...
    小可爱尾巴阅读 153评论 0 0