objective C程序 内存管理

来源:互联网 发布:中国闪存技术知乎 编辑:程序博客网 时间:2024/05/20 00:16

内存管理

内存管理概念

     由于移动设备的内存机器有限所以每个被占用的内存也是有限的。不用的内存是需要回收的,否则程序会崩溃

 

   oc 管理内存的范围: 管理任何继承NSObject的对象,对其他的基本数据类型无效

    基本数据类型数据占用的存储空间是固定的 一般存储在栈区。

    对象类型是程序运行过程中动态分配的,存储在堆区,内存管理主要是 对堆区的对象 的内存管理

引用计数器

     每个oc对象都有自己的引用计数器,是一个整数表示对象被引用的次数,即同一时间有多少东西在使用这个对象,对象刚被创建时,默认计数器值为1,当计数器值变为0时,对象被销毁。

    在每个oc对象内部,都专门有4个字节的存储空间来存储引用计数器。

引用计数器的作用 : 用来判断对象是否回收的唯一依据(存在一种例外:对象值为nil时,引用计数为0,但不回收空间)就是计数器是否为0,若不为0则存在。

引用计数器的操作

    retain使得引用计数器 +1

    release使得引用计数器 -1

retainCount 得到引用计数器的值    %ld   %tu

对象的销毁

    当一个对象的引用计数器为0,那么它将被销毁,其占用的内存被系统回收。             当对象被销毁时,系统会自动向对象发送一条dealloc消息,一般会重写dealloc方法,在这里释放相关的资源,dealloc就像是对象的临终遗言一旦重写了dealloc方法就必须调用[super dealloc],并且放在代码块的最后调用(不能直接调用dealloc方法)一旦对象被回收了,那么他所占据的存储空间就不再可用,坚持使用会导致程序崩溃(野指针错误)

    注意:

        1)dealloc 方法是NSObject 的,一般我们要重写dealloc方法

        2)在dealloc 方法内部,要调用[super dealloc];

    #import<Foundation/Foundation.h>

 

@interface Person : NSObject

 

@end

#import "Person.h"

 

@implementation Person

//dealloc方法,是对象临终的遗言方法

//对象被销毁时,会默认的调用该方法

//dealloc方法 是系统根据引用计数器的值,自动调用的

- (void)dealloc{

  //先释放子类自己的空间

   NSLog(@"person dealloc");

   //再释放父类的

   [super dealloc];

}

 

@end

#import <Foundation/Foundation.h>

#import "Person.h"

int main(int argc, const char * argv[]) {

   @autoreleasepool {

       //用person实例画一个对象

       Person *p =[[Person alloc] init ];

       //查看有几个拥有者

       NSUInteger count =[p retainCount];

       

       NSLog(@"count = %lu",count);//count =1

       

       [p release];//对象要被回收。 使用release 给引用计数器-1

                     //这时retainCount 为0 对象被回收掉用dealloc 方法

       

       

    }

   return 0;

}注意:永远不要直接通过对象调用dealloc方法。对象一旦被回收不可再用,坚持使用会使程序崩溃

  3.内存管理的原则

     如果对象有人使用,就不应该回收

     如果你想使用这个对象,应该让这个对象retain  一次

     如果你不想使用这个对象你应该然然这个对象release  一次

      谁创建谁 release

      谁retain 谁release

 

#import <Foundation/Foundation.h>

 

@interface Dog : NSObject

-(void)eat;

@end

#import "Dog.h"

 

@implementation Dog

-(void)eat{

   NSLog(@"狗在吃东西");

}

 

@end

#import <Foundation/Foundation.h>

#import "Dog.h"

int main(int argc, const char * argv[]) {

   @autoreleasepool {

       //创建 new  alloc init copy

       Dog *bigYellowDog =[[Dog alloc] init];

       

       Dog*jb =[bigYellowDog retain];//  引用计数器+1 当前值为2

         [bigYellowDog release]; //谁retain 谁release

       //只有保证谁创建谁release,才能保证对象被回收

       [bigYellowDog release];

     

       

       Dog *byd = [[Dog alloc] init];

       [byd eat];

       //如果一个对象已经被释放了,这个对象就称之为僵尸对象

       [byd release];

       //这句话默认情况下不会报错

       //如果要报错,要开启僵尸对象检测,

        //byd指针也就是野指针

       [bye eat]

       

       [byd retain];//byd已经是僵尸对象了 不能复生

    }

   return 0;

}

4.内存管的研究内容

   1)野指针  定义的指针变量没有初始化  指向的空间已经被释放

   2)内存泄露

    { Person *p=[Person new]; }

   p 在栈区   [Person new];在堆区

   如果栈区的已经被释放了,而堆区的空间还没有被释放,堆区的空间就被泄露了

注意: p=nil 空指针没有志向任何店东西的指针 ,给空指针发送消息不会报错。

5.单个对象的内存管理

#import <Foundation/Foundation.h>

 

