博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
day9: 内存管理初级:内存管理的方式、引用计数机制,影响计数的各个方法、dealloc方法、内存管理的基本原则
阅读量:4219 次
发布时间:2019-05-26

本文共 6890 字,大约阅读时间需要 22 分钟。

内存管理的黄金法则:
    黄金法则:每次调用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 
//想要这个类的实例可以实现copy方法,就要遵守
协议@interface Person : NSObject
@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 
#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;}

转载地址:http://poxmi.baihongyu.com/

你可能感兴趣的文章
makefile学习网站
查看>>
C 编写lua模块(1)
查看>>
Lua教程:Lua调用C/C++函数(4)
查看>>
win下创建win32控制台工程,执行lua脚本
查看>>
cocos2dx android启动错误
查看>>
eclipse: android rename package name
查看>>
cocos2dx c++调用java思想
查看>>
cocos2dx lua Node节点 私有数据存取
查看>>
lua math.ceil math.ceil
查看>>
cocos2dx CCNode计算node的大小
查看>>
cocos2dx 布局记录(1)
查看>>
lua 多行注释和取消多行注释
查看>>
缩放系数计算
查看>>
cocos2dx --- 按钮点击居中放大
查看>>
cocos2dx menu位置计算
查看>>
cocos2dx资源加载机制(同步/异步)
查看>>
cocos2dx C++调用java -- 字符串传递
查看>>
git学习网站
查看>>
JavaScript 学习网站
查看>>
cocos2dx java调用c++ -- 字符串传递
查看>>