每个对象都有一个隐式的int类型计数器,占4个字节内存。对象诞生(使用alloc或new、copy创建对象)时默认计数为1;每增加一个调用,计数器加1;当计数器为0时,对象销毁,内存回收。
retain 计数器+1
release 计数器-1
retainCount 计数器值
销毁方法:dealloc。如果计数器值为0,则调用此方法销毁对象。
如果重写此方法,则最后须调用[super dealloc];
1.retain和release及retainCount
#import <Foundation/Foundation.h>
@interface Persion : NSObject
@end
@implementation Persion
//当retainCount为0,OC调用此方法回收内存
- (void)dealloc{
NSLog(@"注销");
[super dealloc];
}
@end
void test();
void test1();
int main()
{
test();
return 0;
}
void test1(){
Persion * p = [Persion new]; // [[Persion alloc] init];
NSLog(@"计数器a:%ld", [p retainCount]); // 初始为1
[p retain]; // 计数器+1
NSLog(@"计数器b:%ld", [p retainCount]); // 2
[p release]; // 计数器-1
NSLog(@"计数器c:%ld", [p retainCount]); // 1
[p release]; /** 变成僵尸对象 **/
p = nil; /** 转为空对象 **/
/** OC中没有空指针错误,这一行仅仅是一行无意义的文本 **/
NSLog(@"计数器d:%ld", [p retainCount]);}
void test(){
Persion * p = [Persion new];
NSLog(@"计数器a:%ld", [p retainCount]); // 初始为1
[p retain]; // +1
NSLog(@"计数器b:%ld", [p retainCount]); // 2
[p release]; // -1
NSLog(@"计数器c:%ld", [p retainCount]); // 1
/** retainCount为0,对象变成僵尸对象 **/
[p release]; // -1
/**
野指针错误:EXC_BAD_ACCESS:坏内存、已被回收内存访问错误。
操作僵尸对象报错:EXC_BREAKPOINT,message sent to deallocated instance **/
NSLog(@"计数器d:%ld", [p retainCount]);
}
2.set方法与内存管理
1)汽车
/*
文件:Car.c
项目:dealloc
作者:vigiles
日期:14-5-8
Copyright (c) 2014年 beardap. All rights reserved.
*/
#import <Foundation/Foundation.h>
@interface Car : NSObject
{
NSString * _name;
}
-(void)setName:(NSString * )name;
-(NSString*)name;
@end
--------------------------------------------------------------
/*
文件:Car.m
项目:dealloc
作者:vigiles
日期:14-5-8
Copyright (c) 2014年 beardap. All rights reserved.
*/
#import "Car.h"
@implementation Car
-(void)setName:(NSString * )name{
if (_name != name) {
[_name dealloc];
_name = [name retain];
}
}
-(NSString*)name{
return _name;
}
-(void)dealloc{
[_name release];
[super dealloc];
NSLog(@"Car释放");
}
-(NSString *)description{
return [NSString stringWithFormat:@"%@", _name];
}
@end
2)宠物
/*
文件:Pet.h
项目:dealloc
作者:vigiles
日期:14-5-8
Copyright (c) 2014年 beardap. All rights reserved.
*/
#import <Foundation/Foundation.h>
@interface Pet : NSObject
// 给@propert添加retain参数
@property (retain) NSString * name;
@end
--------------------------------------------------------------
/*
文件:Pet.m
项目:dealloc
作者:vigiles
日期:14-5-8
Copyright (c) 2014年 beardap. All rights reserved.
*/
#import "Pet.h"
@implementation Pet
@synthesize name;
// 因为声明中添加了retain参数
/* 自动实现的set方法会增加引用计数代码。如:
- (void)setName:(NSString *)name{
if (self->name != name) {
[self->name retain];
self->name = name;
}
}*/
- (void)dealloc {
[name release];
NSLog(@"Pet释放了");
[super dealloc];
}
-(NSString *)description{
return [NSString stringWithFormat:@"%@", name];
}
@end
3)人类
/*
文件:Persion.h
项目:dealloc
作者:vigiles
日期:14-5-8
Copyright (c) 2014年 beardap. All rights reserved.
*/
#import <Foundation/Foundation.h>
#import "Car.h"
#import "Pet.h"
typedef struct {
int year;
int month;
int day;
} Date;
@interface Persion : NSObject
{
int _age;
NSString * _name; // 手动管理内存时,OC对象数据类型的属性,务必在dealloc中release
Car * _car; // 当对象dealloc时,属性须release
}
/**
使用@property声明的属性,会自动隐式的创建setter和getter。
@property也可配置参数,有4种:
1.与隐式setter方法中,对象引用相关的参数
1)retain:release旧值;retain新值。适用于OC对象类型。
2)assign:直接赋值;默认。适用于基本数据类型
3)copy
2.生否隐式创建setter
1)readwrite:读写;默认。
2)readonly:只读,仅生成getter
3.多线程管理
1)nonatomic:性能高,建议!非线程安全
2)atomic:性能低;默认。线程安全。某时刻,只能有一个线程进行setter操作
4.setter和getter的名称(别名)
1)setter:修改默认set方法名,这个set方法务必有“:”冒号。因为set方法获取参数需要:分隔,如:
@property(setter = doSet:) int age;
//以后使用不是[Obj setAge]; 而是[Obj doSet:199393];
2)getter:修改默认get方法名,如:
@property(getter = doGet) int hight;
//以后使用不是[Obj hight]; 而是[Obj doGet];
**/
// OC对象数据,管理引用,非线程安全
@property (retain, nonatomic) Pet * pet; // 宠物。当对象dealloc时,属性须release
// 非对象数据,直接赋值,非线程安全,set方法别名,get方法别名
@property (assign, nonatomic, setter = borning:, getter = borned) Date birthday;
-(void)setAge:(int)age;
-(int)age;
-(void)setName:(NSString *)name;
-(NSString *)name;
-(void)setCar:(Car *)car;
-(Car *)car;
@end
-------------------------------------------------------------------------
/*
文件:Persion.m
项目:dealloc
作者:vigiles
日期:14-5-8
Copyright (c) 2014年 beardap. All rights reserved.
*/
/**
手动管理内存条例:
1.只要调用了alloc,必须配对release(或autorelease)
**/
#import "Persion.h"
#import "Car.h"
@implementation Persion
/** 手动声明的set、get方法,须手动实现 **/
-(void)setAge:(int)age{
_age = age;
}
-(int)age{
return _age;
}
// set方法操作OC对象时须要手动管理内存
-(void)setName:(NSString *)name{
NSLog(@"setName一次");
// 如果传人的新对象和现有的原对象不是同一个,则更新
if (_name != name) {
// 原对象减少引用
[_name release];
NSLog(@"_name释放一次");
// 新对象增加引用
_name = [name retain];
NSLog(@"_name更新一次");
}
}
-(NSString *)name{
return _name;
}
-(void)setCar:(Car *)car{
if (_car != car) {
[_car release];
_car = [car retain];
}
}
-(Car *)car{
return _car;
}
/** 根据@property的参数隐式的自动实现set、get方法 **/
@synthesize pet; //既然指定了,那就使用这个没有下划线的属性
//@synthesize birthday; //没有这一句,默认操作的属性是_birthday,带下划线。
/** 当前对象的引用为0,销毁时 **/
-(void)dealloc{
// 1)对self拥有的全部对象属性做一次release
[_name release];
[_car release];
[pet release];
// 2)一定要最后[super dealloc];
[super dealloc];
NSLog(@"Persion回收了");
}
-(NSString *)description{
return [NSString stringWithFormat:@"姓名:%@,年龄:%d,汽车:%@,宠物:%@,生日:%d", _name, _age, _car, pet, _birthday.year];
}
@end
4)main
/**
手动管理内存条例:
1.只要调用了alloc,必须配对release(或autorelease)
没有alloc,就没有release
2.set
1)基本数据类型无需release
-(void)setAge:(int)age{
_age = age;
}
2)OC对象类型
-(void)setCar:(Car *)car{
(1)须首先判断是否传人的新对象
if(car != _car){
(2)把就对象release
[_car release];
(3)对新对象retain
_car = [car retain];
}
}
3.dealloc
-(void)dealloc{
1)对self拥有的全部对象属性做一次release
[_car release];
2)一定要最后[super dealloc];
[super dealloc];
}
**/
#import <Foundation/Foundation.h>
#import "Persion.h"
#import "Car.h"
int main()
{
// 只要调用了alloc,必须配对release(或autorelease)
Persion * p = [[Persion alloc] init];
Date bir = {1987, 3, 1};
//p.birthday = bir;
[p borning:bir];
p.name = @"Eminem"; // 特殊情况,尽管是set方法,这个格式创建的NSString对象自动release
NSString * name = @"Alizee";
p.name = name; // 没有alloc ,就没有release
p.name = @"Alizee";
// 只要调用了alloc,必须配对release
Car * c = [[Car alloc] init];
c.name = @"甲壳虫";
p.car = c;
//p.car = [[Car alloc] init]; //内存不能释放
Pet * pet = [[Pet alloc] init];
pet.name = @"加菲";
p.pet = pet;
NSLog(@"%@", [p description]);
[pet release];
[c release];
[p release]; // Persion回收了
return 0;
}
3.@class与对象的双向引用
1)Persion
/*
文件:Persion.h
项目:dealloc
作者:vigiles
日期:14-5-8
Copyright (c) 2014年 beardap. All rights reserved.
*/
#import <Foundation/Foundation.h>
/** 这里仅说明有一个Car类,没有引入 **/
@class Car;
@interface Persion : NSObject
@property(nonatomic, retain)NSString * name;
// 有属性-汽车
// 这里声明引用其它类的属性为 retain,那么对方的引用属性就为 assign
@property(nonatomic, retain)Car * car;
@end
---------------------------------------------------------------------------
/*
文件:Persion.m
项目:dealloc
作者:vigiles
日期:14-5-8
Copyright (c) 2014年 beardap. All rights reserved.
*/
#import "Persion.h"
/** 在这里引入(包含)属性所属类的头文件 **/
#import "Car.h"
@implementation Persion
-(void)dealloc{
// 在.h里配置属性为 retain,所以需要release
[_car release];
[super dealloc];
NSLog(@"Persion释放");
}
@end
2)Car
/*
文件:Car.h
项目:dealloc
作者:vigiles
日期:14-5-8
Copyright (c) 2014年 beardap. All rights reserved.
*/
#import <Foundation/Foundation.h>
/** 这里仅说明有一个Persion类,没有引入 **/
@class Persion;
@interface Car : NSObject
@property (nonatomic, retain) NSString * name;
// 有属性-人类
/** 一方配置retain,则另一方配置 assign ! **/
@property (nonatomic, assign) Persion * persion;
@end
---------------------------------------------------------------------
/*
文件:Car.m
项目:dealloc
作者:vigiles
日期:14-5-8
Copyright (c) 2014年 beardap. All rights reserved.
*/
#import "Car.h"
/** 在这里引入(包含)属性所属类的头文件 **/
#import "Persion.h"
@implementation Car
-(void)dealloc{
/** 因为这个属性在.h中声明为assign,不release **/
//[_persion release];
[super dealloc];
NSLog(@"Car释放");
}
@end
3)main
#import <Foundation/Foundation.h>
#import "Persion.h"
#import "Car.h"
int main()
{
Persion * p = [[Persion alloc] init];
Car * c = [[Car alloc] init];
p.car = c; // 配置双向关联
c.persion = p; // 配置双向关联
[c release];
[p release];
return 0;
}
4.autorelease
1)Person
/*
文件:Person.h
项目:dealloc
作者:vigiles
日期:14-5-8
Copyright (c) 2014年 beardap. All rights reserved.
*/
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property(nonatomic, assign) int age;
+(id)person;
+(id)personWithAge:(int)age;
@end
-----------------------------------------------------------------------
/*
文件:Persion.m
项目:dealloc
作者:vigiles
日期:14-5-8
Copyright (c) 2014年 beardap. All rights reserved.
*/
#import "Person.h"
@implementation Person
// 没有使用@synthesize指定属性,所以隐式操作_age对应age。
// 仿Java创建对象的方式
+(id)person{
// 对象只能autorelease一次。alloc必须对应一个release或autorelease
// 这一句,无论是Person还是子类,都返回Person对象
//return [[[Person alloc] init] autorelease];
// 如果是Person调用,返回Person对象;如果是Student调用,返回Student对象
return [[[self alloc] init] autorelease]; // self 表示调用这个方法的类
}
// 仿Java重载构造方法。创建对象时初始化一个属性
+(id)personWithAge:(int)age{
//Person * p = [Person person];
Person * p = [self person];
p.age = age;
return p;
}
-(void)dealloc{
NSLog(@"Person,年龄:%d,注销", _age);
[super dealloc];
}
@end
2)Student
/*
文件:Student.h
项目:dealloc
作者:vigiles
日期:14-5-9
Copyright (c) 2014年 beardap. All rights reserved.
*/
#import "Person.h"
@interface Student : Person
@property (nonatomic, assign) int height;
@property (nonatomic, retain) NSString * name;
@end
---------------------------------------------------------------------------
/*
文件:Student.m
项目:dealloc
作者:vigiles
日期:14-5-9
Copyright (c) 2014年 beardap. All rights reserved.
*/
#import "Student.h"
@implementation Student
-(void)dealloc{
[_name release];
[super dealloc];
NSLog(@"Student,姓名:%@,高:%d,注销", _name, _height);
}
@end
3)main
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Student.h"
int main()
{
// 内存自动释放池
@autoreleasepool {
/** 对象只能autorelease一次 **/
// Person * p = [[[Person alloc] init] autorelease];
// 使用自定义的类方法创建对象
Person * p = [Person person];
p.age = 993;
//这一句不能有
//[p release]; //池内的对象不需要release
/** 自动释放池在栈里,后进先出,可以随意创建,不限数量 **/
@autoreleasepool {
Person * p2 = [Person personWithAge:34];
/** 一些系统自动的类使用时都没有alloc、new、copy。对象都是默认autorelease,不要release **/
NSString * str = [NSString stringWithFormat:@"p2.age:%d", p2.age];
NSLog(@"%@", str);
}
// 继承自Person,继承了仿Java构造方法创建对象
Student * s = [Student person];
/**
当Person中的person方法里,return [[[Person alloc] init] autorelease] 时:
报错:signal SIGABRT。
因为[xxx person]返回的是Person对象,其没有height这个属性 **/
s.height = 200;
// 不用release
Student * s2 = [Student personWithAge:12];
s2.name = @"三好"; //没有报错:signal SIGABRT。
s2.height = 90;
}
return 0;
}
5.ARC自动内存管理机制
编译器编译时,自动根据alloc代码插入release代码。和Java的GC内存回收机制完全不同,GC是在运行阶段自动回收内存。XCode5.1、IOS7默认的。
1)强指针与弱指针
#import <Foundation/Foundation.h>
@interface Person : NSObject
@end
@implementation Person
-(void)dealloc{
NSLog(@"Person,注销");
// ARC禁止手动调用super的dealloc
//[super dealloc];
}
@end
int main()
{
// 因为使用了ARC,不再需要创建自动释放池
// @autoreleasepool{}
//Person * p = [[[Person alloc] init] autorelease]; // ARC禁用autorelease
Person * p = [[Person alloc] init];
/** 只要没有 强指针 指向对象,ARC机制就会释放对象
默认情况,任何指针都是强类型的 __strong
弱类型指针,需加前缀 __weak
**/
p = nil; //添加断点
NSLog(@"添加断点,测试对象释放"); //添加断点
Person * p2 = [Person new];
__weak Person * p3 = p2;
p2 = nil; // 在这里Person对象已经销毁了,因为对象是否存在只考虑强指针的感受
p3 = nil; // 弱指针很悲催,如果对象已经不存在,弱指针会被自动销毁
//[p retain]; // ARC禁止手动retain
//[p retainCount]; // ARC禁止手动retainCount
//[p release]; // ARC禁止手动release
/** 无意义的 **/
__weak Person * p4 = [Person new]; // 对象创建即销毁。因为没有强指针指向它
return 0;
}
2)@property参数
#import <Foundation/Foundation.h>
@implementation Dog : NSObject
-(void)dealloc{
NSLog(@"Dog,注销");
}
@end
@interface Person : NSObject
// 声明强指针类型的属性。strong:强类型;weak:弱类型。这里没有下划线
// strong、weak仍然是针对OC对象数据
// strong作用就如同以前使用的retain;weak作用如同assign
@property (nonatomic, strong) Dog * dog;
@property (nonatomic, weak) Dog * dog2;
@end
@implementation Person
-(void)dealloc{
NSLog(@"Person,注销");
}
@end
int main()
{
Person * p = [Person new];
Dog * d = [Dog new];
p.dog = d;
// 因为p属性dog的强引用,这一步d不会被回收
d = nil; // 添加断点
NSLog(@"p.dog:%@", p.dog); // p.dog:<Dog: 0x1002003d0>
// Person对象被回收后,Dog对象才被回收
p = nil; // 加断点
// --------------------------------------
Person * p2 = [[Person alloc] init];
Dog * dog2 = [Dog new];
p2.dog2 = dog2; // 弱指针类型的属性
dog2 = nil; // 唯一一个强引用销毁,这里Dog对象就被注销回收了
NSLog(@"p.dog2:%@", p.dog2); // p.dog2:(null)
p2 = nil;
return 0;
}
3)非ARC项目转ARC项目
Edit,Refactor,Convert to Objective-C ARC...
4)ARC项目中配置非ARC代码
项目创建后默认是ARC机制,但有时导入的一些框架是非ARC的,需要配置项目其允许手动管理内存。
5)ARC中对象的双向引用
#import <Foundation/Foundation.h>
@class Dog;
@interface Person : NSObject
/** ARC中的双向引用和非ARC时是一样的,一方声明为strong,另一方就得声明为weak **/
@property (nonatomic, strong) Dog * dog;
@end
@implementation Person
-(void)dealloc{
NSLog(@"Person,注销");
}
@end
@interface Dog : NSObject
/** ARC中的双向引用和非ARC时是一样的,一方声明为strong,另一方就得声明为weak **/
@property (nonatomic, weak) Person * person;
@end
@implementation Dog
-(void)dealloc{
NSLog(@"Dog,注销");
}
@end
int main()
{
Person * p = [Person new];
Dog * d = [Dog new];
p.dog = d;
d.person = p;
return 0;
}
- end