@interface Dog : NSObject

- (BOOL)compareColorWithOther:(Dog*)dog;

@end

#import "Dog.h"

 

@implementation Dog

- (BOOL)compareColorWithOther:(Dog*)dog{

   

   [dog retain];//让传入的对象的引用计数器+1

   return YES ;

}@end

#import <Foundation/Foundation.h>

#import "Dog.h"

int main(int argc, const char * argv[]) {

   @autoreleasepool {

 

       Dog * d= [[Dog alloc] init]; //1

       

       [d release];//1--》0

       //给空指针发送任何消息,都没有效果

       //[nil run];

       //避免使用僵尸对象方法是,对象释放了以后,给对象赋值为nil

       //d =nil; //nil  给对象赋值, Nil类对象

      // [d run];

       //单个内存泄露问题

         //内存泄露情况1  没有错遵守内存故那里原则,创建完之后没有release;

       //Dog *d = [[Dog alloc] init];

       //内存泄露情况2.没有遵守内存管理原则

       Dog *d = [[Dog alloc] init];//1

       [d retain];//2

       [d release];

       //retain 完了之后额米有release

       //内存泄露问题3 不当的使用nil

       //Dog *d =[[Dog alloc] init];//1

       //d=nil;

       //[d eat]

       //[d release]

          //内存泄露情况4 在方法中对传入的对象进行了retain;

       Dog* d =[[Dog alloc] init];//1

       //对象依然被泄露了

       [d compareWhithOther:d];//2

       [d release];

    }

   return 0;

}

6.多对象内存管理

  set 方法内存管理

   原则:如果在一个类中,有其他类的对象(关联关系)

       set方法书写的时候要判断是否是同一个对象,release旧值,retain新值。

           - (void)setDog:(Dog*)dog{

                //判断对象是否是原对象

                if(_dog =dog){

                    //release 旧值

                    [_dog release];

                  //retain 新值,并且赋值给实例变量

                _dog =[dog retain];

           }

       }

 

#import <Foundation/Foundation.h>

 

@interface Car : NSObject

@property int speed;

-(void)run;

@end

#import "Car.h"

 

@implementation Car

- (void)dealloc{

   NSLog(@"车被销毁了%d",_speed);

   

   [super dealloc];

   

}

-(void)run{

   NSLog(@"车以%d 的速度奔向拉萨",_speed);

}

 

@end

 

#import <Foundation/Foundation.h>

@class Car;

 

 

@interface Person : NSObject

{

   Car *_car;

}

@property (assign)NSString * name;

//拥有一辆车

 

-(void)goLasa;

- (void)setCar:(Car *)car;

-(instancetype)initWithName:(NSString*)name;

@end

#import "Person.h"

#import "Car.h"

 

@implementation Person

-(instancetype)initWithName:(NSString*)name{

   if (self =[super init]) {

       _name = name;

    }

   

   return self;

 

}

-(void)dealloc{

   

   [_car release ];

   NSLog(@"人已经挂了");

   //让父类释放

   [super dealloc];

 

}

-(void)goLasa{

   [_car run];

}

- (void)setCar:(Car *)car{

   //如果是_car == car 这是同一对象,那就不需要relaese

   if (_car != car) {

       [_car release];

       _car =[car retain];

    }

}

@end

 

#import <Foundation/Foundation.h>

#import "Person.h"

#import "Car.h"

 

 

 

