黑马程序员_内存管理与ARC原理

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

内存管理

1.内存管理的基本原理

        移动设备的内存极其有限,在有限的内存中发挥程序的最高使用效率,这需要对内存使用达到极致。一般OC的内存管理是靠引用计数器来判断对象存在的标志。通过引用计数器的加减来回收或返回对象。因此,对引用计数器的操作就是OC内存管理的核心。  

2.引用计数器的作用

        OC的每个对象中都有一个引用计数器,引用计数器就是一个整型数。这个整型数在OC对象中用4个字节空间来存储。当对象执行alloc,new或是copy等操作时,引用计数器就加1,当对象执行release操作时对象计数器就减1。一般用retainCount来返回计数器的值。

Car.h

#import <Foundation/Foundation.h>

@interface Car : NSObject

@end


Car.m

#import "Car.h"

@implementation Car

@end


main.m

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

int main(int argc, const char * argv[])
{
    Car *c = [[Car alloc] init];
    NSLog(@"Car is %ld",[c retainCount]);
    
    [c retain];
    NSLog(@"Car is %ld",[c retainCount]);
    
    [c release];
    NSLog(@"Car is %ld",[c retainCount]);
    
    [c release];
    return 0;
}

      运行结果:

2014-06-07 11:08:08.238 dui[784:303] Car is 1
2014-06-07 11:08:08.280 dui[784:303] Car is 2
2014-06-07 11:08:08.281 dui[784:303] Car is 1
Program ended with exit code: 0

      在对Car类型对象进行操作的时候,retain和alloc都是对引用计数器加1的操作。当执行release之后引用计数器减1。如果对Car类型对象多次执行release那么就会出现野指针错误。因为release到引用计数器为0时,对象就已经被回收销毁,如再对其执行操作,由于那部分内存已不再是原来的对象所有,所以会出现非法引用。

3.内存管理的基本原则

        对内存进行管理遵循的基本原则就是谁创建,谁销毁,不是这个对象创建的,就不需要执行销毁操作。这样符合逻辑的原则,便于内存的有效利用。


set方法的内存管理

1.对象销毁方法的实现

       对象的引用计数器为0时,对象即被销毁。当对象被销毁时调用dealloc方法。

Person.h

#import <Foundation/Foundation.h>

@interface Person : NSObject

@end


Person.m

#import "Person.h"

@implementation Person

- (void)dealloc
{
    NSLog(@"Person is dealloc");
    [super dealloc];
}

@end


main.m

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

int main(int argc, const char * argv[])
{
    Person *p = [[Person alloc] init];
    [p release];
    return 0;
}

        运行结果:

2014-06-07 11:53:26.154 dealloc[890:303] Person is dealloc
Program ended with exit code: 0

        在覆写dealloc方法时一定要在最后调用父类中的dealloc方法,这样就不会报警告。因为类的创建和释放都是由系统来运作的,如果手动直接调用dealloc方法,可能造成系统崩溃,所以调用父类中的dealloc方法来交与系统处理。

2.property的参数类型

        property可以自动生成对成员进行操作的set方法和get方法。set方法和get方法随着程序的使用有时需要实现更多的拓展,所以property的参数类型使得方法拓展方便了许多。property的参数主要集中在这四个方面:内存管理,控制方法的生成,多线程管理,控制方法的名称更改。

  • 内存管理

        assign:默认类型,就是直接赋值,不用返回。

        retain:在set方法中释放旧的对象返回新的对象。

        copy:和retain一样返回新的对象。

  • 控制方法的生成

        readwrite:默认参数即生成set方法也生成get方法。

        readonly:只生成get方法。

  • 多线程管理

        nonatomic:不用多线程,提高了性能。

        atomic:默认多线程同步,性能低。

  • 控制方法的名称更改

        setter:更改set方法的名称。

        getter:更改get方法的名称。


Person.h

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property int idcode;
@property (nonatomic,assign) int age;
@property (nonatomic,retain) NSString *name;
@property (nonatomic,readonly) int num;
@property (atomic,setter = cset:,getter = cscore) int score;

