您的位置:首页 > 职场人生

黑马程序员------OC 内存管理MRC和autorelease pool

2015-07-30 21:24 603 查看
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
内存管理

1.内存管理概念

由于移动设备的内存机器有限 所以每个被占用的内存也是有限的。不用的内存是余姚回收的,否则程序会崩溃

oc 管理内存的范围: 管理任何继承NSObject的对象,对其他的基本数据类型无效

基本数据类型数据占用的存储空间是固定的 一般存储在栈区。

对象类型是程序运行过程中动态分配的,存储在堆区,内存管理主要是 对堆区的对象 的内存管理

oc内存管理分类:MRC(手动管理内存) ARC(自动管理内存)

2.引用计数器

引用计数器是用来保存当前对象有几个东西在使用它(数字),在对象里有一块专门的空间大小为8个字节来存储

引用计数器的作用 : 用来判断对象是否应该回收(如果对象不等于nil 当引用计数器为0,此时要回收对象的内存空间)

引用计数器的操作

retain 使得引用计数器 +1

release 使得引用计数器 -1

retainCount 得到引用计数器的值

如果一个对象被释放的时候,就会调用“临终遗言”(dealloc方法)

注意:

1)dealloc 方法是NSObject 的,一般我们要重写dealloc方法

2)在dealloc 方法内部,要调用[super dealloc];

#import <Foundation/Foundation.h>

@interface Person : NSObject

@end
#import "Person.h"

@implementation Person
//dealloc方法,是对象临终的遗言方法
//对象被销毁时,会默认的调用该方法
//dealloc方法 是系统根据引用计数器的值,自动调用的
- (void)dealloc{
//先释放子类自己的空间
NSLog(@"person dealloc");
//再释放父类的
[super dealloc];
}

@end
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
//用person实例画一个对象
Person *p =[[Person alloc] init ];
//查看有几个拥有者
NSUInteger count =[p retainCount];

NSLog(@"count = %lu",count);//count =1

[p release];//对象要被回收。 使用release 给引用计数器-1
//这时retainCount 为0 对象被回收掉用dealloc 方法

}
return 0;


注意:永远不要直接通过对象调用dealloc方法。对象一旦被回收不可再用,坚持使用会使程序崩溃

3.内存管理的原则

如果对象有人使用,就不应该回收

如果你想使用这个对象,应该让这个对象retain 一次

如果你不想使用这个对象你应该然然这个对象 release 一次

谁创建谁 release

谁retain 谁release

#import <Foundation/Foundation.h>

@interface Dog : NSObject
-(void)eat;
@end
#import "Dog.h"

@implementation Dog
-(void)eat{
NSLog(@"狗在吃东西");
}

@end
#import <Foundation/Foundation.h>
#import "Dog.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
//创建 new  alloc init  copy
Dog *bigYellowDog =[[Dog alloc] init];

Dog*jb =[bigYellowDog retain];//  引用计数器+1 当前值为2
[bigYellowDog release]; //谁retain 谁release
//只有保证谁创建谁release,才能保证对象被回收
[bigYellowDog release];

Dog *byd = [[Dog alloc] init];
[byd eat];
//如果一个对象已经被释放了,这个对象就称之为僵尸对象
[byd release];
//这句话佛人情况下不会报错
//如果要报错,要开启僵尸对象检测,
//byd指针也就是野指针
[bye eat]

[byd retain];//byd已经是僵尸对象了 不能复生
}
return 0;
}


4.内存管的研究内容

1)野指针 定义的指针变量没有初始化 指向的空间已经被释放

2)内存泄露

{ Person *p=[Person new]; }

p 在栈区 [Person new];在堆区

如果栈区的已经被释放了,而堆区的空间还没有被释放,堆区的空间就被泄露了

注意: p=nil 空指针 没有志向任何店东西的指针 ,给空指针发送消息不会报错。

5.单个对象的内存管理

#import <Foundation/Foundation.h>

@interface Dog : NSObject
- (BOOL)compareColorWithOther:(Dog*)dog;
@end
#import "Dog.h"

@implementation Dog
- (BOOL)compareColorWithOther:(Dog*)dog{

[dog retain];//让传入的对象的引用计数器+1
return YES ;
}@end
#import <Foundation/Foundation.h>
#import "Dog.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {

