黑马程序员——内存管理

---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------

终于将最难的内存管理看完了,真心觉得内存管理学起来比较抽象,其实内存管理说难也不是很难,只有一个retain和release,只要能够正确的判断什么时候应该retain,什么时候应该release那么就说明已经掌握内存管理的机制了。但是这也是最难的就是如何判断对象什么时候应该release什么时候应该retain。

MJ老师说的一般看见alloc就必须在后面加上一个release,而且操作都要写在relese前面,防止一个很神奇的错误,就是野指针错误。

说到这里又需要讲一下什么是野指针错误。其实就是一个指针指向了一个被释放的回收的对象。那个对象被称为僵尸对象,而且僵尸对象一旦称为僵尸之后就不能再复活了。

不能说你先relese将对象回收完后对象已经变为僵尸对象了你再一个retain对象又可以重新复活,这明显是不行的。


还有每个对象里面都有一个计数器,当计数器为0的时候对象就会被回收,而且计数器是用4个字节来存放的。

方法的基本使用

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

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

retainCount:获取当前的计数器

dealloc

当一个对象要被回收的时候,就会调用

一定要调用[super dealloc],这句调用要放在最后面

 

概念

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

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

空指针 :没有指向任何东西的指针(存储的东西是nilNULL0),给空指针发送消息不会报错

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

int main()
{
    // 1
    Person *p = [[Person alloc] init];
    
    //NSUInteger c = [p retainCount];
    
    //NSLog(@"计数器:%ld", c);
    
    
    // 2 retain方法返回的是对象本身
    [p retain];
    
    // 1
    [p release];
    
    // 0 野指针:指向僵尸对象(不可用内存)的指针
    [p release];
    
    [p retain];
    
    // -[Person setAge:]: message sent to deallocated instance 0x100109a10
    // 给已经释放的对象发送了一条-setAge:消息:
    p.age = 10;
    //[p setAge:10];
    
    // 指针p变成空指针
    //p = nil;
    
    // EXC_BAD_ACCESS : 访问了一块坏的内存(已经被回收、已经不可用的内存
    // 野指针错误
    // OC不存在空指针错误,给空指针发送消息,不报错
    [p release];
    [p release];
    [p release];
    [p release];
    
    [nil release];
    
    return 0;
}

当出现一个对象需要使用另外一个对象的时候,那么就要在这个对象中重写他的delloc方法。

因为当你自己这个对象被销毁回收之后,你使用或者调用的对象如果没被释放那么就会出现野指针错误,因此在delloc方法中就要将调用的对象release下才行

你想使用(占用)某个对象,就应该让对象的计数器+1(让对象做一次retain操作)

