黑马程序员——入学Blog06----内存管理

-----------android培训java培训iOS培训.Net培训、期待与您交流!------------ 

         不同类型的变量的生命周期是不同的,而这不同是取决于变量在内存中存放的位置。值类型,比如int,double是存放在栈中的,系统会自动管理,而引用类型,比如对象会被分配在堆中,需要程序负责申请及回收内存。在OC中是利用指针来操纵对象的,指针与内存是息息相关的。不合理的操作将会导致:

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

        野指针:是指向被释放的或者访问受限的垃圾内存的指针

        空指针:在OC中就是一个指针变量指向nil

        关于移动设备的内存管理是指某个App占用的内存较多时,需要回收一些不再被使用的对象,它的管理范围是任何继承自NSObjct的对象,不包括基本数据类型(int,char,struct等)。

每个OC对象都拥有一个整数类型的引用计数器,用来表示有多少个人正在使用这个OC对象,而每个OC对象内部专门使用4个字节的存储空间来存储引用计数器。

         每次使用alloc,new,或者copy创建一个新对象时,引用计数器默认为1,当一个对象的引用计数器为0时,对象占用的内存就会被回收,反之,在整个程序的运行过程中,所占用的内存就不能被回收。

         引用计数器的主要操作有retain方法,可以使引用计数值 + 1,同时返回对象本身,release方法可以使引用计数器值-1,通过retainCount方法可以知道当前对象的引用计数器值,当对象的引用计数器为0时,系统会自动调用该对象的dealloc消息,dealloc方法可以被重写,但不要直接调用。

        为了让开发者专注于感兴趣的代码和对象关系,所以提供了ARC。ARC的全称是Automatic Reference Counting,即自动引用计数,它是一个编译器特性,提供了对OC对象自动管理内存。一旦开启了ARC的功能,就不能向对象发送release,retain这种操作内存的相关代码了。

        1.ARC的开启与关闭

                         

         2.ARC关闭情况下的内存管理

  • 谁创建对象,谁负责release:你通过alloc,new或者[mutable]copy创建了对象,就由你负责调用release或[autorelease]
  • 谁retain,谁release:你用retain,让对象的引用计数器+1,就由你负责release
//
//  main.m


#import <Foundation/Foundation.h>
#import "Phone.h"
#import "Person.h"
int main(int argc, const char * argv[])
{

    Person *p1 = [Person new];
    NSUInteger c = [p1 retainCount];
    NSLog(@"生成对象时的引用计数器值 = %ld",c);
    c = [[p1 retain] retainCount];
    NSLog(@"发送retain后对象的引用计数器值 = %ld",c);
    [p1 release];
    c = [p1 retainCount];
    NSLog(@"发送release后对象的引用计数器值 = %ld",c);
    [p1 release];
    NSLog(@"断点一");
    // 初始化Person与Phone对象
    Person *p2 = [[Person alloc] init];
    Phone *iphone = [[Phone alloc] init];
    Phone *samsung = [[Phone alloc] init];
    
    // p2拥有了一部iphone;
    p2.phone = iphone;
    NSLog(@"iphone对象的引用计数器为:%ld,samsung对象的引用计数器为:%ld",[iphone retainCount],[samsung retainCount]);
    // p2换成了三星手机
    p2.phone = samsung;
    NSLog(@"iphone对象的引用计数器为:%ld,samsung对象的引用计数器为:%ld",[iphone retainCount],[samsung retainCount]);
    NSLog(@"断点二");
    [samsung release];
    [iphone release];
    [p2 release];
    
    
    // autorelease的作用
    @autoreleasepool {
        Person *p3 = [[[Person alloc] init] autorelease];
        p3.age = 23;
    }
    NSLog(@"断点三");
    return 0;
}

//  手机类
//  Phone.h

#import <Foundation/Foundation.h>

@interface Phone : NSObject

// 打电话给某人
- (void) callSomeone;

// 上网
- (void) sulfTheInternet;

@end

//
//  Phone.m

#import "Phone.h"

@implementation Phone

