Objective - C 9. 内存管理初级

一 、 内存管理的方式.

iOS 应用程序出现 Crash(闪退,崩溃), 90% 以上的原因是内存问题.

在一个拥有数十个甚至上百个类的工程里, 查找内存问题极其困难. 了解内存常见问题.能帮我们减少出错几率

内存问题体现在两个方面: 内存溢出, 野指针异常.






野指针异常: 指的是 对象的内存空间已经被系统回收, 但仍然使用指针操作这块内存.

了解内存管理,能够帮我们提升程序性能,大大减少调试 bug 的时间.


内存的管理方式: 
  1. 垃圾回收( gc ) :程序员只需要开辟内存空间, 系统会自动判断并回收不再被使用的空间,整个过程不需要在写代码
  2. MRC  (Manual Reference Count) :人工引用计数, 内存的开辟和释放都由程序代码进行控制. 对内存的控制更加灵活.在需要内存时,及时释放.
  3. ARC (Auto Reference Count): 自动引用计数: iOS5.0的编译器特性, 它允许用户只开辟空间, 不用去释放空间. 他不是垃圾回收, 它的本质还是 MRC 只是编辑器帮程序员默认加了释放的代码

iOS的内存管理
iOS 支持两种内存管理方式: ARC 和 MRC.
   MRC 的内存管理机制是:引用计数.
   ARC 本质还是是基于 MRC 的.


什么是引用计数
在 C 语言中,使用 malloc 和 free, 进行堆内存的创建和释放.堆内存只有正在使用和销毁两种状态.
但在实际开发中,我们常常会遇到两个以上的指针同时使用同一块内存, C 语言无法记录内存使用者的个数.
   OC 采用引用计数机制来管理内存,当一个新的引用指向对象时, 引用计数器就会递增, 当去掉一个引用时,引用计   数就会递减, 当引用计数到 0 时, 该对象就将释放占有的内存资源.
// 创建一个 person 对象  , 对象的的引用计数器变为1. 这是由 0 到 1 的过程
    Person *person1 = [[Person alloc] init];
    NSLog(@"%ld", person1.retainCount);  // 引用计数器为 1
    
    // 减掉一个引用计数
    [person1 release]; // 引用计数器 - 1
    person1 = nil 
    NSLog(@"%lu", person1.retainCount); // 引用计数器 打印结果为1 但实质是0 因为对象 person1 所指向的内存空间已经被释放, 并且要将 person1 指向空.让其安全释放

影响引用计数的方法

+ alloc : 开辟内存空间, 让被开辟的内存空间的引用计数变为1. 这是由0 到 1 的过程.
// 创建一个 person 对象  , 对象的的引用计数器变为1. 这是由 0 到 1 的过程
    Person *person1 = [[Person alloc] init];
    NSLog(@"%ld", person1.retainCount);  // 引用计数器为 1

- retain: 引用计数加 1 
 Person *person2 = [[Person alloc] init]; // 引用计数为1
    
    // 引用计数 + 1
    [person2 retain];
    NSLog(@"%lu", person2.retainCount); // 现在引用计数为2

- copy : 把某一个内存区域的内容拷贝一份,到新的内存空间里去, 被拷贝区域的引用计数不变, 新的内存区域的引用计数为1  

如果要在在 main 函数中使用 copy 方法. 实质使用的是 - (id) copyWithZone: (NSZone *)zone 
所以必须遵守的是<NScopying>协议,在.h 父类名后写上<NScopying> 并且 实现协议中的方法. copyWithZone
// 要想实现对象的拷贝,第一步就是在.h文件中遵守<NSCopying>协议
@interface Person : NSObject <NSCopying>
<pre name="code" class="objc">// 2. 实现<NSCopying>协议中的方法,copyWithZone:
- (id)copyWithZone:(NSZone *)zone
{
    // 创建一个和self一模一样的对象返回
    Person *p = [[Person alloc] init];
    
    p.name = self.name;
    p.age = self.age;
    
    
    return p;
}


 
  

