——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
一、内存管理的基本管理
1.由于移动设备的内存有限,所以我们必须确保在需要的时候分配内存,在程序运行结束的时候释放占用的内存。如果只分配而不释放,就会发生内存泄漏。
2.管理的范围:只对任何继承NSObject的对象(存放堆区的),而对其他的基本数据无效。
3.原理:
对象所有权:任何自己创建的对象都归自己所有。
如果一个对象有指向其他对象的实例变量,则称该对象拥有这些对象。如果一个函数创建了一个对象,则称该函数拥有这个对象。
引用计数器:是判断对象是否要回收的依据。当一个对象没有了拥有者时,它的的引用计数器归0,此时需要回收。
当某段代码需要访问一个对象时,给系统发送retain消息,该代码将该对象的引用计数器加一,表示“我要访问该对象”。
当某段代码结束访问时,给系统发送release消息,将对象的引用计数器减一,表示不再访问该对象。
直到引用计数器的值retainCount为0时,表示不再有代码访问该对象了。因此它将被销毁,其占用的内存被系统回收以便重用。
当对象被销毁时,会调用系统的dealloc方法。但是需要重写。
重写dealloc
-(void)dealloc{
NSLog(@“XXXXX dealloc”);
[super dealloc]; // super 的作用是访问父类的dealloc方法
Person *p1=[Person new];
[p1 retainCount]; //此时为1
Person*p2=p1;
[p2 retainCount];//此时还是为1;
分类:
MRC(手动内存管理)
ARC(自动内存管理)Xcode自动默认的。
垃圾回收
二、原则
1.
如果对象有人使用,就不应该被回收;
谁创建 谁release;
谁retain 谁release;
有始有终;
2.
内存管理的研究内容:
1)野指针: 定义指针时候没有初始化, 或则是指向的空间内容已经被释放。
2)内存泄露: 栈区的p指针已经被释放,而堆区的空间还未释放。
{
Person *p1=[Person new];
} //此刻造成内存泄露
3.
单个对象的内存管理
如果对象已经被释放,那么这个对象称为僵尸对象;Xcode 中勾选了僵尸对象判断,则系统会报错。如果没有勾选,
被释放后的对象还是能够访问曾经指向的空间,但是此刻是以野指针访问的。
为了避免使用僵尸对象可以在对象释放后将对象指向nil。
4**set方法的内存管理**
在一个类中 存在其他的关联关系的对象,set方法书写时要release 旧值,retain新值。
-(void)setDog:(Dog*)dog{
if(_dog!=dog){
[_dog release];
[_dog=dog retain ;]
}
}
当为基本数据类型时,可以直接赋值。
- MRC下@property的参数
1)原子性
automic 对属性加锁
nonautomic 对属性不加锁 不安全
读写性:
readwrite 读写
readonly 可读
内存管理:
assign 直接赋值
retain release旧值 retain新值
copy
@property (nonatomic ,assign)Car* car;
如果对对象直接赋值,会造成内存泄露。
在一个类中有关联其他对象的时候用retain。
如果要替换setter和getter的名称,可以
@property (nonatomic ,setter=isVip:)
@property (nonatomic ,getter=isVip:)
6.@class 的使用
@class 类名A;
它的作用就是告诉编译器,类A只是一个类,对于类中有哪些属性和方法,不去判断检测。
好处是 当A中的内容发生了改变,不需要重新进行编译。,他可以解决循环引用的问题。
7.循环retain
循环retain会造成两个对象都发生内存泄露,为防止这种现象
1)让其中的一个对象多释放一次
2)一个对象用retain,另一个对象用assign;
三、自动释放池
1.当[p autorelease]时,会将p以栈的形式存放在栈区的释放池中,此时retainCount不变;
2.自动释放池结束时候,会给池中发送一条release消息,相当于一次release。
3.放到释放池中的代码需要手动加入到释放池[p release];才能生效。
4.autorelease的嵌套,作用域就在本代码块的区间内。
5.快速创建对象(用构造方法)
要求: 1)快速创建对象
2)程序结束时能将对象自动释放
构建一个student类,通过重写构造方法实现创建学生对象的时候能够默认制定的年龄。
//**Person.m**
#import <Foundation/Foundation.h>
@interface Student : NSObject
@property(nonatomic,assign)int age;
-(instancetype)initWith:(int)age;
+(instancetype)studentWith:(int)age;
@end
//**Person.h**
@implementation Student
+(instancetype)studentWith:(int)age{ //instancetype 的作用要优于id,是因为前者能够智能判断赋值的指针变量的类型。
return [[[self alloc]initWith:age]autorelease]; //自定义 且自动加入释放池
}
-(instancetype)initWith:(int)age{
if (self==[super init]) { //重写构造方法
_age=18;
}
return self;
}
-(void)dealloc{
NSLog(@"self dealloc");
[super dealloc];
}
@end
//**main.m**
#import <Foundation/Foundation.h>
#import "Student.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Student*stu1=[Student studentWith:18];
Student *stu2=[Student studentWith:20];
NSLog(@"%d",stu1.age);
NSLog(@"%d",stu2.age);
}
return 0;
}
四、ARC
1.正常创建对象,不用手动释放。
2.自动引用计数,当没有强指针指向时,ARC自动释放对象。
3.重写dealloc方法时不用写[super dealloc];
4.@property参数中没有了retain ,换成了强指针__strong 和弱指针__weak。
5.
Car *c1=[Car new];
c1=nil;
此时c1的指向发生改变,无强指针指向的空间瞬间被释放。
作用同于
__weak Car*c2=[Car new];
建立变量时候直接被释放,无效表达式。
6.ARC下的循环引用问题
strong P.dog=dog1;
Strong dog.owner= p1;
在这种情况下,即使当代码块运行结束时dog1和p1的指向都还只向着对方,仍然不会释放,造成内存泄露。
解决方案:其中一个对象设置为strong,另一个设置为weak;
Dog.h
#import <Foundation/Foundation.h>
@class Person;//避免循环引用
@interface Dog : NSObject
@property (nonatomic,strong)Person* owner;
//owner 的指针是强指针
-(void)run;
@end
Dog.m
#import "Dog.h"
@implementation Dog
-(void)run{
NSLog(@"狗在跑。");
}
-(void)dealloc{
NSLog(@"dog dealloc");
}
@end
Person.h
#import <Foundation/Foundation.h>
#import "Dog.h"
@interface Person : NSObject
@property(nonatomic,strong)Dog* dog;
//dog的指针也是强指针
-(void)liuDog;
@end
@implementation Person
-(void)liuDog{
NSLog(@"人在遛狗。");
}
-(void)dealloc{
NSLog(@"Person dealloc");
}
@end
main.m
#import "Dog.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p1=[[Person alloc]init];
Dog*byd =[Dog new];
p1.dog=byd;
byd.owner=p1;
//此时两个指针互相指向对方,而且都是强指针,所以即使在ARC的情况下,当函数体结束时,两个指针及指向的空间都不会释放,造成内存泄露。
解决的方案就是将其中之一的对象的属性改为weak。
[p1 liuDog];
}
return 0;
}
7.ARC中如果弱指针指向的对象不见了,则弱指针做清空操作(指向nil)。