// 打电话给某人
- (void) callSomeone
{
    NSLog(@"打电话");
}

// 上网
- (void) sulfTheInternet
{
    NSLog(@"上网");
}

// 重写dealloc方法
- (void)dealloc
{
    NSLog(@"Phone对象被回收");
    [super dealloc];
}
@end

//  人类
//  Person.h

#import <Foundation/Foundation.h>
@class Phone;
@interface Person : NSObject
{
    Phone *_phone;  // 人拥有一部手机
    int _age;        // 人的年龄
}

// 成员变量的setter && getter方法
- (void) setPhone:(Phone *)phone;
- (Phone *)phone;

- (void) setAge:(int)age;
- (int)age;
@end

//
//  Person.m

#import "Person.h"

@implementation Person

// age的setter && getter方法
- (void) setAge:(int)age
{
    _age = age;  // < 1 >
}
- (int)age
{
    return _age;
}

// Phone的setter && getter方法
- (void) setPhone:(Phone *)phone
{
   // _phone = phone; < 2 >
   // _phone = [phone retain]; < 3 >
    if (phone != _phone)
    {
        // 将旧手机丢掉
        [_phone release];
        
        // 换上新手机
        _phone = [phone retain];
    }
}
- (Phone *)phone
{
    return _phone;
}

// 重写dealloc
- (void) dealloc
{
    [_phone release];  // < 4 >
    NSLog(@"Person对象被回收");
    [super dealloc];
}
@end

第一个断点的运行结果:


    可以得出在生成OC对象时,引用计数器确实为1,retain能将引用计数器+1,release能将引用计数器-1,当引用计数器为0时,会调用类的dealloc方法回收对象



第二个断点的运行结果:


    分析:

Phone *iphone = [[Phone alloc] init];
       这个对象被iphone指针所指向,所以引用计数器为1,后面又被p2对象的成员变量_iphone所指向,所有引用计数器为应该要为2,而samsung指针所指向的对象只有一个引用者,所以引用计数器应该为1,在换手机后,samsung对象的引用计数器就应该为2,而iphone对象的引用计数器应该要减去1,变成1,以上打印是符合结果的。看看Person的setPhone方法做了什么?

      在Person.m的setPhone方法中,如果采用< 2 >的方法,没有调用retain方法,那么iphone指针所指向的对象引用计数是不会为2的,如果采用< 3 >的写法,那么在换手机的过程中,iphone指针所指向的对象的引用计数不会减少


第三个断点的运行结果:


     分析:在断点2前,phone对象被两个指针所引用,而Person对象被一个p2指针所引用,所以,Phone对象要被release两次,根据,谁retain,创建,就负责release的原则,两次release分别要在Phone对象的dealloc方法及Person对象的dealloc方法。所以< 4 > 处才写上 [ _phone release],对于p3对象,没有显式的调用release方法,但是使用@autorelease代码块中使用autorelease方法,就会再代码块结束后调用一个release,所以效果是一致的。因为内存管理是针对OC对象的,所以< 1 >处的 age变量自然是不需要retain的。


     3.开启ARC后的内存管理

      ARC的判断标准是只有没有强指针指向对象,就会释放对象,而指针默认情况下是都是强指针,最明显的变化是不需要写autorelease,release,retain这些代码了,而且也不能调用[super dealloc]。

//
//  main.m


#import <Foundation/Foundation.h>
#import "Phone.h"
#import "Person.h"
#import "Cat.h"
int main(int argc, const char * argv[])
{
    Cat *cat = [[Cat alloc] init];
    cat = [[Cat alloc] init];
    NSLog(@"断点1");
    return 0;
}

//
//  Cat.m


#import "Cat.h"

@implementation Cat

// 重写dealloc方法
- (void) dealloc
{
    NSLog(@"Cat对象被回收了");
}
@end

 运行结果:

    分析: main.m中使用了两次alloc代表有两个Cat对象被创建,但是cat指针指指向了其中一个,也就意味着没有被(强)指针指向的那个,根据ARC的判断标准这个对象需要被回收。即使代码中没有看到release。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值