day9: 内存管理初级:内存管理的方式、引用计数机制,影响计数的各个方法、dealloc方法、内存管理的基本原则

内存管理的黄金法则:
    黄金法则:每次调用alloc一次,都需要调用release一次,他们两是成对出现的。
 
 内存管理的对象:
    所有NSObject下的所有OC对象,都需要做内存管理,基本类型的
 
 内存管理初级
    内存常见问题体现在两个方面:内存溢出 和 野指针异常(大部分问题都是野指针异常问题)
    内存溢出:没有被释放掉的内存
    野指针异常:对象内存空间已经被系统回收,仍然使用指针操作这块内存,野指针异常是程序crash的主要原因,代码量越大的程序,越难找出出现野指针的位置
 
 了解内存管理,能帮我们提升程序性能,大大减少调试bug时间
 
 内存管理的方式:
    两种内存管理方式:ARC 和 MRC
    Manual Reference Count 人工引用计数(xcode 5 之后就用自动引用计数来管理了,不建议使用MRC,若使用MRC还的手工把ARC调为NO)
    Auto Reference Count 自动引用计数(ARC是基于MRC的)
    现在xcode可以
 
 引用计数机制
    C语言中,使用malloc和free,进行堆内存的创建和释放,堆内存只有正在使用和销毁两种状态
    实际开发中,可能会遇到,两个或两个以上的指针使用同一块内存。C语言无法记录内存使用者的个数
    OC采用引用计数机制管理内存,当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时,引用计数就递减,当引用计数为零时,该对象就将释放占有的资源
    影响引用计数的方法:
    +alloc   +1
    -retain  +1
    -copy    +1
    -release      -1
    -autorelease  -1
 
    dealloc方法
    -dealloc是继承自父类的方法,当对象引用计数为0的时候,由对象自动调用
 
 内存管理的基本原则
 
 自动释放池:autoreleasepool的使用
 通过autoreleasepool控制autorelease对象的释放
 自动释放池中是以栈的形式存在的,在池子释放的时候,会对池子里面所有的对象发送一条release消息,最后进池子的会最先收到release消息
 作用:1、把对象放入到自动释放池当中 (如果不在自动释放池中发送release和autorelease是不可以的)
      2、对象发送autorelease消息时,这个对象的引用计数不会立即-1,在出池子的时候才会-1;
      3、一个对象只能调用一次autorelease,不能多次调用
      4、autorelease的一个重要作用是在便利构造器当中使用
      5、向一个对象发送autorelease消息,这个对象何时-1取决于autoreleasepool


Person.h

#import <Foundation/Foundation.h>

//想要这个类的实例可以实现copy方法,就要遵守<NSCopying>协议
@interface Person : NSObject<NSCopying>

@property(nonatomic,assign)NSString * name;
@property(nonatomic,assign)NSInteger age;

-(instancetype)initWithName:(NSString *)name andAge:(NSInteger)age;
+(instancetype)initWithName:(NSString *)name andAge:(NSInteger)age;

-(void)setName:(NSString *)name;
-(void)setAge:(NSInteger)age;

@end

Person.m

#import "Person.h"

@implementation Person
@synthesize name = _name,age = _age;



//重写父类的方法
//当对象的引用计数从0到1的过程时,由系统来自动调用
-(void)dealloc{
    NSLog(@"%@ 对象已销毁",self);
    //父类的销毁实现方法是必须得调用的
    [super dealloc];
}

//初始化方法
-(instancetype)initWithName:(NSString *)name andAge:(NSInteger)age{
    if (self = [super init]) {
        _name = name;
        _age = age;
    }
    return self;
}

//便利构造器
+(instancetype)initWithName:(NSString *)name andAge:(NSInteger)age{
    Person *  p = [[Person alloc]initWithName:name andAge:age];
    //autorelease的一个重要作用是在便利构造器当中使用
    return  [p autorelease];
}

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


//setter方法的内存设置
-(void)setName:(NSString *)name{
    if (_name != name) {
        [_name release];
        _name = [name retain];
    }
}

//基本数据类型  不用做内存管理
-(void)setAge:(NSInteger)age{
    _age = age;
}

@end

main.m

#import <Foundation/Foundation.h>
#import "Person.h"


int main(int argc, const char * argv[]) {
    //相当于:NSAutoreleasePool * pool = [[NSAutoreleasePool alloc]init];
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
        
        //+alloc
        Person * p = [[Person alloc]init];   //p.retainCount = 1
        
        //得到p的引用计数(代表有多少指针指向它)
        NSUInteger pCount = p.retainCount;
        NSLog(@"p.retainCount = %lu",pCount);
        
        //retain p的引用在原来的基础上+1
        [p retain];                         //p.retainCount = 2
        [p retain];                         //p.retainCount = 3
        //release 让对象的引用计数-1
        [p release];                        //p.retainCount = 2
        [p release];                        //p.retainCount = 1
        [p release];                        //p.retainCount = 0
        
        //message sent to deallocated instance 野指针错误,向一个已经释放掉的对象发送了一条消息
        //避免野指针错误的解决方案:[nil release];
        //向一个nil发送都不会引起crash,因此当p所指向的堆内存的引用计数变为0时,p=nil;就不会引起程序的crash了
        p = nil;    //保证p所指向的空间引用计数为0 的时候,在使用的时候运行不崩溃。
        NSLog(@"p.retainCount = %lu",p.retainCount);
        
        //copy
        Person * p2 = [p copy];
        NSLog(@"p2.retainCount = %lu",p2.retainCount);
        
        //打印这两个地址,我们现在是拷贝出来的一个新的对象
        NSLog(@"p=%p,p2=%p",p,p2);
        
        //因为它遵守了NSCopying协议,所以可以调用copy这个方法
        NSString * str = @"zero";
        NSString * str2 = [str copy];
        //因为在常量区所以引用计数为-1;
        NSLog(@"str = %ld,str2 = %ld",str.retainCount,str2.retainCount);
        
        //autorelease 它的引用计数不会立即-1
        Person * p3 = [[Person alloc]init];
        [p3 autorelease];
        NSLog(@"p3.retainCount = %lu",p3.retainCount);
        
        
        @autoreleasepool {
            //接收autorelease消息的对象会被放入离它最近的自动释放池
            Person * p7 = [[[Person alloc]init]autorelease];
            NSLog(@"p7 = %ld",p7.retainCount);
            
            //用构造方法 初始化的对象
            Person * p8 = [Person initWithName:@"zero" andAge:18];
             NSLog(@"p8.retainCount = %ld",p8.retainCount);
//            [p8 release];
            p8 = nil;
            NSLog(@"p8.retainCount = %ld",p8.retainCount);
            
        }
        
        
        
        
        //----------copy--------------
        //浅拷贝:拷贝的是地址,也就是不会申请一块新的内存空间,源对象的内存空间的引用计数+1;
        //深拷贝:拷贝的是内容,也就是会申请一块新的内存空间,并把源内存空间中的内容拷贝进去,所以源内存空间的引用计数还是1,新内存空间的引用计数由0变为1;深拷贝拷贝出来的是一个可变的字符串对象。
        
//        把源对象的内容复制一份,在堆内存申请一块新内存空间,把复制的内容粘贴进去(拷贝的是内容)
        
        //浅拷贝 copy
        NSString * str5 = [[NSString alloc]initWithFormat:@"popo"];
        NSString * str6 = [str5 copy];
        NSLog(@"str5 = %@ str6 = %@",str5,str6);
        NSLog(@"str5 = %p str6 = %p",str5,str6);
        NSLog(@"str5.retainCount = %lu str6.retainCount = %lu",str5.retainCount,str6.retainCount);
        
        //深拷贝 mutableCopy
        NSMutableString * str7 = [str5 mutableCopy];
        NSLog(@"str5 = %p,str7 = %p",str5,str7);
        NSLog(@"str5.retainCount = %lu str7.retainCount = %lu",str5.retainCount,str7.retainCount);
        
        NSMutableString * mstr = [[NSMutableString alloc]initWithFormat:@"浩浩"];
        
        NSString * mstr2 = [mstr copy];
        NSLog(@"mStr = %p,  mStr2 = %p",mstr,mstr2 );
        
        NSMutableString * mStr3 = [mstr mutableCopy];
        [mStr3 appendFormat:@"你妹呢?"];
        
        NSLog(@"%@",mStr3);
        NSLog(@"mStr = %p,mStr3 = %p",mstr,mStr3);
        NSLog(@"%lu,%lu",mstr.retainCount,mStr3.retainCount);
        /*
         1、只有不可变字符串跟copy才是钱拷贝,其他三种都是深拷贝
         2、不管源字符串是否可变,只要用mutableCopy 拷贝出来的字符串都是可变的
         */
        
        Person * p5 = [Person initWithName:@"波波" andAge:18];
        Person * p6 = [p5 copy];
        p6.name = @"浩浩";
        p6.age = 20;
        NSLog(@"p5.retainCount = %lu,p6.retainCount = %lu",p5.retainCount,p6.retainCount);
        NSLog(@"p5 - %@,%ld",p5.name,p5.age);
        NSLog(@"p6 - %@,%ld",p6.name,p6.age);
        
        
        
        
    //这里的{相当于:[pool release];
    }
    
    //创建池子
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc]init];
    
    //创建三个对象放入到池子当中
    Person * p4 = [[Person alloc]init];
    Person * p5 = [[Person alloc]init];
    Person * p6 = [[Person alloc]init];
    
    //向一个对象发送autorelease消息,这个对象何时-1取决于autoreleasepool
    [p4 autorelease];
    [p5 autorelease];
    [p6 autorelease];
//    [p6 autorelease]; //引用计数为0的时候不能再次释放
    
    //打印这三个对象的地址
    NSLog(@"p4 = %@",p4);
    NSLog(@"p5 = %@",p5);
    NSLog(@"p6 = %@",p6);
    
    //释放池子
    [pool release];   
    
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值