- (void)setNum:(int)num;
@end


Person.m

#import "Person.h"

@implementation Person

- (void)setNum:(int)num
{
    _num = num;
}

@end


main.m

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

int main(int argc, const char * argv[])
{
    Person *p = [[Person alloc] init];
    p.idcode = 10;
    NSLog(@"idcode = %d",p.idcode);
    
    p.age = 20;
    NSLog(@"age = %d",p.age);
    
    p.name = @"lsmseed";
    NSLog(@"name = %@",p.name);
    
    [p setNum:10];
    NSLog(@"num = %d",p.num);
    
    [p cset:30];
    NSLog(@"score = %d",p.cscore);
    return 0;
}

        运行结果:

2014-06-07 16:06:13.999 dealloc[1139:303] idcode = 10
2014-06-07 16:06:14.039 dealloc[1139:303] age = 20
2014-06-07 16:06:14.041 dealloc[1139:303] name = lsmseed
2014-06-07 16:06:14.042 dealloc[1139:303] num = 10
2014-06-07 16:06:14.043 dealloc[1139:303] score = 30
Program ended with exit code: 0

      property修饰的成员可以是一般的变量,也可以是对象成员。property的参数在修饰对象成员时一般加上retain参数,这样自动生成的方法可以释放以前的对象,赋值成新的对象。

Good.h

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

@interface Good : NSObject

@property (nonatomic,retain) Book *b;
@property (nonatomic,retain) Person *p;

@end


Good.m

#import "Good.h"

@implementation Good

- (void)dealloc
{
    [_p release];
    [_b release];
    NSLog(@"Person book is dealloc");
    [super dealloc];
}

@end


Person.h

#import <Foundation/Foundation.h>

@interface Person : NSObject

@end


Person.m

#import "Person.h"

@implementation Person

@end


Book.h

#import <Foundation/Foundation.h>

@interface Book : NSObject

@end


Book.m

#import "Book.h"

@implementation Book

@end


main.m

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

int main(int argc, const char * argv[])
{
    Good *g = [[Good alloc] init];
    
    g.p = [[Person alloc] init];
    g.b = [[Book alloc] init];
    
    [g release];
    return 0;
}

      运行结果:

2014-06-07 21:52:42.141 set[1313:303] Person book is dealloc
Program ended with exit code: 0

      成员对象加上retain修饰之后可以自动生成释放旧对象赋值新对象的方法,但是不会自动生成成员对象自动销毁的方法,所以在Good类的dealloc方法中要对成员对象做release操作,让对象在调用完后自动回收。

@class的循环引用

1. @class的使用

        @class就是声明了一个类,这样在不导入对应类的头文件的情况下,就可以使用该类型做声明,但是在实现的时候还是要导入对应类的头文件。                

Person.h

#import <Foundation/Foundation.h>

@class Book;
@interface Person : NSObject

@property (nonatomic,retain) Book *b;

@end


Person.m

#import "Person.h"
#import "Book.h"
@implementation Person

- (void)dealloc
{
    [_b release];
    NSLog(@"Book is dealloc");
    [super dealloc];
}

@end


Book.h

#import <Foundation/Foundation.h>

@interface Book : NSObject

@end

Book.m

#import "Book.h"

@implementation Book

@end


main.m

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

int main(int argc, const char * argv[])
{
    Person *p = [[Person alloc] init];
    p.b = [[Book alloc] init];
    
    [p release];
    return 0;
}

        运行结果:

2014-06-07 22:15:35.117 pp[1428:303] Book is dealloc
Program ended with exit code: 0

      @class作为类的声明引用,可以提高编译效率。在需要实现的时候导入头文件,这样程序的效率也可以得到优化。

2. 循环引用僵死的解决方法

        循环引用即两个类互相使用@class引用对方,双方的成员都是对方类型的变量。这样会引发一个问题就是互相自动生成的默认方法都retain一次对方类型的对象,这会使得最终两个成员对象都无法释放销毁。

