终于将最难的内存管理看完了,真心觉得内存管理学起来比较抽象,其实内存管理说难也不是很难,只有一个retain和release,只要能够正确的判断什么时候应该retain,什么时候应该release那么就说明已经掌握内存管理的机制了。但是这也是最难的就是如何判断对象什么时候应该release什么时候应该retain。
MJ老师说的一般看见alloc就必须在后面加上一个release,而且操作都要写在relese前面,防止一个很神奇的错误,就是野指针错误。
说到这里又需要讲一下什么是野指针错误。其实就是一个指针指向了一个被释放的回收的对象。那个对象被称为僵尸对象,而且僵尸对象一旦称为僵尸之后就不能再复活了。
不能说你先relese将对象回收完后对象已经变为僵尸对象了你再一个retain对象又可以重新复活,这明显是不行的。
还有每个对象里面都有一个计数器,当计数器为0的时候对象就会被回收,而且计数器是用4个字节来存放的。
方法的基本使用
retain:计数器+1,会返回对象本身
release:计数器-1,没有返回值
retainCount:获取当前的计数器
dealloc
当一个对象要被回收的时候,就会调用
一定要调用[super dealloc],这句调用要放在最后面
概念
僵尸对象 :所占用内存已经被回收的对象,僵尸对象不能再使用
野指针 :指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错(EXC_BAD_ACCESS)
空指针 :没有指向任何东西的指针(存储的东西是nil、NULL、0),给空指针发送消息不会报错
#import <Foundation/Foundation.h>
#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;
}
当出现一个对象需要使用另外一个对象的时候,那么就要在这个对象中重写他的delloc方法。
因为当你自己这个对象被销毁回收之后,你使用或者调用的对象如果没被释放那么就会出现野指针错误,因此在delloc方法中就要将调用的对象release下才行
你想使用(占用)某个对象,就应该让对象的计数器+1(让对象做一次retain操作)
你不想再使用(占用)某个对象,就应该让对象的计数器-1(让对象做一次release)
谁retain,谁release
谁alloc,谁release
还有在调用对象的set方法的时候当传入的参数是一个类的时候就需要将当期使用的类release然后将传入的类retain,因为retain方法功能是将计数器加一并且还有一个返回值就是放回对象本身,所以可以用当前对象来接受
/*内存管理代码规范:
1.只要调用了alloc,必须有release(autorelease)
对象不是通过alloc产生的,就不需要release*/
//2.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];
}
}
//3.dealloc方法的代码规范
//1> 一定要[super dealloc],而且放到最后面
//2> 对self(当前)所拥有的其他对象做一次release
- (void)dealloc
{
[_car release];
[super dealloc];
}
记得之前在讲set,get方法的时候,讲了一个关键字@property 数据类型 类型名
已这种方式来写的xcode的就会自动为我们生成一个set和get方法
现在要遵循oc的内存管理机制,每次在使用alloc的时候都需要release这样写就很麻烦。
所以我们还是可以用@property这个关键字来对变量名进行声明,但是需要在@property后面加上两个参数 (nonatomic,retain)起中nonatomic是起到一个优化的作用mj老师说的必须加,然后retain参数就是实现一个生成的set方法里面,release旧值,retain新值。
但是在implementation中还是需要重写delloc方法,用来release调用过的对象
Person.h
#import <Foundation/Foundation.h>
#import "Book.h"
@interface Person : NSObject
@property int age;
// retain : 生成的set方法里面,release旧值,retain新值
@property (nonatomic,retain) Book *book;
@property (nonatomic,retain) NSString *name;
@end
Person.m
#import "Person.h"
@implementation Person
- (void)dealloc
{
[_book release];
[_name release];
[super dealloc];
}
@end
set方法内存管理相关的参数
retain : release旧值,retain新值(适用于OC对象类型)
assign : 直接赋值(默认,适用于非OC对象类型)
copy : release旧值,copy新值
是否要生成set方法
readwrite :同时生成setter和getter的声明、实现(默认)
readonly :只会生成getter的声明、实现
多线程管理
nonatomic :性能高 (一般就用这个)
atomic :性能低(默认)
setter和getter方法的名称
setter :决定了set方法的名称,一定要有个冒号 :
getter :决定了get方法的名称(一般用在BOOL类型)
循环引用
循环引用就是两个对象相互调用
such as:学生有一个学号,学号也对应一个学生。那么在对象的相互调用的过程中,如果在声明变量时还是用@property(nonatomic,retain)的话,相互调用那么内存永远不会被释放。
那么解决这个循环引用的方法就是一段用retain一段用assign。
@class的作用:仅仅告诉编译器,某个名称是一个类
@class Person; //仅仅告诉编译器,Person是一个类
开发中引用一个类的规范
在.h文件中用@class来声明类
在.m文件中用#import来包含类的所有东西
两端循环引用解决方案
一端用retain
一端用assign
#import <Foundation/Foundation.h>
#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;
}
Person.h
#import <Foundation/Foundation.h>
#import "Card.h"
// @class仅仅是告诉编译器,Card是一个类
//@class Card;
@interface Person : NSObject
@property (nonatomic, retain) Card *card;
@end
Person.m
#import "Person.h"
#import "Card.h"
@implementation Person
- (void)dealloc
{
NSLog(@"Person被销毁了");
[_card release];
[super dealloc];
}
@end
card.h
#import <Foundation/Foundation.h>
@class Person;
@interface Card : NSObject
@property (nonatomic, assign) Person *person;
@end
最后一个讲的时autorelease
光理解这个单词就能知道这个关键字大概有什么用 自动释放
autorelease其实就是一个类,在IOS 5.0前就是调用的NSAutoreleasePool这个类方法来创建autorelease自动释放池
在IOS 5.0以后就直接使用@autoreleasepool{}中的类需要利用autorelease的就将对象写在里面,在init之后在调用autorelease方法就行了。这样写了之后我们就不用在手动释放内存了,不用在不停的写release retain之类的,大大增加了程序员编写是的效率。
#import <Foundation/Foundation.h>
#import "Person.h"
int main()
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Person *pp = [[[Person alloc] init] autorelease];
[pool release]; // [pool drain];
@autoreleasepool
{
// 1
Person *p = [[[[Person alloc] init] autorelease] autorelease];
// 0
// [p release];
}
return 0;
}
void test()
{
@autoreleasepool
{// { 开始代表创建了释放池
// autorelease方法会返回对象本身
// 调用完autorelease方法后,对象的计数器不变
// autorelease会将对象放到一个自动释放池中
// 当自动释放池被销毁时,会对池子里面的所有对象做一次release操作
Person *p = [[[Person alloc] init] autorelease];
p.age = 10;
@autoreleasepool
{
// 1
Person *p2 = [[[Person alloc] init] autorelease];
p2.age = 10;
}
Person *p3 = [[[Person alloc] init] autorelease];
} // } 结束代表销毁释放池
}
最后就是oc历史性的功能ARC机制。那么后面再说。。 ---------------------- ASP.Net+Unity开发、 .Net培训、期待与您交流! ----------------------