您的位置:首页 > 移动开发 > Objective-C

黑马程序员—Objective-C学习—类和对象的内存管理

2014-11-04 13:34 369 查看
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

管理范围:任何继承了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(@"---");

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  OC学习笔记
相关文章推荐