黑马程序员_内存管理与ARC原理
来源:互联网 发布:名片录入软件 编辑:程序博客网 时间:2024/06/15 19:40
内存管理
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。
- 黑马程序员_内存管理与ARC原理
- 黑马程序员_OC基础12_内存管理02ARC
- 黑马程序员---08内存管理(ARC)
- 黑马程序员_内存管理
- 黑马程序员_内存管理
- 黑马程序员_内存管理小结
- 黑马程序员——OC——ARC内存管理
- 黑马程序员——OC——ARC内存管理
- 黑马程序员——OC语言------内存管理和ARC
- 黑马程序员IOS-OC语言-内存管理、ARC
- 黑马程序员---OC基础---内存管理(MRC、ARC)
- 黑马程序员 --- 内存管理的MRC和ARC
- 黑马程序员——Objective-C的内存管理MRC与ARC——黑马 IOS 技术博客
- ARC 与内存管理
- 【黑马程序员】内存管理概念及原理
- 黑马程序员--IOS_学习笔记_内存管理_@autorelease
- 黑马程序员_数组与内存
- 黑马程序员 OC中的内存管理及MRC和ARC单个对象的内存管理问题
- SetCapture ReleaseCapture
- 用 Psyco让Python运行得像C一样快
- Spring 3.2 源码解析 -- XML bean 元素到 BeanDefinition 解析过程
- 小明陪你编游戏系列(四)GDI+实现双缓冲
- 黑马程序员_封装继承多态
- 黑马程序员_内存管理与ARC原理
- 01 计算机系统漫游
- 黑马程序员_分类协议代码块
- Quartz中扩展MethodInvokingJobDetailFactoryBean实现对任务调度的拦截
- linux 替当前目录加权限
- 基于Spring MVC的简单HelloWorld实例
- [Linux]Fedora17/18安装Ruby1.8 和 Redmine 2.2
- nose配合NoteUNit生成XML文件(命令行+脚本)
- 黑马程序员_Foundation框架