美文网首页
iOS 底层基础知识(1)

iOS 底层基础知识(1)

作者: 在路上浅谈管理 | 来源:发表于2019-10-31 12:23 被阅读0次

    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区别以及五大内存区

    相关文章

      网友评论

          本文标题:iOS 底层基础知识(1)

          本文链接:https://www.haomeiwen.com/subject/rznzvctx.html