你不想再使用(占用)某个对象,就应该让对象的计数器-1(让对象做一次release

retain,谁release

alloc,谁release

还有在调用对象的set方法的时候当传入的参数是一个类的时候就需要将当期使用的类release然后将传入的类retain,因为retain方法功能是将计数器加一并且还有一个返回值就是放回对象本身,所以可以用当前对象来接受


/*内存管理代码规范:
 1.只要调用了alloc,必须有release(autorelease)
   对象不是通过alloc产生的,就不需要release*/
 
 //2.set方法的代码规范
 //1> 基本数据类型:直接复制
 - (void)setAge:(int)age
 { 
    _age = age;
 }
 
 //2> OC对象类型
 - (void)setCar:(Car *)car
 {
    // 1.先判断是不是新传进来对象
    if ( car != _car )
    {
        // 2.对旧对象做一次release
        [_car release];
 
        // 3.对新对象做一次retain
        _car = [car retain];
    }
 }

//3.dealloc方法的代码规范
 //1> 一定要[super dealloc],而且放到最后面
 //2> 对self(当前)所拥有的其他对象做一次release
 - (void)dealloc
 {
    [_car release];
    [super dealloc];
 }

记得之前在讲set,get方法的时候,讲了一个关键字@property 数据类型 类型名

已这种方式来写的xcode的就会自动为我们生成一个set和get方法

现在要遵循oc的内存管理机制,每次在使用alloc的时候都需要release这样写就很麻烦。

所以我们还是可以用@property这个关键字来对变量名进行声明,但是需要在@property后面加上两个参数 (nonatomic,retain)起中nonatomic是起到一个优化的作用mj老师说的必须加,然后retain参数就是实现一个生成的set方法里面,release旧值,retain新值。

但是在implementation中还是需要重写delloc方法,用来release调用过的对象


Person.h

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

@interface Person : NSObject
@property int age;

// retain : 生成的set方法里面,release旧值,retain新值
@property (nonatomic,retain) Book *book;
@property (nonatomic,retain) NSString *name;

@end

Person.m

#import "Person.h"

@implementation Person

- (void)dealloc
{
    [_book release];
    [_name release];
    [super dealloc];
}

@end


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

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

assign : 直接赋值(默认,适用于非OC对象类型)

copy   : release旧值,copy新值

是否要生成set方法

readwrite :同时生成settergetter的声明、实现(默认)

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

多线程管理

nonatomic :性能高 (一般就用这个)

atomic    :性能低(默认)

settergetter方法的名称

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

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


循环引用

循环引用就是两个对象相互调用

such as:学生有一个学号,学号也对应一个学生。那么在对象的相互调用的过程中,如果在声明变量时还是用@property(nonatomic,retain)的话,相互调用那么内存永远不会被释放。

那么解决这个循环引用的方法就是一段用retain一段用assign。


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

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

开发中引用一个类的规范

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

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

两端循环引用解决方案

一端用retain

一端用assign


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

int main()
{
    // p - 1
    Person *p = [[Person alloc] init];
    // c - 1
    Card *c = [[Card alloc] init];
    
    // c - 2
    p.card = c;
    
    // p - 1
    c.person = p;
    
    // c - 1
    [c release];
    
    // p - 0  c - 0
    [p release];
    return 0;
}

Person.h

#import <Foundation/Foundation.h>
#import "Card.h"
// @class仅仅是告诉编译器,Card是一个类
//@class Card;

@interface Person : NSObject

@property (nonatomic, retain) Card *card;

@end

Person.m

#import "Person.h"
#import "Card.h"

@implementation Person

- (void)dealloc
{
    NSLog(@"Person被销毁了");
    [_card release];
    
    [super dealloc];
}

@end

card.h

#import <Foundation/Foundation.h>


@class Person;

@interface Card : NSObject

@property (nonatomic, assign) Person *person;

@end

最后一个讲的时autorelease

光理解这个单词就能知道这个关键字大概有什么用 自动释放

autorelease其实就是一个类,在IOS 5.0前就是调用的NSAutoreleasePool这个类方法来创建autorelease自动释放池

在IOS 5.0以后就直接使用@autoreleasepool{}中的类需要利用autorelease的就将对象写在里面,在init之后在调用autorelease方法就行了。这样写了之后我们就不用在手动释放内存了,不用在不停的写release retain之类的,大大增加了程序员编写是的效率。


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

int main()
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    
    Person *pp = [[[Person alloc] init] autorelease];
    
    [pool release]; // [pool drain];
    
    @autoreleasepool
    {
    
        // 1
        Person *p = [[[[Person alloc] init] autorelease] autorelease];
        
        // 0
        // [p release];
    }
    
    return 0;
}


void test()
{
    @autoreleasepool
    {// { 开始代表创建了释放池
        
        // autorelease方法会返回对象本身
        // 调用完autorelease方法后,对象的计数器不变
        // autorelease会将对象放到一个自动释放池中
        // 当自动释放池被销毁时,会对池子里面的所有对象做一次release操作
        Person *p = [[[Person alloc] init] autorelease];
        
        p.age = 10;
        
        
        
        @autoreleasepool
        {
            // 1
            Person *p2 = [[[Person alloc] init] autorelease];
            p2.age = 10;
            
            
        }
        
        
        Person *p3 = [[[Person alloc] init] autorelease];
        
        
    } // } 结束代表销毁释放池
}

最后就是oc历史性的功能ARC机制。那么后面再说。。

---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值