int main(int argc, const char * argv[]) {

   @autoreleasepool {

       Person *p =[[Person alloc] initWithName:@"凤姐"];

       

       Car * bmw =[[Car alloc] init];

       bmw.speed =200;

       //给凤姐一辆车

       [p setCar:bmw];

       //去拉萨

       [p goLasa];

        [bmw release];

       

         [p goLasa];

        //创建新车

       Car *byd =[Car new];

        byd.speed =100;

       //凤姐给新车

       [p setCar:byd];

       [p goLasa];

         [byd release];

       

         [p release];

      

      

       

       

       

}

   return 0;

}

     }

 7.@property参数

     原子性 atomic 对属性加锁,多线程下线程安全,默认值

           nonatomic 对属性不加锁 多线程下不安全,但是速度快

     读写性 readwrite 生成getter、setter 默认值

           readonly  只生成getter方法

     set方法处理(内存管理)

                assign  直接赋值 默认值

                retain   先release原来的值,再retain新值

                  copy   先release原来的值,再copy新值

    什么时候使用retain

    在一个类中有关联其他对象的时候,这个时候的@property (nonatomic,retain)

     什么时候使用assign  实例变量是最基本的数据的时候

   //这个时候会生成 set和get方法的声明和实现

     readobnly 只会生成 get方法。默认的时readwrite

   @property (nonatomic,assign)int Num;

   

    替换set方法的名称@property(nonatomic ,setter=isVip:)

        [p setVip: ispYES];//--->[p isVip:YES]

    替换个get方法的名称@propery(nonatomic,setter=isVip:,getter=isVip);

   @class 的使用

  可以简单的引用一个类

   @class Dog //类的引入

     仅仅是告诉编译器:Dog是一个类;并不会包含Dog这个类的所有内容

   

    具体使用  在.h文件中使用@class引用一个类;在.m文件中使用#import包含这个类的.h文件

   #import作用:要把映入的头文件内容拷贝到写#import处,如果Person.h文件内容发生变化,此时的所有的Person.h这个头文件的类都要重新编译

   使用格式  @class  类名

       @clss xxx 

     含义:是告诉编译器 xxx是一个类至于有那些属性和方法此处不去检测

     好处是:如果xxx文件内容发生了改变,而不需要重新编译

   @class 可以解决循环引用的问题

       

     #import 和@class的区别

     作用上区别:#import会包含引用类的所有信息(内容),包括引用类的变量和方法

               @class仅仅是告诉编译器有这么一个类,具体这个类例有什么信息,完全不知道

     效率上的区别

      如果有上百的文件都#import了同一个文件,或者这些文件一次被#import,那么一旦最开始的问价稍有改动,后面引用到这个文件的所有类都需要重新编译一边,编译效率非常低

 8.autorelease基本使用

      自动释放池

    1)在ios程序运行过程中,会创建无数个池子,这些池子都是以栈结构(先进后出)存在的。

    2)当一个对象调用autorelease时,会将这个对象放到位于栈顶的释放池中

   基本方法

    1)会将对象放到一个自动释放放池中

    2) 当自动释放池子被销毁时,会对池子里的所有对象做一次release

    3)会返回对象本身

    4)在调用完autorelease方法后,对象的计数器不收影响(销毁时影响)

 

     自动释放池的使用

    1)创建自动释放池

       @autoreleasepool{

      }

    2)加入释放池

          在自动释放池中

         [对象 autorelease];

     Person *p = [Person new ];

    //创建自动释放池

    @autoreleasepool {//自动释放池开始

    [p autorelease]//把对象放到自动释放池 ,引用计数器不会变化

 

      }//自动释放池结束 p对象被回收

 

    好处(1)不再需要关心对象释放的时间  (2)不需要关心社么时候调用release

    

   autorelease是什么原理

     autorelease实际上只是把release的调用延迟了,对每个Autorelease ,系统只是把该Object放入了当前的autorelease pool中,当该pool被释放时,该pool中的所有Object会被调用release。

   autorelease 的使用注意

    1)并不是所有的放到自动释放池中的代码,产生的对象就会自动释放,如果需要释放,必须加入到自动释放池

       Person *p =[[Person new] autorelease];

      我们只需要在自动释放池代码块中调用autorelease就可以把对象加入到自动释放池

   2)如果对象调用;autorelease 但是,调用autorelease的时候,没有在任何一个自动释放池中,此时该对象也不会被加入到自动释放池

    自动释放池的栈结构(数据结构),和内存的栈区是不一样的对象在 位于栈顶的释放池

 

 自动释放池的应用

     NSString * str = [NSString stringWithFormat:@"xxxx"];

       NSArray * array =[NSArray array]

   Person 类方法:

     帮我们自动创建对象,并且管理对象的内存(加入到自动释放池)

   Person *p =[Person person];

   1)创建一个对象 p

    2)用完之后,系统把对象释放掉p

   Person *p =[[Person alloc] init];

      p autorelease;

    +(id)person{

        //创建对象

       return [[[Person alloc] init] autorelease]; //返回对象空间

         //能够帮我们把对象给加入到自动释放池

    }

快速创建一个学生类初始化年龄

#import <Foundation/Foundation.h>

 

@interface Student : NSObject

 

@property (nonatomic,assign) int age;

 

- (instancetype)initWithAge:(int) age;

 

+ (instancetype)studentWithAge:(int)age;

@end

#import "Student.h"

 

@implementation Student

//重写构造方法给年龄初始化

- (instancetype)initWithAge:(int) age{

  //初始化父类判断有没有成功

   if (self = [super init]) {

    //初始化子类。赋值年龄。

       _age =age;

    }

   return self;

}

 

+ (instancetype)studentWithAge:(int)age{

 

 

   return    [[[Student alloc]initWithAge:age]  autorelease];

}

 

-(void)dealloc{

 

   NSLog(@"Student dealloc");

   [super dealloc];

}

 

@end

 

#import <Foundation/Foundation.h>

#import "Student.h"

int main(int argc, const char * argv[]) {

   @autoreleasepool {

       

       Student *stu =[[Student alloc] initWithAge:18];

       NSLog(@"stu.age = %d",stu.age);

       [stu release];

       

       //快速创建一个对象 给年龄初始化

        //1)定义类方法

       //2)类方法有参数,传递一个年龄

        Student *stu1 =[Student  studentWithAge:18];

       NSLog(@"stu1.age = %d",stu1.age);

    }

   return 0;

}

0 0