美文网首页
iOS 底层 - OC对象内存管理之MRC

iOS 底层 - OC对象内存管理之MRC

作者: 水中的蓝天 | 来源:发表于2020-04-15 22:28 被阅读0次

    本文源自本人的学习记录整理与理解,其中参考阅读了部分优秀的博客和书籍,尽量以通俗简单的语句转述。引用到的地方如有遗漏或未能一一列举原文出处还望见谅与指出,另文章内容如有不妥之处还望指教,万分感谢 !

    作为一名开发者来说对内存管理一定不会感到陌生,那么内存管理到底是管理哪部分的内存呢?

    iOS 程序内存布局比较了解的应该会知道内存布局分为:保留区、代码段、数据段、堆区、栈区、内核区;真正需要开发者参与管理的是:在堆空间申请的内存;如果程序员不释放,程序结束后,会由操作系统回收; 这里的结束也有可能是 崩溃导致 的结束

    我多句嘴啊,为什么栈空间不需要开发者参与管理内存 ?

    对啊,这是为什么呢 ? 嗯 原来是由编译器自动分配释放,所以不需要开发者参与咯 !

    简单描述栈空间的内存管理原则 ?

    分配:编译阶段由编译器来给参数值、局部变量等分配存储空间
    释放:当超出其作用域时,由编译器控制从栈中弹出。

    Manual Reference Counting :MRC, 手动引用计数

    Automatic Reference Counting : ARC ,自动引用计数

    • MRC:遵循谁申请、谁添加、谁释放的原则。需要手动处理内存计数的 +1-1。从12年iOS5开始,逐步被ARC(自动引用计数)模式取代。

    • ARC取代了MRC后,在App编译阶段,由编译器(LLVM)在合适的位置添加了OC对象的内存管理代码。

    OC对象的内存管理原则:

    • 在iOS中,使用引用计数来管理OC对象的内存

    • 一个新创建的OC对象引用计数默认是1,当引用计数减为0时;OC对象就会销毁,释放其占用的内存空间

    • 调用retain会让OC对象的引用计数 +1,调用release会让OC对象的引用计数 -1

    内存管理的经验总结

    • 当调用allocnewcopymutableCopy方法返回了一个对象,当不需要这个对象时,就要调用release或者autorelease来释放它

    • [NSMutableArray array][NSMutableDictionary dictionary]等创建出来的对象不需要再添加内存管理代码(releaseretain),
      因为方法内部已经实现的内存管理代码

    • 想拥有某个对象,就让它的引用计数 加一; 不想再拥有时,就让它的引用计数 减一

    • MRC环境OC对象的声明:使用retain关键字修饰,在dealloc的父类方法调用前释放; 不需要实现setter方法的内存管理代码,因为用retain关键字修饰后已经自动实现了setter方法、getter方法以及内存管理代码

    MRC环境OC对象的声明示例

    @property (nonatomic, retain)XYHCar  *car;
    
    - (void)dealloc
    {
    //    [_car release];
    //    _car = nil;
    //或
        self.car = nil;    
        // 父类的dealloc放到最后
        [super dealloc];
    }
    
    
    • 可以通过以下私有函数来查看自动释放池的情况
    void _objc_autoreleasePoolPrint(void)
    

    这很奇怪,既然是私有函数怎么能够被外界调用呢 ?怎么做到的呢 ?
    可以的,使用 extern 关键字(前提是该函数没有被struct修饰为静态方法)对该函数在需要用到的地方重新声明一遍,真正调用时编译器会去查找有没有这个函数,只要有就会调用啦 ! 比如:

    示例代码

    传入void表示没有参数
    extern void _objc_autoreleasePoolPrint(void);
    
    int main(int argc, const char * argv[]) {
       @autoreleasepool { //  r1 = push()
           
           XYHOlg *olg1 = [[[XYHOlg alloc] init] autorelease];
           XYHOlg *olg2 = [[[XYHOlg alloc] init] autorelease];
           
           @autoreleasepool { // r2 = push()
               for (int i = 0; i < 6; i++) {
                   XYHOlg *olg3 = [[[XYHOlg alloc] init] autorelease];
                   NSLog(@"%@",olg3);
               }
               @autoreleasepool { // r3 = push()
                   XYHOlg *olg4 = [[[XYHOlg alloc] init] autorelease];
                   _objc_autoreleasePoolPrint();
                   NSLog(@"%@,%@,%@",olg1,olg2,olg4);
               } // pop(r3)
           } // pop(r2)
           
       } // pop(r1)
       
       return 0;
    }
    

    输出结果
    (hot) :当前 pool page
    (cold):不是当前 pool page
    (hot)(cold): 就一个pool page,怎么着都行

    objc[1363]: ##############
    objc[1363]: AUTORELEASE POOLS for thread 0x1000d1dc0
    objc[1363]: 12 releases pending.
    objc[1363]: [0x100805000]  ................  PAGE  (hot) (cold)
    objc[1363]: [0x100805038]  ################  POOL 0x100805038 第一个自动释放池
    objc[1363]: [0x100805040]       0x10051fbc0  XYHOlg
    objc[1363]: [0x100805048]       0x10051fc10  XYHOlg
    objc[1363]: [0x100805050]  ################  POOL 0x100805050 第二个自动释放池
    objc[1363]: [0x100805058]       0x10051fc20  XYHOlg
    objc[1363]: [0x100805060]       0x10051fc30  XYHOlg
    objc[1363]: [0x100805068]       0x100520130  XYHOlg
    objc[1363]: [0x100805070]       0x100520140  XYHOlg
    objc[1363]: [0x100805078]       0x100520150  XYHOlg
    objc[1363]: [0x100805080]       0x100520160  XYHOlg
    objc[1363]: [0x100805088]  ################  POOL 0x100805088 第三个自动释放池
    objc[1363]: [0x100805090]       0x100520170  XYHOlg
    objc[1363]: ##############
    Program ended with exit code: 0
    

    release和autorelease的区别 ?

    • release:在使用完对象后调用
    • autorelease:
      在初始化的时候调用,
      一个对象被autorelease,则该对象内存地址将被追加到当前任务的AutoreleasePool(自动释放池)里,通过AutoreleasePoolPage对象来管理,在合适的时机释放
      AutoreleasePool并没有单独的结构,而是由若干个AutoreleasePoolPage双向链表的形式组合而成(分别对应结构中的parent指针和child指针)

    ARC环境声明OC对象可以用assign来修饰吗 ?为什么?

    • 当然可以用来修饰OC对象,且编译器还不会报错,但不推荐
    • assign关键字修饰的OC对象在 setter方法不会生成 相关的内存管理代码
    • assign关键字修饰的对象释放后,指针不会被清空 依然指向释放前的对象;这很危险,在后续的操作中如果用到该对象会出现悬垂指针访问导致崩溃
    • assign: 多用来修饰基本数据类型

    小小知识点:指针

    注意:这里的内存管理 范围OC对象 ,至于基本数据类型如:intfloat不在范围之内;

    OC对象的引用计数都是自然数吗 ?

    绝大多数是的,只有NSString变NSTaggedPointerString时引用计数为其内存地址的十进制数

    在开发中就没有需要用到OC对象之外的数据类型了吗 ?他们的内存管理该如何实现 ?

    在iOS开发过程中可能会用到的语言有很多,比如:CC++RNSwiftJavascript等;这其中最多使用到是C的一些数组、字符串、指针, C语言中对堆内存的申请是调用malloc函数实现、释放是调用free函数实现;
    Javascript 具有自动垃圾回收机制,执行环境会管理代码执行过程中使用的内存。

    示例代码:

    int _tmain(int argc, _TCHAR* argv[])
    {
        char *p = (char *)malloc(1024*1024*1024);//在堆中申请了内存
        memset(p, 'a', sizeof(int) * 10);//初始化内存
        int i = 0;
        for (i = 0; i < 10; i++)
        {
            p[i] = i + 65;
        }
        print_array(p, 10);
        free(p);//释放申请的堆内存
    }
    

    相关文章

      网友评论

          本文标题:iOS 底层 - OC对象内存管理之MRC

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