Person.h

#import <Foundation/Foundation.h>

@class Book;
@interface Person : NSObject

@property (nonatomic,retain) Book *b;

@end


Person.m

#import "Person.h"
#import "Book.h"
@implementation Person

- (void)dealloc
{
    [_b release];
    NSLog(@"Book is dealloc");
    [super dealloc];
}

@end


Book.h

#import <Foundation/Foundation.h>

@class Person;
@interface Book : NSObject

@property (nonatomic,assign) Person *p;

@end


Book.m

#import "Book.h"
#import "Person.h"
@implementation Book

- (void)dealloc
{
    NSLog(@"Person is dealloc");
    [super dealloc];
}
@end


main.m

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

int main(int argc, const char * argv[])
{
    Person *p = [[Person alloc] init];
    Book *b = [[Book alloc] init];
    
    p.b = b;
    b.p = p;
    
    [p release];
    [b release];
    return 0;
}

       运行结果:

2014-06-07 22:49:26.081 pp[1582:303] Book is dealloc
2014-06-07 22:49:26.084 pp[1582:303] Person is dealloc
Program ended with exit code: 0

        如果出现循环引用锁死不能互相释放对象的情况,在property的参数上一个用retain一个用assign。这样互相引用的对象就能被顺利释放。在dealloc方法中,如果是加retain参数的成员对象就写上release操作,如果是assign参数修饰的就不用写release了。

自动释放池

1.autorelease的作用

        当一对象调用autorelease方法时,这个对象会被放入一个自动释放池中。这个池子在被销毁的时候将会对池内所有的对象都做一次release操作。autorelease方法不会马上改变引用计数器的值,可以理解成延迟改变,autorelease方法调用之后返回对象本身。

Car.h

#import <Foundation/Foundation.h>

@interface Car : NSObject

@end


Car.m

#import "Car.h"

@implementation Car

- (void)dealloc
{
    NSLog(@"Car is dealloc");
    [super dealloc];
}

@end


main.m

#import <Foundation/Foundation.h>
#import "Car.h"
int main(int argc, const char * argv[])
{
    @autoreleasepool
    {
        Car *c = [[[Car alloc] init] autorelease];
        NSLog(@"Car count = %ld",[c retainCount]);
    }
    return 0;
}

      运行结果:

2014-06-07 23:15:27.023 ff[1690:303] Car count = 1
2014-06-07 23:15:27.123 ff[1690:303] Car is dealloc
Program ended with exit code: 0

        autorelease的使用使得创建出来的对象可以不用关心内存何时被释放,也不用考虑引用间的复杂关系。对于内存占用较多的对象不易使用autorelease,如果内存占用较小,autorelease对内存影响较小。

2.自动释放池的创建

        自动释放池即对象调用autorelease方法时都会进入的池子。自动释放池可以创建多个,也可以嵌套创建。

        自动释放池创建的一般形式:

@autoreleasepool
{
    // ....
}

      嵌套形式:

@autoreleasepool
{
         @autoreleasepool
         {

         }
}

3.autorelease的应用

         有了autorelease在创建对象的时候就不用再考虑内存管理,有多少内存用多少。一般除了alloc,new,copy等方法其他的方法都内部声明了autorelease,这样我们再使用方法的时候就不用写release操作了。但是,有的时候还是需要自己实现创建对象后的自动释放。

Seed.h

#import <Foundation/Foundation.h>

@interface Seed : NSObject
@property int count;

+ (id)initSeed;
+ (id)initWithSeed:(int)count;

@end


Seed.m

#import "Seed.h"

@implementation Seed

+ (id)initSeed
{
    return [[[self alloc] init] autorelease];
}

+ (id)initWithSeed:(int)count
{
    Seed *s = [self initSeed];
    s.count = 10;
    return s;
}

- (void)dealloc
{
    NSLog(@"Seed dealloc");
    [super dealloc];
}
@end


