------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
管理范围:任何继承了NSObject的对象,对其他基本数据类型(int、char、float、double、struct、enum等)无效
一、引用计数器
每个OC对象都有自己的引用计数器,是一个整数,表示“对象被引用的次数”
每个OC对象内部专门有4个字节的存储空间来存储引用计数器
1、引用计数器的作用
a、当使用alloc、new或者copy创建一个新对象时,新对象的引用计数器默认就是1
b、当一个对象的引用计数器值为0时,对象占用的内存就会被系统回收
2、引用计数器的操作
a、retain、release方法的基本使用
1> retain:计数器+1;会返回对象本身
2> release:计数器-1,没有返回值
3> retainCount:获取当前的引用计数器值,打印用%ld
4> dealloc:当一个对象要被回收的时候,就会调用,不要直接调用;如果重写dealloc一定要调用[super dealloc],而且这句调用一定要放在最后面
注意:区别于初始化时的[super init];
2、概念
a、僵尸对象: 所占用内存已经被回收的对象,僵尸对象不能再使用
b、野指针: 指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错:EXC_BAD_ACCESS
c、 空指针: 没有指向任何东西的指针 (存储的东西是nil、NULL、0),给空指针发送消息不会报错,如:[nil release]
3、对象的销毁
a、当一个对象的引用计数器值为0时,那么它将被销毁,其占用的内存被系统回收
b、当一个对象被销毁时,系统会自动向对象发送一条dealloc消息
c、一般会重写dealloc方法,在这里释放相关资源,dealloc就像对象的遗言
d、一旦重写了dealloc方法,就必须调用[super dealloc],并且放在最后面调用
e、不要直接调用dealloc方法
f、一旦对象被回收了,它占用的内存就不再可用,坚持使用会导致程序崩溃(野指针错误)
#import<Foundation/Foundation.h>
@interface Person :NSObject
@propertyint age;
@end
@implementation Person
//当一个Person对象被回收的时候,就会自动调用这个方法
- (void)dealloc
{
NSLog(@"Person对象被回收");
// super的dealloc一定要调用;而且放在最后面
[superdealloc];
}
@end
int main(int argc, constchar * argv[])
{ // 1
Person *p = [[Personalloc]init];
NSUInteger c = [pretainCount];
NSLog(@"计数器:%ld",c);
// retain方法返回的是对象本身
// 2
[pretain];
// 引用计数器减1
// 1
[prelease];
// 0
[prelease];
// message sent to deallocated instance 0x100109a10
// 给已经释放的对象发送了一条-setAge:消息
// p.age = 10;
// p指向的是僵尸对象,应清空指针,指针p变成空指针
p =nil ;
// OC不存在空指针错误,给空指针发送消息不报错
[nil release];
// EXC_BAD_ACCESS :访问了一块坏的内存(已经被回收、已经不可用的内存)
// 野指针错误
[prelease];
[prelease];
[prelease];
[prelease];
return0;
}
二、内存管理原则
1、只要还有人在用某个对象,那么这个对象就不会被回收
2、只要你想用这个对象,就让对象的计数器+1
3、当你不再使用这个对象时,就让对象的计数器-1
4、如果你通过alloc、new、或者[mutable]copy来创建对象,那么你必须调用release或autorelease
5、只要你调用了retain,你就要调用release
三、set方法的内存管理
1、内存管理代码规范
只要调用了alloc,必须有release(autorelease)
2、set方法的代码规范
a、 基本数据类型:直接复制
- (void)setAge:(int)age
{
_age = age;
}
b、OC对象类型
- (void)setCar:(Car *)car
{
// 1、判断是否是新传进来的对象
if (_car != car) {
// 2、对旧对象做一次release操作
[_car release];
// 3、对新对象做一次retain操作
_car = [car retain];
}
}
3、dealloc方法的代码规范
a、 一定要写[super dealloc],而且放到最后面
b、 对当前对象(self)所拥有的其他对象做一次release操作
4、@property参数
1、set方法内存管理相关的参数
a、retain : release旧值,retain新值 (适用于OC对象类型)
b、assign : 直接赋值(默认,适用于非OC对象类型) // 以后写基本数据类型代码时一定要加
c、copy : release旧值,copy新值
2、是否要生成set方法
a、readwrite :同时生成setter和getter的声明、实现 (默认)
b、readonly :只会生成getter的声明和实现
3、多线程管理
a、nonatomic :性能高 (一般就用这个)// 以后写代码时一定要加
b、atomic :性能低 (默认)
4、setter和getter方法的名称,只影响方法名不影响成员变量名
a、setter : 决定了set方法的名称,一定要有冒号:
b、getter : 决定了get方法的名称 (一般用在BOOL类型)
@interface Person :NSObject
@property (assign,readwrite,atomic)int age;
@property (retain,nonatomic)NSString *name;
@property (readonly)int height;
//返回BOOL类型的方法名一般以is开头
@property (getter = isRich,setter = setRich:)BOOL rich;
@end
四、循环引用
1、@class的作用:仅仅告诉编译器,某个名称是一个类
@class Person; // 仅仅告诉编译器,Person是一个类
2、开发中引用一个类的规范
a、在.h文件中用@class来声明类
b、在.m文件中用#import来包含类的所有东西
c、除了父类,引用其他类的时候,用@class,不用#import,提高性能
3、两端循环引用解决方案: 一端用retain,一端用assign
4、@class和 #import的区别
a、 #import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器这是类的声明,具体这个类里的信息,这里不需要知道,等实现文件中真正要用到时,才会真正去查看类中的信息
b、如果有上百个头文件都#import了同一个文件,或者这些文件依次被#improt,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍,而相对来 讲,使用@class方式就不会出现这种问题了
c、在.m实现文件中,如果需要引用到被引用类的实体变量或者方法时,@class还需要使用#import方式引入被引用类
五、autorelease
1、autorelease的基本用法
a、会将对象放到一个自动释放池中
b、当自动释放池被销毁时,会对池子里面的所有对象做一次release操作
c、会返回对象本身
d、调用完autorelease方法后,对象的计数器不变
2、autorelease的好处
a、不用再关心对象的释放时间
b、不用再关心什么时候调用release
3、autorelease的使用注意
a、占用内存较大的对象不要随便使用autorelease
b、占用内存较小的对象使用autorelease,没有太大影响
4、错误写法
a、alloc 之后调用了autorelease,又调用了release
@autoreleasepool {
// 1
Person *p =[[[Person alloc] init] autorelease];
// 0 会发生野指针错误
[p release];
}
b、 连续调用多次autorelease
@autoreleasepool {
// 释放池销毁时会进行两次release操作
Person *p =[[[[Person alloc] init] autorelease] autorelease];
}
5、自动释放池
a、 在ios程序运行过程中,会创建无数个池子,这些池子都是以栈结构存在(先进后出)
b、当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池
6、自动释放池的创建方式
a、 IOS 5.0前
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Person *p2 = [[[Person alloc] init] autorelease];
[pool release]; // [pool drain];
b、 IOS 5.0 开始
@autoreleasepool
{
}
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic, assign) int age;
@end
@implementation Person
- (void)dealloc
{
NSLog(@"Person --- dealloc");
[super dealloc];
}
@end
int main(int argc, const char * argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Person *p2 = [[[Person alloc] init] autorelease];
[pool release]; // [pool drain];
@autoreleasepool
{ // {开始代表创建了释放池
// autorelease方法会返回对象本身
// 调用完autorelease方法后,对象的计数器不变
// autorelease会将对象放到一个自动释放池中
// 当自动释放池被销毁时,会对池子里面的所有对象做一次release操作
Person *p = [[[Person alloc] init] autorelease];
p.age = 10;
@autoreleasepool {
Person *p2 = [[[Person alloc] init] autorelease];
p2.age =20;
}
} // }结束代表销毁释放池
return 0;
}
7、autorelease应用
a、系统自带的方法里面没有包含alloc、new、copy,说明返回的对象都是autorelease的
b、开发中经常会提供一些类方法,快速创建一个已经autorelease过的对象
创建对象时不要直接用类名,一般用self
+ (id)person
{
return [[[self alloc] init] autorelease];
}
#import<Foundation/Foundation.h>
@interface Person :NSObject
@property (nonatomic,assign)int age;
+ (id) person;
+ (id) personWithAge:(int)age;
@end
@implementation Person
+ (id)person
{
return [[[selfalloc]init]autorelease];
}
- (void)dealloc
{
NSLog(@"%d岁的人被销毁了",_age);
[superdealloc];
}
+(id)personWithAge:(int)age
{
// Person *p = [[[Person alloc] init] autorelease];
Person *p = [selfperson];
p.age = age;
return p;
}
@end
@interface GoodPerson :Person
@property (nonatomic,assign)int money;
@end
@implementation GoodPerson
@end
int main(int argc, constchar * argv[])
{
Person *p = [[Personalloc]init];
p.age =19;
[prelease];
@autoreleasepool {
Person *p1 = [Personperson];
p1.age =20;
Person *p2 = [PersonpersonWithAge:30];
NSString *str =@"sf23";// 默认autorelease
NSString *str2 = [NSStringstringWithFormat:@"age is %d",10];
NSNumber *num = [[NSNumberalloc]initWithInt:10];
[numrelease];
NSNumber *num2 = [NSNumbernumberWithInt:100];
GoodPerson *m = [GoodPersonpersonWithAge:340];
m.money = 100;
}
return0;
}
六、ARC机制
自动引用技术,是编译器特性
ARC判断准则: 只要没有强指针指向对象,就会释放对象。
1、ARC特点
a、 不允许调用release、retain、retainCount
b、允许重写dealloc,但不允许调用[super dealloc]
c、 @property的参数
1>strong : 成员变量是强指针 (适用于OC对象类型)
2>weak : 成员变量是弱指针 (适用于OC对象类型)
3>assign : 适用于非OC对象类型
4>以前的retain改为用strong,其他一切不变
2、指针分2种:
a、强指针:默认情况下,所以指针都是强指针
b、弱指针:__weak(两个下划线加weak)
3、当两端循环引用的时候,解决方案
a、 ARC
一端用strong,一端用weak
b、 非ARC
一端用retain,一端用assign
Person.h
#import<Foundation/Foundation.h>
@classDog;
@interface Person :NSObject
@property (nonatomic,strong)Dog *dog;
@property (nonatomic,strong)NSString *name;
@property (nonatomic,assign)int age;
@end
Person.m
@implementation Person
- (void)dealloc
{
NSLog(@"Person is dealloc");
}
@end
Dog.h
@classPerson;
@interface Dog :NSObject
@property (nonatomic,weak)Person *person;
@end
Dog.m
@implementation Dog
- (void) dealloc
{
NSLog(@"Dog -- dealloc");
}
@end
main.m
#import<Foundation/Foundation.h>
#import"Person.h"
#import"Dog.h"
int main(int argc, constchar * argv[])
{
Person *p = [[Personalloc]init];
Dog *d = [[Dogalloc]init];
p.dog = d;
d.person = p;
return0;
}
void test()
{
Person *p = [[Personalloc]init];
// Person *p2 = p; // 有这个代码,上面的对象就只有在程序结束的时候才会释放
// 创建新对象,释放上面的对象
p = [[Personalloc]init];
// 销毁p对象
// p = nil;
NSLog(@"---");
}
void test2()
{
// __weak Person *p = [[Person alloc] init]; //没有强指针指向,直接被释放,p变成空指针
Person *p = [[Personalloc]init];
// 弱指针 p2
__weakPerson *p2 = p;
// 对象在此处被释放
p =nil;
p2 =nil;
NSLog(@"---");
}
void test3()
{
Person *p = [[Personalloc]init];
Dog *d = [[Dogalloc]init];
p.dog = d;
Dog *d2 = [[Dogalloc]init];
p.dog = d2;
NSLog(@"---");
}