黑马程序员—Objective-C学习—类和对象的内存管理

------ Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

管理范围:任何继承了NSObject的对象,对其他基本数据类型(int、char、float、double、struct、enum等)无效

一、引用计数器

每个OC对象都有自己的引用计数器,是一个整数,表示“对象被引用的次数”

每个OC对象内部专门有4个字节的存储空间来存储引用计数器

1、引用计数器的作用

a、当使用alloc、new或者copy创建一个新对象时,新对象的引用计数器默认就是1

b、当一个对象的引用计数器值为0时,对象占用的内存就会被系统回收

2、引用计数器的操作

a、retain、release方法的基本使用

1> retain:计数器+1;会返回对象本身

2> release:计数器-1,没有返回值

3> retainCount:获取当前的引用计数器值,打印用%ld

4> dealloc:当一个对象要被回收的时候,就会调用,不要直接调用;如果重写dealloc一定要调用[super dealloc],而且这句调用一定要放在最后面

注意:区别于初始化时的[super init];

2、概念

a、僵尸对象: 所占用内存已经被回收的对象,僵尸对象不能再使用

b、野指针: 指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错:EXC_BAD_ACCESS

c、 空指针: 没有指向任何东西的指针 (存储的东西是nil、NULL、0),给空指针发送消息不会报错,如:[nil release]

3、对象的销毁

a、当一个对象的引用计数器值为0时,那么它将被销毁,其占用的内存被系统回收

b、当一个对象被销毁时,系统会自动向对象发送一条dealloc消息

c、一般会重写dealloc方法,在这里释放相关资源,dealloc就像对象的遗言

d、一旦重写了dealloc方法,就必须调用[super dealloc],并且放在最后面调用

e、不要直接调用dealloc方法

f、一旦对象被回收了,它占用的内存就不再可用,坚持使用会导致程序崩溃(野指针错误)

#import<Foundation/Foundation.h>

@interface Person :NSObject

@propertyint age;

@end

@implementation Person

//当一个Person对象被回收的时候,就会自动调用这个方法

- (void)dealloc

{

   NSLog(@"Person对象被回收");    

   // superdealloc一定要调用;而且放在最后面

    [superdealloc];

}

@end

int main(int argc, constchar * argv[])

{      // 1

       Person *p = [[Personalloc]init];

        

       NSUInteger c = [pretainCount];

        

       NSLog(@"计数器:%ld",c);        

       // retain方法返回的是对象本身

        // 2

        [pretain];        

       // 引用计数器减1

       // 1

        [prelease];        

       // 0

        [prelease];

        

       // message sent to deallocated instance 0x100109a10

       // 给已经释放的对象发送了一条-setAge:消息

       // p.age = 10;

        

       // p指向的是僵尸对象,应清空指针,指针p变成空指针

        p =nil ;        

       // OC不存在空指针错误,给空指针发送消息不报错

        [nil release];        

       // EXC_BAD_ACCESS :访问了一块坏的内存(已经被回收、已经不可用的内存)

       // 野指针错误

        [prelease];

        [prelease];

        [prelease];

        [prelease];   

   return0;

}

二、内存管理原则

1、只要还有人在用某个对象,那么这个对象就不会被回收

2、只要你想用这个对象,就让对象的计数器+1

3、当你不再使用这个对象时,就让对象的计数器-1

4、如果你通过alloc、new、或者[mutable]copy来创建对象,那么你必须调用release或autorelease

5、只要你调用了retain,你就要调用release

三、set方法的内存管理

1、内存管理代码规范

只要调用了alloc,必须有release(autorelease)

2、set方法的代码规范

a、 基本数据类型:直接复制

- (void)setAge:(int)age

        {

            _age = age;

        }

b、OC对象类型

           - (void)setCar:(Car *)car

           {

                // 1、判断是否是新传进来的对象

                if (_car != car) {

                 // 2、对旧对象做一次release操作

                [_car release];

                 // 3、对新对象做一次retain操作

                 _car = [car retain];

                 }

            }

3、dealloc方法的代码规范

a、 一定要写[super dealloc],而且放到最后面

b、 对当前对象(self)所拥有的其他对象做一次release操作

4、@property参数