Dog * d= [[Dog alloc] init]; //1

[d release];//1--》0
//给空指针发送任何消息,都没有效果
//[nil run];
//避免使用僵尸对象方法是,对象释放了以后,给对象赋值为nil
//d =nil; //nil  给对象赋值 , Nil类对象
// [d run];
//单个内存泄露问题
//内存泄露情况1  没有错遵守内存故那里原则,创建完之后没有release;
//Dog *d = [[Dog alloc] init];
//内存泄露情况2.没有遵守内存管理原则
Dog *d = [[Dog alloc] init];//1
[d retain];//2
[d release];
//retain 完了之后额米有release
//内存泄露问题3 不当的使用nil
//Dog *d =[[Dog alloc] init];//1
//d=nil;
//[d eat]
//[d release]
//内存泄露情况4 在方法中对传入的对象进行了retain;
Dog* d =[[Dog alloc] init];//1
//对象依然被泄露了
[d compareWhithOther:d];//2
[d release];
}
return 0;
}


6.多对象内存管理

set 方法内存管理

原则: 如果在一个类中,有其他类的对象(关联关系)

set方法书写的时候要判断是否是同一个对象,release 旧值,retain新值。

- (void)setDog:(Dog*)dog{

//判断对象是否是原对象

if(_dog =dog){

//release 旧值

[_dog release];

//retain 新值 ,并且赋值给实例变量

_dog =[dog retain];

}

}

#import <Foundation/Foundation.h>

@interface Car : NSObject
@property int speed;
-(void)run;
@end
#import "Car.h"

@implementation Car
- (void)dealloc{
NSLog(@"车被销毁了%d",_speed);

[super dealloc];

}
-(void)run{
NSLog(@"车以%d 的速度奔向拉萨",_speed);
}

@end

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

@interface Person : NSObject
{
Car *_car;
}
@property (assign)NSString * name;
//拥有一辆车

-(void)goLasa;
- (void)setCar:(Car *)car;
-(instancetype)initWithName:(NSString*)name;
@end
#import "Person.h"
#import "Car.h"

@implementation Person
-(instancetype)initWithName:(NSString*)name{
if (self =[super init]) {
_name = name;
}

return self;

}
-(void)dealloc{

[_car release ];
NSLog(@"人已经挂了");
//让父类释放
[super dealloc];

}
-(void)goLasa{
[_car run];
}
- (void)setCar:(Car *)car{
//如果是_car == car 这是同一对象 ,那就不需要relaese
if (_car != car) {
[_car release];
_car =[car retain];
}
}
@end

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

int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p =[[Person alloc] initWithName:@"凤姐"];

Car * bmw =[[Car alloc] init];
bmw.speed =200;
//给凤姐一辆车
[p setCar:bmw];
//去拉萨
[p goLasa];
[bmw release];

[p goLasa];
//创建新车
Car *byd =[Car new];
byd.speed =100;
//凤姐给新车
[p setCar:byd];
[p goLasa];
[byd release];

[p release];

}
return 0;
}
}

7.@property参数

原子性 atomic 对属性加锁,多线程下线程安全,默认值

nonatomic 对属性不加锁 多线程下不安全,但是速度快

读写性 readwrite 生成getter、setter 默认值

readonly 只生成getter方法

set方法处理(内存管理)

assign 直接赋值 默认值

retain 先release原来的值,再retain新值

copy 先release原来的值,再copy新值

什么时候使用retain

在一个类中有关联其他对象的时候,这个时候的@property (nonatomic,retain)

什么时候使用assign 实例变量是最基本的数据的时候

//这个时候会生成 set和get方法的声明和实现

readobnly 只会生成 get方法。默认的时readwrite

@property (nonatomic,assign)int Num;

替换set方法的名称 @property(nonatomic ,setter=isVip:)

[p setVip: ispYES];//--->[p isVip:YES]

替换个get方法的名称 @propery(nonatomic,setter=isVip:,getter=isVip);

@class 的使用

可以简单的引用一个类

@class Dog //类的引入

仅仅是告诉编译器:Dog是一个类;并不会包含Dog这个类的所有内容

具体使用 在.h文件中使用@class引用一个类;在.m文件中使用#import包含这个类的.h文件