- release:  引用计数减1, 如果内存空间之前 引用计数为4,release 后引用计数变为3,如果.之前引用计数为1.release 之后变为0 内存被系统回收

    // 引用计数 - 1
    [person2 release]; // 2
    [person2 release]; // 1 person2内存空间被释放.
    person2 = nil; // 将其指向空.安全释放
    NSLog(@"%lu", person2.retainCount);

- dealloc: 是继承自父类的方法, 当对象引用计数为0的时候, 由对象自动调节.

#pragma mark - 重写 dealloc 方法.
- (void)dealloc
{
    NSLog(@"%@ 我被销毁了.", self);
    // 使用从父类继承过来的方法,销毁从父类继承过来的属性或者实例变量
    [super dealloc];
}

- autorelease :在未来的某一时刻引用计数 减1.  

- autoreleasepool:自动释放池控制 autorelease 对象的释放

向一个对象发送 autorelease 消息 , 这个对象何时释放, 取决于 autoreleasepool 何时结束.
    @autoreleasepool {
        Person *person3 = [[Person alloc] init]; //引用计数 为 1
        NSLog(@"%@", person3);
        [person3 autorelease]; // 给 person3 发送 autorelease 消息
        NSLog(@"%ld", person3.retainCount);
        // 一直到自动释放池结束后. person3 被释放

    }
自动释放池里.可以不用把 person3 置空.

  @autoreleasepool {
        Person *p = [[Person alloc] init]; //retainCount 为1
        [p retain]; //retainCount 为2
        [p autorelease]; //retainCount 为2 延迟 未来的某个时刻释放
        [p release]; //retainCount 为 2 - 1 = 1
        // 自动释放池结束后 p 对象内存被释放
    }

自动释放池会逐一向对象发送 release 消息, 越靠近自动释放池的对象.越先被释放

出了大括号,自动释放池,才向各个对象发送 release 消息

   @autoreleasepool {
        Person *p4 = [[Person alloc] init];
        NSLog(@"%@", p4);
        [p4 autorelease];
        
        @autoreleasepool {
            Person *p5 = [[Person alloc] init];
            NSLog(@"%@", p5);
            [p5 autorelease];
            
            Person *p6 = [[Person alloc] init];
            NSLog(@"%@", p6);
            [p6 autorelease]; //p6 先被释放
        }
        
        Person *p7 = [[Person alloc] init];
        NSLog(@"%@", p7);
        [p7 autorelease];
    }

内存管理的原则

引用计数的增加和减少必须相等. 当引用计数降为0之后, 不应该再使用这块内存空间

凡是使用了. alloc, retain 或者 copy 让内存的引用计数增加了, 就需要是使用 release 或者 autorelease 让内存的引用计数减少. 在一段代码内, 增加和减少的的次数必须要相等.

copy 方法

- 跟 retain 不同, 一个对象想要 copy, 生成自己的副本,需要实现 NSCopying 协议,定义 copy 的细节,(如何 copy)
如果类没有接受 NSCopying 协议而给对象发送 copy 消息,会引起 crash

例如: Person 类copy 方法的实现

Person.h
// 要想实现对象的拷贝,第一步就是在.h文件中遵守<NSCopying>协议
@interface Person : NSObject <NSCopying>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;

@end

// 2. 实现<NSCopying>协议中的方法,copyWithZone:
- (id)copyWithZone:(NSZone *)zone
{
    Person *p = [[Person alloc] init];
    p.name = self.name;
    p.age = self.age;
    return p;
}

main 函数中   
 Person *p1 = [[Person alloc] init];
    p1.name = @"123";
    p1.age = 12;
    
    // 向p1 发送 copy 方法.赋值给 p2
    Person *p2 = [p1 copy];
    
    NSLog(@"%@", p2);







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值