1、set方法内存管理相关的参数

a、retain : release旧值,retain新值 (适用于OC对象类型)

b、assign : 直接赋值(默认,适用于非OC对象类型) // 以后写基本数据类型代码时一定要加

c、copy   : release旧值,copy新值 

2、是否要生成set方法

a、readwrite :同时生成setter和getter的声明、实现 (默认)

b、readonly  :只会生成getter的声明和实现

3、多线程管理

a、nonatomic :性能高 (一般就用这个)// 以后写代码时一定要加

b、atomic    :性能低 (默认)

4、setter和getter方法的名称,只影响方法名不影响成员变量名

a、setter : 决定了set方法的名称,一定要有冒号:

b、getter : 决定了get方法的名称 (一般用在BOOL类型)


@interface Person :NSObject


@property (assign,readwrite,atomic)int age;


@property (retain,nonatomic)NSString *name;


@property (readonly)int height;


//返回BOOL类型的方法名一般以is开头

@property (getter = isRich,setter = setRich:)BOOL rich;


@end

四、循环引用

1、@class的作用:仅仅告诉编译器,某个名称是一个类  

      @class Person; // 仅仅告诉编译器,Person是一个类

2、开发中引用一个类的规范

a、在.h文件中用@class来声明类

b、在.m文件中用#import来包含类的所有东西

c、除了父类,引用其他类的时候,用@class,不用#import,提高性能

3、两端循环引用解决方案: 一端用retain,一端用assign

4、@class和 #import的区别

a、 #import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器这是类的声明,具体这个类里的信息,这里不需要知道,等实现文件中真正要用到时,才会真正去查看类中的信息

b、如果有上百个头文件都#import了同一个文件,或者这些文件依次被#improt,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍,而相对来 讲,使用@class方式就不会出现这种问题了

c、在.m实现文件中,如果需要引用到被引用类的实体变量或者方法时,@class还需要使用#import方式引入被引用类

五、autorelease

1、autorelease的基本用法

a、会将对象放到一个自动释放池中

b、当自动释放池被销毁时,会对池子里面的所有对象做一次release操作

c、会返回对象本身

d、调用完autorelease方法后,对象的计数器不变

2、autorelease的好处

a、不用再关心对象的释放时间

b、不用再关心什么时候调用release

3、autorelease的使用注意

a、占用内存较大的对象不要随便使用autorelease

b、占用内存较小的对象使用autorelease,没有太大影响

4、错误写法

a、alloc 之后调用了autorelease,又调用了release

@autoreleasepool {

            // 1

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

            // 0 会发生野指针错误

            [p release];

        }

b、 连续调用多次autorelease

@autoreleasepool {

            // 释放池销毁时会进行两次release操作

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

        }

5、自动释放池

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

b、当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池

6、自动释放池的创建方式

a、 IOS 5.0前

        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

 

        Person *p2 = [[[Person alloc] init] autorelease];

 

        [pool release]; // [pool drain];

b、 IOS 5.0 开始

        @autoreleasepool

        {

            

        }

#import <Foundation/Foundation.h>


@interface Person : NSObject


@property (nonatomicassignint age;


@end

@implementation Person

- (void)dealloc

{

    NSLog(@"Person --- dealloc");

    [super dealloc];

}

@end

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

{

    NSAutoreleasePool *pool = [[NSAutoreleasePool allocinit];

    

    Person *p2 = [[[Person allocinitautorelease];

    

    [pool release]; // [pool drain];

    

    @autoreleasepool

    { // {开始代表创建了释放池

        

        // autorelease方法会返回对象本身

        // 调用完autorelease方法后,对象的计数器不变

        // autorelease会将对象放到一个自动释放池中

        // 当自动释放池被销毁时,会对池子里面的所有对象做一次release操作

        Person *p = [[[Person allocinitautorelease];

        

        p.age = 10;

        

        @autoreleasepool {

            

            Person *p2 = [[[Person allocinitautorelease];

            

            p2.age =20;

        }

        

    } // }结束代表销毁释放池

    

    return 0;

}

7autorelease应用
a、系统自带的方法里面没有包含allocnewcopy,说明返回的对象都是autorelease
b、开发中经常会提供一些类方法,快速创建一个已经autorelease过的对象

    创建对象时不要直接用类名,一般用self

        + (id)person

        {

            return [[[self alloc] init] autorelease];

        }

#import<Foundation/Foundation.h>

@interface Person :NSObject

@property (nonatomic,assign)int age;


+ (id) person;

+ (id) personWithAge:(int)age;


@end

@implementation Person


+ (id)person

{

   return [[[selfalloc]init]autorelease];

}

- (void)dealloc

{

   NSLog(@"%d岁的人被销毁了",_age);

    [superdealloc];

}

+(id)personWithAge:(int)age

{

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

   Person *p = [selfperson];

    p.age = age;

   return p;

}


@end

@interface GoodPerson :Person


@property (nonatomic,assign)int money;


@end

@implementation GoodPerson

@end

int main(int argc, constchar * argv[])

{

   Person *p = [[Personalloc]init];

    p.age =19;

    [prelease];


   @autoreleasepool {

        

       Person *p1 = [Personperson];

        

        p1.age =20;

        

       Person *p2 = [PersonpersonWithAge:30];

        

        NSString *str =@"sf23";// 默认autorelease

        

       NSString *str2 = [NSStringstringWithFormat:@"age is %d",10];

        

       NSNumber *num = [[NSNumberalloc]initWithInt:10];

        [numrelease];

        

       NSNumber *num2 = [NSNumbernumberWithInt:100];

        

       GoodPerson *m = [GoodPersonpersonWithAge:340];

        m.money = 100;

        

    }

   return0;

}

六、ARC

自动引用技术,是编译器特性

ARC判断准则: 只要没有强指针指向对象,就会释放对象。 

1、ARC特点

a、 不允许调用release、retain、retainCount

b、允许重写dealloc,但不允许调用[super dealloc]

c、 @property的参数

       1>strong : 成员变量是强指针 (适用于OC对象类型)

       2>weak   : 成员变量是弱指针 (适用于OC对象类型)

       3>assign : 适用于非OC对象类型

       4>以前的retain改为用strong,其他一切不变 

2、指针分2种:

a、强指针:默认情况下,所以指针都是强指针

b、弱指针:__weak(两个下划线加weak) 

3、当两端循环引用的时候,解决方案

   a、 ARC

        一端用strong,一端用weak

    b、 非ARC

        一端用retain,一端用assign

Person.h

#import<Foundation/Foundation.h>

@classDog;


@interface Person :NSObject

@property (nonatomic,strong)Dog *dog;

@property (nonatomic,strong)NSString *name;

@property (nonatomic,assign)int age;


@end

Person.m

@implementation Person


- (void)dealloc

{

   NSLog(@"Person is dealloc");

}


@end

Dog.h

@classPerson;

@interface Dog :NSObject

@property (nonatomic,weak)Person *person;

@end

Dog.m

@implementation Dog


- (void) dealloc

{

   NSLog(@"Dog -- dealloc");

}


@end

main.m

#import<Foundation/Foundation.h>

#import"Person.h"

#import"Dog.h"


int main(int argc, constchar * argv[])

{

   Person *p = [[Personalloc]init];

    

   Dog *d = [[Dogalloc]init];

    p.dog = d;

    d.person = p;

   return0;

}


void test()

{

   Person *p = [[Personalloc]init];

    

   // Person *p2 = p; // 有这个代码,上面的对象就只有在程序结束的时候才会释放

    

   // 创建新对象,释放上面的对象

    p = [[Personalloc]init];

    

   // 销毁p对象

   // p = nil;

    

   NSLog(@"---");

}

void test2()

{

    

   // __weak Person *p = [[Person alloc] init]; //没有强指针指向,直接被释放,p变成空指针

    

   Person *p = [[Personalloc]init];

    

   // 弱指针 p2

   __weakPerson *p2 = p;

    

   // 对象在此处被释放

    p =nil;

    

    p2 =nil;

    

   NSLog(@"---");

}

void test3()

{

   Person *p = [[Personalloc]init];

    

   Dog *d = [[Dogalloc]init];

    p.dog = d;

    

   Dog *d2 = [[Dogalloc]init];

    p.dog = d2;

    

   NSLog(@"---");

    

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值