------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
@interface Person : NSObject
@end
#import "Person.h"
@implementation Person
//dealloc方法,是对象临终的遗言方法
//对象被销毁时,会默认的调用该方法
//dealloc方法 是系统根据引用计数器的值,自动调用的
- (void)dealloc{
//先释放子类自己的空间
NSLog(@"person dealloc");
//再释放父类的
[super dealloc];
}
@end
#import
#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
@interface Dog : NSObject
-(void)eat;
@end
#import "Dog.h"
@implementation Dog
-(void)eat{
NSLog(@"狗在吃东西");
}
@end
#import
#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
@interface Dog : NSObject
- (BOOL)compareColorWithOther:(Dog*)dog;
@end
#import "Dog.h"
@implementation Dog
- (BOOL)compareColorWithOther:(Dog*)dog{
[dog retain];//让传入的对象的引用计数器+1
return YES ;
}@end
#import
#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
@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
@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
#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
@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
#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;
}