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

来源:互联网 发布:淘宝素材图 编辑:程序博客网 时间:2024/05/21 01:51
内存管理的黄金法则:
    黄金法则:每次调用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