zoukankan      html  css  js  c++  java
  • Objective-C内存管理

    1.内存管理中的基本问题

      1.1为什么要进行内存管理    分配在堆空间中的对象 需要手动去释放

    回顾堆栈的区别                                                                          生命周期 

    栈空间    函数 函数中局部变量                   调用函数压栈    函数调用结束 释放  

    数据段    静态变量  全局变量                      程序开始             程序结束 释放

    堆:                malloc  alloc                                程序猿手动释放 free() release

      1.2内存管理的定义  

    内存管理就是确保开辟的堆空间被正确的释放。

    内存管理中的问题:

    1.内存泄露  堆空间没有释放

    2. 内存崩溃 野指针 (过早释放:使用已经释放的空间     

    重复释放:重复释放同一个空间)

      1.3 C语言内存管理的缺陷

    1 释放一个堆 ,必须保证所有使用堆的指针结束使用,避免(提前释放);

    2  释放一个指针,确保指向同一个堆的指针,只有一个被释放,避免(重复释放);

    3 模块化分工编程,不易明确谁来释放

    4 多线程操作,不能确定哪个线程最后结束

      1.4 OC内存管理的基本原则

    1.对象在完成创建的同时,内部会自动创建一个引用计数器,这个计数器,是系统用来判断是否回收对象的唯一依据,当我们的引用计数retainCount = 0的时候,系统会毫不犹豫回收当前对象

     2.[对象 retain]   reatinCount + 1 ,返回self

     3.[对象 release]  reatinCount - 1

     4.我们的引用计数retainCount = 0时  对象就被销毁了

      5.dealloc函数,当一个对象要被销毁的时候,系统会自动调用dealloc函数,通知对象你将要被销毁

      内存管理原则(配对原则):只要出现了 new,alloc,retain,就一定配对出现一个release,autorelease

     1         Person * p = [[Person alloc] init];
     2         NSLog(@"%lu",p.retainCount);
     3 //        [p retain];
     4         Person * q = [p retain];
     5         NSLog(@"%lu",q.retainCount);
     6         
     7         [p release];
     8         NSLog(@"%lu",p.retainCount);
     9         
    10         [q release];
    11         NSLog(@"%lu",p.retainCount);
    12         [p run];

    创建一个Person类  一个p指针指向创建的对象    创建完成之后 计数器为1   创建一个q指针指向p原本指的对象 计数器+1为2 

    [p release] 计数器-1   [q release] 计数器再-1 此时对象释放    

    1 - (void)dealloc {
    2     NSLog(@"人被释放了");
    3     [super dealloc];
    4 }

    类中重写dealloc方法 则会输出 人被释放了  若开启僵尸对象检测 则[p run]报错

    // 为什么最后不为0?

    最后一次输出,引用计数没有变成0.

    这是为什么呢?

    因为该对象的内存已经被回收,而我们向一个已经被回收的对象发了一个retainCount消息,所以它的输出结果应该是不确定的,如果该对象所占的内存被复用了,那么就有可能造成程序异常崩溃。

    那为什么在这个对象被回收之后,这个不确定的值是1而不是0呢?这是因为当最后一次执行release时,系统知道马上就要回收内存了,就没有必要再将retainCount减1了,因为不管减不减1,该对象都肯定会被回收,而对象被回收后,它的所有的内存区域,包括retainCount值也变得没有意义。不将这个值从1变成0,可以减少一次内存操作,加速对象的回收。

    2.单个对象的内存管理

        2.1.1 内存泄露的第一种情况

    1     Person * p = [[Person alloc] init];
    2     NSLog(@"%lu",p.retainCount);
    3     Person * q = [p retain];
    4     NSLog(@"%lu",q.retainCount);
    5     [p release];//引用两次需要两次release  少写一次都会内存泄露
    6     [q release];

     2.1.2内存泄露的第二种情况

    1     Person * p2 = [[Person alloc] init];
    2     NSLog(@"%lu",p2.retainCount);
    3     p2 = nil;
    4     [p2 release]; // [nil release]

    释放前指向nil   然后release 之前的对象没有被释放

      2.2.1 野指针的提前释放 

    1         Person * p = [[Person alloc] init];
    2         NSLog(@"%lu",p.retainCount);
    3         Person * q = [p retain];
    4         
    5         [p release];
    6        // p = nil;
    7         [q release];
    8         //q = nil;
    9         [p run]; // [nil run]  // nil 调用任何方法都不会报错

    提前释放之后再用对象的方法 会形成提前释放的野指针

      2.2.2野指针的重复释放

    1         Person * p = [[Person alloc] init];
    2         NSLog(@"%lu",p.retainCount);        
    3         [p release];
    4         [p release];
    5 //重复释放 

    释放之后 不可retain  无法起死回生 

    3.多个对象的内存管理

     1         // 创建Person对象
     2         Person * p = [[Person alloc] init];
     3         // 创建Car对象
     4         Car * c = [[Car alloc] init];
     5         // 让人有一辆车
     6         [p setCar:c];
     7         [p drive];
     8         [c release];
     9         [p drive];
    10         // 只要p对象存在,就可以随意调用自己的方法
    11         [p release];
    1 //Car类成员方法的实现以及dealloc的重写
    2 - (void)dealloc {
    3     NSLog(@"dealloc 车被销毁了");
    4     [super dealloc];
    5 }
    6 
    7 - (void)run {
    8     NSLog(@"车跑起来了");
    9 }
     1 //Person类成员方法的实现以及dealloc方法的重写
     2 
     3 - (void)setCar:(Car *)car {
     4     //在setCar的时候,让car的retainCount+1
     5     // 目的是保证p对象存在的时候,_car对象一定存在
     6     _car = [car retain];
     7 }
     8 
     9 - (Car *)car {
    10     return _car;
    11 }
    12 
    13 - (void)drive {
    14     [_car run];
    15 }
    16 
    17 - (void)dealloc {
    18     NSLog(@"dealloc 人被销毁了");
    19     // 保证p对象销毁的时候,他所持有的_car对象也被销毁,防止出现内存泄露
    20     [_car release];
    21     [super dealloc];
    22 }

       set方法的内存管理  

    若两次set方法的参数都为同一对象  那么会先把对象release一次之后再retain   但release之后就释放掉了  所以要判断参数是否和对象的成员相等

     1 - (void)setCar:(Car *)car {
     2     //在setCar的时候,让car的retainCount+1
     3     // 目的是保证p对象存在的时候,_car对象一定存在
     4     
     5     // 如果调用 _car = car,并且_car的retainCount=1 的情况下会出现野指针问题
     6     if (_car != car) {
     7         // 第一次运行 [nil release]
     8         [_car release];
     9         _car = [car retain];
    10     }
    11     
    12 }

    set方法可以改写为如下所示。

    4.@property的参数

    // 1与内存管理相关的参数

    // 默认是 assign

    // retain 生成符合内存管理原则的set方法

    // assign 直接赋值,不考虑内存管理(一般基本数据类型)

    // 2 与多线程相关的代码

    // notatomic 不生成与多线程相关的代码 默认 iOS开发中用这个

    // atomic 生成与多线程相关的代码 mac开发中会用到

    // 3 是否生成set与get方法

    // readonly 只生成get方法

    // readwrite 生成get与set方法 默认

    // 没有只生成set方法的参数

    // 4 set与get方法名称相关的参数

    // setter 设置set方法的名字(有冒号);

    // getter 设置get方法的名字

     

    5.循环引用问题

    Person类有Car类的属性

    Car类也有Person类的属性 就成为循环引用  

    .h文件中导入头文件应换成 @class 类名 的形式 

    前向声明 告诉系统有这样一个类

    .m文件中应导入头文件 调用类的成员方法

    引用关系应该一强一弱  

     1 #import <Foundation/Foundation.h>
     2 #import "Person.h"
     3 #import "Car.h"
     4 int main(int argc, const char * argv[]) {
     5     @autoreleasepool {
     6         // p 1 car 1
     7         Person * p = [[Person alloc] init];
     8         Car * car = [[Car alloc] init];
     9         // p 1 car 2
    10         [p setCar:car];
    11         // car 2  p 1
    12         [car setPerson:p];
    13         // p 0 car 1
    14         [p release];      //难点总结:p release之后 计数count 变为0,此时调用 p 的 dealloc 函数,随之成员变量_car release
    15         // p 0  car 0
    16         
    17         [car release];
    18         
    19         
    20         
    21         
    22 //        // p 1 car 1
    23 //        Person * p = [[Person alloc] init];
    24 //        Car * car = [[Car alloc] init];
    25 //        // p 1 car 1
    26 //        [p setCar:car];
    27 //        // car 1 p 2
    28 //        [car setPerson:p];
    29 //        // p 1 car 1
    30 //        [p release];
    31 //        // p 0  car 0
    32 //        [car release];
    33         
    34         
    35         
    36         
    37 //        // p 1 car 1
    38 //        Person * p = [[Person alloc] init];
    39 //        Car * car = [[Car alloc] init];
    40 //        // p 1 car 2
    41 //        [p setCar:car];
    42 //        // car 2 p 2
    43 //        [car setPerson:p];
    44 //        // p 1 car 2
    45 //        [p release];
    46 //        // p 1 car 1
    47 //        [car release];        
    48        
    49     }
    50     return 0;
    51 }
    main.m
    1 #import <Foundation/Foundation.h>
    2 @class Car; // 前向声明 告诉系统有这样一个类
    3 @interface Person : NSObject
    4 @property (nonatomic,retain) Car * car;
    5 @end
    Person.h
    1 #import "Person.h"
    2 #import "Car.h"
    3 @implementation Person
    4 - (void)dealloc {
    5     NSLog(@"person 被销毁了");
    6     [_car release];
    7     [super dealloc];
    8 }
    9 @end
    Person.m
    1 #import <Foundation/Foundation.h>
    2 @class Person;
    3 @interface Car : NSObject
    4 @property (nonatomic,assign) Person * person;
    5 @end
    Car.h
    1 #import "Car.h"
    2 #import "Person.h"
    3 @implementation Car
    4 - (void)dealloc {
    5     NSLog(@"car 被销毁了");
    6 //    [_person release]; 弱引用 所以没有这行
    7     [super dealloc];
    8 }
    9 @end
    Car.m

     

    6.autorelease的基本原理  

    autorelease   作用为延迟释放   

    1     @autoreleasepool {
    2         Person * p = [[Person alloc] init];
    3         //        [p release];// 立即减1
    4         [p autorelease]; // 延迟减1 把对象添加到最近的自动释放池
    5         NSLog(@"aaa");
    6     }// 自动调用 [pool drain]
    7    

    也可以new一个NSAutoreleasePool对象      释放时调用[pool drain]方法  可以起到}的作用

     1     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
     2     // {
     3     Person * p = [[Person alloc] init];
     4     [p autorelease];
     5     //        [p retain];
     6     Person * q = [[Person alloc] init];
     7     [q autorelease];
     8     NSLog(@"aaa");
     9     // 给池子里每一个对象发送release方法
    10     [pool drain]; // [p release]
    11     // }
    12     NSLog(@"bbb");
    
    
    1     Person * p = [[Person alloc] init];
    2     // 自动释放池是可以随意创建的
    3     // 不管这个对象是在@autoreleasepool之内还是之外创建的,只要你在池子中调用了autorelease方法,那么这个对象就会被放入到池子中,随着池子的销毁而销毁
    4     // 创建一个自动释放池
    5     @autoreleasepool {
    6         [p autorelease];
    7         NSLog(@"aaa");
    8     }
    9     NSLog(@"bbb");
    
    
        Person * p = [[Person alloc] init];
        @autoreleasepool {
            [p autorelease];
        
            @autoreleasepool {
                [p autorelease];
                NSLog(@"aaa");
            }//此时已经释放
            NSLog(@"bbb");
        }

    7. autorelease的应用 

     1     @autoreleasepool {
     2 //        Chief * c = [[Chief alloc] init];
     3 //        [c autorelease];
     4         Chief * chief = [Chief chief];
     5         
     6         Food * food = [chief foodByChief];
     7         NSLog(@"%@",food.name);
     8         
     9         NSLog(@"aaa");
    10     }
    11     NSLog(@"bbb");
    12     return 0;
    13 
    14 
    15 
    16 
    17 
    18 + (id)chief {
    19     id chief = [[self alloc] init];
    20     
    21     return [chief autorelease];
    22 }
    23 
    24 - (Food *)foodByChief {
    25     Food * food = [[Food alloc] init];
    26     food.name = @"辣条";
    27     return [food autorelease];
    28 }

    8. ARC的使用 

     1 #import <Foundation/Foundation.h>
     2 #import "Dog.h"
     3 // __strong __weak
     4 
     5 void testStrong()
     6 {
     7     //只要有强指针指向一个对象,那么系统就不会回收该对象,只要没有强指针指向对象,系统立即回收该对象
     8     // 默认情况下所有的指针都是强指针类型
     9     // 在ARC中,不能调用retain release autorelease
    10     // 也不能显式调用[super dealloc]
    11     Dog * dog = [[Dog alloc] init];
    12     // 对象一
    13     NSLog(@"aaa");
    14     //
    15     Dog __weak * dog1 = [[Dog alloc] init]; // assign
    16     
    17     //        dog = nil;
    18     Dog * dog2 = [[Dog alloc] init];
    19     // 对象二
    20     dog = dog2;
    21     NSLog(@"%@ %@",dog,dog2);
    22 }
    23 
    24 void testWeak()
    25 {
    26     Dog * dog = [[Dog alloc] init];
    27     
    28     Dog * __weak dog1 = dog;
    29     NSLog(@"%@ %@",dog,dog1);
    30     dog = nil;
    31     NSLog(@"%@ %@",dog,dog1);
    32     // 如果对于一个对象而言,这个对象既有强引用的指针,又有弱引用的指针,当强引用的指针不存在时,所有弱引用的指针会自动置空
    33 }
    34 
    35 void testAutoreleaseing()
    36 {
    37     //
    38     Dog * dog = nil;
    39     Dog __autoreleasing * dog1 = nil
    40     ;
    41     
    42     @autoreleasepool {
    43         dog = [[Dog alloc] init];
    44         NSLog(@"dog---%@",dog);
    45         
    46         dog1 = [[Dog alloc] init];
    47         NSLog(@"dog1--%@",dog1);
    48         
    49     }
    50     NSLog(@"dog------%@",dog);
    51     NSLog(@"dog1------%@",dog1);
    52 }
    53 
    54 void testUnsafe_unretained()
    55 {
    56     // __unsafe_unretained只要指向对象的指针至少有一个强引用存在,那么 __unsafe_unretained和__weak功能一样
    57     // 若没有强引用的指针后,__weak类型的指针会自动置空,而__unsafe_unretained类型的指针不会
    58     Dog __unsafe_unretained * dog = nil;
    59     {
    60         Dog * dog11 = [[Dog alloc] init];
    61         dog = dog11;
    62         dog11 = nil;
    63         NSLog(@"%@",dog);
    64     }
    65 }
    66 
    67 
    68 int main(int argc, const char * argv[]) {
    69     @autoreleasepool {
    70         testUnsafe_unretained();
    71         
    72     }
    73     return 0;
    74 }
     1 #import "Dog.h"
     2 
     3 @implementation Dog
     4 - (void)dealloc {
     5     NSLog(@"dog 被释放了");
     6     // 不能显式调用[super dealloc]
     7     // 不能调用release autorelease retain
     8     // 不能查看retainCount
     9 //    [super dealloc];
    10 }
    11 @end

    9.MRC与ARC的混编

    Dog类为ARC     在MRC中运行时需要更改的设置

    Car和Engine为MRC   在ARC中运行时需要更改的设置

  • 相关阅读:
    jQuery 动画之 添加商品到购物车
    <% %> 、 <%= %> 、<%# %> 的区别
    WebFrom模拟MVC
    MVC 基础
    LinQ to SQL 存储过程
    Leetcode练习(Python):数组类:第26题:给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
    Leetcode练习(Python):数组类:第18题:给定一个包含&#160;n 个整数的数组&#160;nums&#160;和一个目标值&#160;target,判断&#160;nums&#160;中是否存在四个元素 a,b,c&#160;和 d&#160;,
    Leetcode练习(Python):数组类:第16题:给定一个包括&#160;n 个整数的数组&#160;nums&#160;和 一个目标值&#160;target。找出&#160;nums&#160;中的三个整数,使得它们的和与&#160;target&#160;最接近。返回这三个数的和。假定每组输入只存在唯一答案。
    Leetcode练习(Python):数组类:第15题:给你一个包含 n 个整数的数组&#160;nums,判断&#160;nums&#160;中是否存在三个元素 a,b,c ,使得&#160;a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。 注意:答案中不可以包含重复的三元组。
    Leetcode练习(Python):数组类:第11题:给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点&#160;(i,&#160;ai) 。在坐标内画 n 条垂直线,垂直线 i&#160;的两个端点分别为&#160;(i,&#160;ai) 和 (i, 0)。找出其中的两条线,使得它们与&#160;x&#160;轴共同构成的容器可以容纳最多的水。
  • 原文地址:https://www.cnblogs.com/gwkiOS/p/4941360.html
Copyright © 2011-2022 走看看