#import作用:要把映入的头文件内容拷贝到写#import处 ,如果Person.h文件内容发生变化,此时的所有的Person.h这个头文件的类都要重新编译

使用格式 @class 类名

@clss xxx

含义:是告诉编译器 xxx是一个类 至于有那些属性和方法此处不去检测

好处是:如果xxx文件内容发生了改变,而不需要重新编译

@class 可以解决循环引用的问题

#import 和@class的区别

作用上区别:#import会包含引用类的所有信息(内容),包括引用类的变量和方法

@class仅仅是告诉编译器有这么一个类,具体这个类例有什么信息,完全不知道

效率上的区别

如果有上百的文件都#import了同一个文件,或者这些文件一次被#import,那么一旦最开始的问价稍有改动,后面引用到这个文件的所有类都需要重新编译一边,编译效率非常低

8.autorelease基本使用

自动释放池

1)在ios程序运行过程中,会创建无数个池子,这些池子都是以栈结构(先进后出)存在的。

2)当一个对象调用autorelease时,会将这个对象放到位于栈顶的释放池中

基本方法

1)会将对象放到一个自动释放放池中

2) 当自动释放池子被销毁时,会对池子里的所有对象做一次release

3)会返回对象本身

4)在调用完autorelease方法后,对象的计数器不收影响(销毁时影响)

自动释放池的使用

1)创建自动释放池

@autoreleasepool{

}

2)加入释放池

在自动释放池中

[对象 autorelease];

Person *p = [Person new ];

//创建自动释放池

@autoreleasepool {//自动释放池开始

[p autorelease]//把对象放到自动释放池 ,引用计数器不会变化

}//自动释放池结束 p对象被回收

好处 (1)不再需要关心对象释放的时间 (2)不需要关心社么时候调用release

autorelease是什么原理

autorelease实际上只是把release的调用延迟了,对每个Autorelease ,系统只是把该Object放入了当前的autorelease pool中,当该pool被释放时,该pool中的所有Object会被调用release。

autorelease 的使用注意

1)并不是所有的放到自动释放池中的代码,产生的对象就会自动释放,如果需要释放,必须加入到自动释放池

Person *p =[[Person new] autorelease];

我们只需要在自动释放池代码块中调用autorelease就可以把对象加入到自动释放池

2)如果对象调用;autorelease 但是,调用autorelease的时候,没有在任何一个自动释放池中,此时该对象也不会被加入到自动释放池

自动释放池的栈结构(数据结构),和内存的栈区是不一样的 对象在 位于栈顶的释放池

自动释放池的应用

NSString * str = [NSString stringWithFormat:@"xxxx"];

NSArray * array =[NSArray array]

Person 类方法:

帮我们自动创建对象,并且管理对象的内存(加入到自动释放池)

Person *p =[Person person];

1)创建一个对象 p

2)用完之后,系统把对象释放掉p

Person *p =[[Person alloc] init];

p autorelease;

+(id)person{

//创建对象

return [[[Person alloc] init] autorelease]; //返回对象空间

//能够帮我们把对象给加入到自动释放池

}

快速创建一个学生类初始化年龄
#import <Foundation/Foundation.h>

@interface Student : NSObject

@property (nonatomic,assign) int age;

- (instancetype)initWithAge:(int) age;

+ (instancetype)studentWithAge:(int)age;
@end
#import "Student.h"

@implementation Student
//重写构造方法给年龄初始化
- (instancetype)initWithAge:(int) age{
//初始化父类 判断有没有成功
if (self = [super init]) {
//初始化子类。赋值年龄。
_age =age;
}
return self;
}

+ (instancetype)studentWithAge:(int)age{

return    [[[Student alloc] initWithAge:age]  autorelease];
}

-(void)dealloc{

NSLog(@"Student dealloc");
[super dealloc];
}

@end

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

Student *stu =[[Student alloc] initWithAge:18];
NSLog(@"stu.age = %d",stu.age);
[stu release];

//快速创建一个对象 给年龄初始化
//1)定义类方法
//2)类方法有参数,传递一个年龄
Student *stu1 =[Student  studentWithAge:18];
NSLog(@"stu1.age = %d",stu1.age);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: