------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
基本原理
1 什么是内存管理
移动设备的内存及其有限,每个app所能占用的内存是有限制的
当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不需要使用的内存空间。比如回收一些不需要使用的对象、变量等
管理范围
任何继承了NSObject的对象,对其他基本数据类型(int、char、float、double、struct、enum)无效
2 原理
每个对象内部都保存了⼀一个与之相关联的整数,称为引⽤用计数器
当使⽤用alloc、new或者copy创建⼀一个对象时,对象的引⽤用计数器被设置为1
给对象发送⼀一条retain消息,可以使引⽤用计数器值+1
给对象发送⼀一条release消息,可以使引⽤用计数器值-1
当⼀一个对象的引⽤用计数器值为0时,那么它将被销毁,其占⽤用的内存被系统回收,系统也会⾃自动向对象发送⼀一条dealloc消息。⼀一般会重写dealloc⽅方法,在这⾥里释放相关资源。⼀一定不要直接调⽤用dealloc⽅方法
可以给对象发送retainCount消息获得当前的引用计数器值
3 内存管理原则
1.谁创建,谁释放(“谁污染,谁治理”)。如果你通过alloc、new或(mutable)copy来创建⼀一个对象,那么你必须调⽤用release或autorelease。换句话说,不是你创建的,就不⽤用你去释放
2.⼀一般来说,除了alloc、new或copy之外的⽅方法创建的对象都被声明了autorelease
3.谁retain,谁release。只要你调⽤用了retain,⽆无论这个对象是如何⽣生成的,你都要调⽤用release
引用计数器
1.方法的基本使用
1> retain :计数器+1,会返回对象本身
2> release :计数器-1,没有返回值
3> retainCount :获取当前的计数器
4> dealloc
* 当一个对象要被回收的时候,就会调用
* 一定要调用[super dealloc],这句调用要放在最后面
2.概念
1> 僵尸对象 :所占用内存已经被回收的对象,僵尸对象不能再使用
2> 野指针 :指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错(EXC_BAD_ACCESS)
3> 空指针 :没有指向任何东西的指针(存储的东西是nil、NULL、0),给空指针发送消息不会报错
#import
#import "Person.h"
int main()
{
// 1
Person *p = [[Person alloc] init];
//NSUInteger c = [p retainCount];
//NSLog(@"计数器:%ld", c);
// 2 retain方法返回的是对象本身
[p retain];
// 1
[p release];
// 0 野指针:指向僵尸对象(不可用内存)的指针
[p release];
[p retain];
// -[Person setAge:]: message sent to deallocated instance 0x100109a10
// 给已经释放的对象发送了一条-setAge:消息:
p.age = 10;
//[p setAge:10];
// 指针p变成空指针
//p = nil;
// EXC_BAD_ACCESS : 访问了一块坏的内存(已经被回收、已经不可用的内存
// 野指针错误
// OC不存在空指针错误,给空指针发送消息,不报错
[p release];
[p release];
[p release];
[p release];
[nil release];
return 0;
}
#import
@interface Person : NSObject
@property int age;
@end
#import "Person.h"
@implementation Person
// 当一个Person对象被回收的时候,就会自动调用这个方法
- (void)dealloc
{
NSLog(@"Person对象被回收");
// super的dealloc一定要调用,而且放在最后面
[super dealloc];
}
@end
多对象内存管理
1.你想使用(占用)某个对象,就应该让对象的计数器+1(让对象做一次retain操作)
2.你不想再使用(占用)某个对象,就应该让对象的计数器-1(让对象做一次release)
3.谁retain,谁release
4.谁alloc,谁release
示例:
#import
#import "Person.h"
#import "Book.h"
int main()
{
// b-1
Book *b = [[Book alloc] init];
// p-1
Person *p1 = [[Person alloc] init];
//p1想占用b这本书
// b-2
[p1 setBook:b];
// p-0
// b-1
[p1 release];
p1 = nil;
// b-0
[b release];
b = nil;
return 0;
}
#import
@interface Book : NSObject
{
int _price;
}
- (void)setPrice:(int)price;
- (int)price;
@end
#import "Book.h"
@implementation Book
- (void)setPrice:(int)price
{
_price = price;
}
- (int)price
{
return _price;
}
- (void)dealloc
{
NSLog(@"Book对象被回收");
[super dealloc];
}
@end
#import
#import "Book.h"
@interface Person : NSObject
{
Book *_book;
}
- (void)setBook:(Book *)book;
- (Book *)book;
@end
#import "Person.h"
@implementation Person
- (void)setBook:(Book *)book
{
_book = [book retain];
}
- (Book *)book
{
return _book;
}
- (void)dealloc
{
[_book release];
NSLog(@"Person对象被回收");
[super dealloc];
}
@end
set方法的内存管理
set方法的代码规范
1> 基本数据类型:直接复制
- (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];
}
}
#import
#import "Car.h"
#import "Person.h"
#import "Student.h"
#import "Dog.h"
int main()
{
// stu - 1
Student *stu = [[Student alloc] init];
// Car - 2
// 这行内存有内存泄露
//stu.car = [[Car alloc] init];
// stu - 0
// Car - 1
[stu release];
// 这行内存有内存泄露
// [[Car alloc] init].speed = 100;
return 0;
}
void test3()
{
Student *stu = [[Student alloc] init];
Car *c = [[Car alloc] init];
stu.car = c;
Dog *d = [[Dog alloc] init];
stu.dog = d;
NSString *s = @"Jack";
stu.name = s;
[d release];
[c release];
[stu release];
}
void test2()
{
Person *p1 = [[Person alloc] init];
p1.age = 20;
// c1 - 1
Car *c1 = [[Car alloc] init];
c1.speed = 100;
// c1 - 2
p1.car = c1;
// c1 - 1
[c1 release];
Car *c2 = [[Car alloc] init];
c2.speed = 200;
// c1 - 0
p1.car = c2;
[c2 release];
[p1 release];
}
void test1()
{
// p-1
Person *p = [[Person alloc] init];
p.age = 20;
// c1-1
Car *c1 = [[Car alloc] init];
c1.speed = 250;
// c1-2
p.car = c1;
// c1-1
[c1 release];
p.car = c1;
p.car = c1;
p.car = c1;
p.car = c1;
p.car = c1;
p.car = c1;
p.car = c1;
[p release];
}
void test()
{
// p-1
Person *p = [[Person alloc] init];
p.age = 20;
// c1-1
Car *c1 = [[Car alloc] init];
c1.speed = 250;
// p想拥有c1
// c1-2
p.car = c1; // [p setCar:c1];
// c2-1
Car *c2 = [[Car alloc] init];
c2.speed = 300;
// p将车换成了c2
// c1-1
// c2-2
p.car = c2;
// c2-1
[c2 release];
// c1-0
[c1 release];
// p-0 c2-0
[p release];
}
#import
#import "Car.h"
@interface Person : NSObject
{
Car *_car;
int _age;
}
- (void)setAge:(int)age;
- (int)age;
- (void)setCar:(Car *)car;
- (Car *)car;
@end
#import "Person.h"
// _car -> c1 0
@implementation Person
- (void)setCar:(Car *)car
{
if (car != _car)
{
// 对当前正在使用的车(旧车)做一次release
[_car release];
// 对新车做一次retain操作
_car = [car retain];
}
}
- (Car *)car
{
return _car;
}
- (void)setAge:(int)age
{ // 基本数据类型不需要管理内存
_age = age;
}
- (int)age
{
return _age;
}
- (void)dealloc
{
// 当人不在了,代表不用车了
// 对车做一次release操作
[_car release];
NSLog(@"%d岁的Person对象被回收了", _age);
[super dealloc];
}
@end
#import
@interface Car : NSObject
{
int _speed;
}
- (void)setSpeed:(int)speed;
- (int)speed;
@end
#import "Car.h"
@implementation Car
- (void)setSpeed:(int)speed
{
_speed = speed;
}
- (int)speed
{
return _speed;
}
- (void)dealloc
{
/*
_speed :直接访问成员变量
self->_speed :直接访问成员变量
self.speed : get方法
[self speed] : get方法
*/
NSLog(@"速度为%d的Car对象被回收了", _speed);
[super dealloc];
}
@end
#import
#import "Car.h"
#import "Dog.h"
@interface Student : NSObject
{
int _no;
NSString *_name;
Car *_car;
Dog *_dog;
}
- (void)setNo:(int)no;
- (int)no;
- (void)setName:(NSString *)name;
- (NSString *)name;
- (void)setCar:(Car *)car;
- (Car *)car;
- (void)setDog:(Dog *)dog;
- (Dog *)dog;
@end
#import "Student.h"
@implementation Student
- (void)setNo:(int)no
{
_no = no;
}
- (int)no
{
return _no;
}
- (void)setName:(NSString *)name
{
if ( name != _name )
{
[_name release];
_name = [name retain];
}
}
- (NSString *)name
{
return _name;
}
- (void)setCar:(Car *)car
{
if ( car != _car )
{
[_car release];
_car = [car retain];
}
}
- (Car *)car
{
return _car;
}
- (void)setDog:(Dog *)dog
{
if ( dog != _dog )
{
[_dog release];
_dog = [dog retain];
}
}
- (Dog *)dog
{
return _dog;
}
- (void)dealloc
{
[_name release];
[_car release];
[_dog release];
[super dealloc];
}
@end
#import
@interface Dog : NSObject
@end
#import "Dog.h"
@implementation Dog
@end
模型设计示例:
#import
#import "User.h"
#import "Status.h"
int main()
{
// 新建2个用户
User *u = [[User alloc] init];
u.name = @"2B";
User *u2 = [[User alloc] init];
u2.name = @"傻B";
// 新建2条微博
Status *s = [[Status alloc] init];
s.text = @"今天天气真好!";
s.user = u;
Status *s2 = [[Status alloc] init];
s2.text = @"今天天气真的很好!";
s2.retweetStatus = s;
s2.user = u2;
[u2 release];
[u release];
[s2 release];
[s release];
return 0;
}
#import
typedef enum {
SexMan, // 男
SexWoman // 女
} Sex;
typedef struct {
int year;
int month;
int day;
} Date;
// 姓名、微博号码、密码、头像、性别、手机、生日
@interface User : NSObject
@property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) NSString *account;
@property (nonatomic, retain) NSString *password;
// http://weibo.com/a.png URL
@property (nonatomic, retain) NSString *icon;
@property (nonatomic, assign) Sex sex;
@property (nonatomic, retain) NSString *phone;
@property (nonatomic, assign) Date birthday;
@end
#import "User.h"
@implementation User
- (void)dealloc
{
[_name release];
[_account release];
[_icon release];
[_password release];
[_phone release];
[super dealloc];
}
@end
#import
#import "User.h"
// 微博内容、微博配图、发送时间、微博发送人、转发的微博、被评论数、被转发数
@interface Status : NSObject
@property (nonatomic, retain) NSString *text;
@property (nonatomic, retain) NSString *icon;
// 从1970-01-01 00:00:00 开始,一共度过了多少毫秒
@property (nonatomic, assign) long time;
//@property (nonatomic) time_t time;
@property (nonatomic, retain) User *user;
@property (nonatomic, retain) Status *retweetStatus;
@property (nonatomic, assign) int commentsCount;
@property (nonatomic, assign) int retweetsCount;
@end
#import "Status.h"
@implementation Status
- (void)dealloc
{
[_text release];
[_user release];
[_retweetStatus release];
[_icon release];
[super dealloc];
}
@end
循环引用
使用场景
分析下⾯面⼀一种情景:(假设都是retain引⽤用)
对象A引⽤用了对象B,对象B引⽤用了对象C,对象C引⽤用了对象B.
这时候B和C的引⽤用计数分别是2和1.当A不再使⽤用B,调⽤用release释放对B的所有权,因为C还引⽤用了B,所以B的引⽤用计数为1,B不会被释放。B不释放,C的引⽤用计数就是1,C也不会被释放。从此,B和C永远留在内存中
为了打断上⾯面这种循环引⽤用,B和C互相引⽤用时,应该⼀一端使⽤用ratain,另⼀一端使⽤用assign
使用方法
1.@class的作用:仅仅告诉编译器,某个名称是一个类
@class Person; // 仅仅告诉编译器,Person是一个类
2.开发中引用一个类的规范
1> 在.h文件中用@class来声明类
2> 在.m文件中用#import来包含类的所有东西
3.两端循环引用解决方案
1> 一端用retain
2> 一端用assign
示例:
#import
#import "Card.h"
#import "Person.h"
int main()
{
// p - 1
Person *p = [[Person alloc] init];
// c - 1
Card *c = [[Card alloc] init];
// c - 2
p.card = c;
// p - 1
c.person = p;
// c - 1
[c release];
// p - 0 c - 0
[p release];
return 0;
}
#import
#import "Card.h"
// @class仅仅是告诉编译器,Card是一个类
//@class Card;
@interface Person : NSObject
@property (nonatomic, retain) Card *card;
@end
#import "Person.h"
#import "Card.h"
@implementation Person
- (void)dealloc
{
NSLog(@"Person被销毁了");
[_card release];
[super dealloc];
}
@end
#import
@class Person;
@interface Card : NSObject
@property (nonatomic, assign) Person *person;
@end
#import "Card.h"
#import "Person.h"
@implementation Card
- (void)dealloc
{
NSLog(@"Car被销毁了");
// [_person release];
[super dealloc];
}
@end