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

来源:互联网 发布:名片录入软件 编辑:程序博客网 时间:2024/06/15 19:40
----------------------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 12014-06-07 11:08:08.280 dui[784:303] Car is 22014-06-07 11:08:08.281 dui[784:303] Car is 1Program 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 deallocProgram 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 = 102014-06-07 16:06:14.039 dealloc[1139:303] age = 202014-06-07 16:06:14.041 dealloc[1139:303] name = lsmseed2014-06-07 16:06:14.042 dealloc[1139:303] num = 102014-06-07 16:06:14.043 dealloc[1139:303] score = 30Program 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 deallocProgram 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 deallocProgram 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 dealloc2014-06-07 22:49:26.084 pp[1582:303] Person is deallocProgram 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 = 12014-06-07 23:15:27.123 ff[1690:303] Car is deallocProgram 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 = 02014-06-08 11:44:35.858 lsm[484:303] count = 102014-06-08 11:44:35.859 lsm[484:303] Seed dealloc2014-06-08 11:44:35.860 lsm[484:303] Seed deallocProgram 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 dealloc2014-06-08 12:12:35.202 strwea[574:303] Word is deallocProgram 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 dealloc2014-06-08 12:36:59.810 weak[690:303] Wheel is deallocProgram ended with exit code: 0

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

3.arc的使用注意

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

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


0 0
原创粉丝点击