main.m

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

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

    @autoreleasepool {
        Seed *s = [Seed initSeed];
        NSLog(@"count = %d",s.count);
        
        Seed *s1 = [Seed initWithSeed:10];
        NSLog(@"count = %d",s1.count);
    }
    return 0;
}

        运行结果:

2014-06-08 11:44:35.854 lsm[484:303] count = 0
2014-06-08 11:44:35.858 lsm[484:303] count = 10
2014-06-08 11:44:35.859 lsm[484:303] Seed dealloc
2014-06-08 11:44:35.860 lsm[484:303] Seed dealloc
Program ended with exit code: 0

ARC新特性

1.arc的基本原理

        arc即全称为automatic reference counting。这是xcode提供的新特性,它能很好地帮助程序员管理内存,使得对象地内存不用再使用手动方式来管理。arc的原理就是当一个强引用指向对象还存在时,这个对象就一直在内存中不被释放。

2.强指针和若指针

  • 强指针

       一般默认的指针都是强指针,强指针指向的对象不能被回收。强指针用__strong或strong关键字来表示。

  • 弱指针

         弱指针用__weak或weak来表示。弱指针指向的对象被回收,指针自动置为nil。弱指针指向的对象不会出现野指针错误。

Str.h

#import <Foundation/Foundation.h>
@class Word;

@interface Str : NSObject

@property (nonatomic,strong) Word *w;

@end


Str.m

#import "Str.h"

@implementation Str

- (void)dealloc
{
    NSLog(@"Str is dealloc");
}

@end


Word.h

#import <Foundation/Foundation.h>

@interface Word : NSObject

@end


Word.m

#import "Word.h"

@implementation Word

- (void)dealloc
{
    NSLog(@"Word is dealloc");
}

@end


main.m

#import <Foundation/Foundation.h>
#import "Str.h"
#import "Word.h"

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

    @autoreleasepool {
        Str *s = [Str new];
        s.w = [Word new];
    }
    return 0;
}

        运行结果:

2014-06-08 12:12:35.197 strwea[574:303] Str is dealloc
2014-06-08 12:12:35.202 strwea[574:303] Word is dealloc
Program ended with exit code: 0

        由于IOS7默认强制支持arc模式,所以使用arc之后,成员对象原来使用的retain参数直接改为strong就可以正常使用了。当使用了arc之后,在两个类中也有可能出现互相引用对方类型的对象,导致双方僵持不休的情况。所以,当循环引用时,一端用强引用,一端用弱引用。

Car.h

#import <Foundation/Foundation.h>
@class Wheel;

@interface Car : NSObject

@property (nonatomic,strong) Wheel *w;

@end


Car.m

#import "Car.h"

@implementation Car

- (void)dealloc
{
    NSLog(@"Car is dealloc");
}

@end


Wheel.h

#import <Foundation/Foundation.h>
@class Car;

@interface Wheel : NSObject

@property (nonatomic,weak) Car *c;

@end


Wheel.m

#import "Wheel.h"

@implementation Wheel

- (void)dealloc
{
    NSLog(@"Wheel is dealloc");
}

@end

main.m

#import <Foundation/Foundation.h>
#import "Car.h"
#import "Wheel.h"

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

    @autoreleasepool {
        Car *c = [Car new];
        c.w = [Wheel new];
    }
    return 0;
}

        运行结果:

2014-06-08 12:36:59.807 weak[690:303] Car is dealloc
2014-06-08 12:36:59.810 weak[690:303] Wheel is dealloc
Program ended with exit code: 0

        使用weak修饰的成员对象,在定义完之后就会自动释放,不会等到程序结束时再释放。

3.arc的使用注意

  • arc模式下不用再调用release,retain,retainCount来管理内存,arc已经自动实现了这些操作。
  • 在实现中可以重写dealloc方法,但是不用再调用父类中的dealloc方法。
  • 简单地来说,以前用retain的现在都用strong来修饰。
  • 两端互相引用时,一端用strong,一端用weak。

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


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值