黑马程序员-IOS-OC基础-内存管理

--------------------------------------  android培训 java培训 ios培训期待与您交流! ---------------------------------
(文中代码都是在关闭ARC的情况下编写的)
1,内存管理的基本原理
回收那些不需要再使用的内存,提高移动设备的运行速度。管理范围:任何继承NSObject的对象,对其他数据类型(int、char、float、double、struct、enum等)无效,也就是说内存管理只对对象有效。(可以理解为内存管理,管理的是堆内存中的对象)
<1>对象的基本结构(对象存储在堆内存中)
每个OC对象都有自己的引用计数器,是一个整数,表示“对象被引用的次数”,既有多少人正在使用这个OC对象;每个OC对象内部专门有4个字节的存储空间来存储金庸计数器。
<2>对象的引用计数器
(1)作用:当使用alloc、new或者copy创建以个新对象的时候,新对象的引用计数器默认值是1;当一个对象的引用计数器值为0时,对象占用的内存会被系统回收,也就是如果对象的计数器不为0,那么在整个程序运行过程中,它所占用的内存就不可能被回收,除非是整个程序已经退出。
(2)操作:给对象发送一条retain消息,可以使引用计数器值+1(retain方法返回对象本身);给对象发送一条release消息,可以使引用计数器值-1;如果想获取当前引用计数器的值,可以给对象发送retainCount消息。
<3>对象的销毁
(1)当一个对象的引用计数器值为0时,它就会被销毁,起占用的内存就会被系统回收;
(2)当对象被销毁是,系统会自动向对象发送一条dealloc消息;一般会重写dealloc方法,在对象被销毁时释放相关资源,dealloc就像对象的遗言(重写dealloc方法可以实现对象的监听); 一旦重写dealloc方法,就必须调用[super dealloc],并且放在最后面调用。
#import "Person.h"

@implementation Person
// 当一个Person对象被回收的时候,就会自动调用这个方法
- (void)dealloc
{
    //重写dealloc方法后的输出,可以实现对象的监听
    NSLog(@"Person被回收了");
    
    //这里必须调用[super dealloc]且只能放在最后
    [super dealloc];
}
@end
(3) dealloc方法不要直接调用,会引发野指针错误。一旦对象被回收,它所占用的内存就不再可用,坚持使用会导致程序崩溃(野指针错误)。
/*
1.方法的基本使用
1> retain :计数器+1,会返回对象本身
2> release :计数器-1,没有返回值
3> retainCount :获取当前的计数器
4> dealloc
* 当一个对象要被回收的时候,就会调用
* 一定要调用[super dealloc],这句调用要放在最后面

2.概念
1> 僵尸对象 :所占用内存已经被回收的对象,僵尸对象不能再使用
2> 野指针 :指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错(EXC_BAD_ACCESS)
3> 空指针 :没有指向任何东西的指针(存储的东西是nil、NULL、0),给空指针发送消息不会报错
*/
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[])
{
    Person *p = [[Person alloc] init];
    //获取对象引用计数器的值
    NSUInteger c = [p retainCount];
   
    //[p dealloc];//dealloc方法不要直接掉用,会引发野指针错误
    NSLog(@"%ld",c);//c == 1
    
   
    //使对象引用计数器的值-1
    [p release];
    //[p release];//不能多次调用release,会引发野指针错误。
     p = nil; //清空指针,使p变成空指针,避免出现野指针错误,
    return 0;
}
/*运行结果
 2014-04-10 13:00:05.815 7-引用计数器的操作[818:303] 1
 2014-04-10 13:00:05.818 7-引用计数器的操作[818:303] Person被回收了
 */
2,Xcode的设置
<1>取消ARC的方法

<2>开启僵尸对象监控




3,内存管理的原则
<1>谁创建,谁release:   如果你通过alloc、new或[mutable]copy来创建一个对象,那么你必须调用release或autorelease; 换句话说,不是你创建的,就不用你去[auto]release
<2>谁retain,谁release: 只要你调用了retain,无论这个对象是如何生成的,你都要调用release

<3>总结:有始有终,曾经让对象的计数器+1,就必须在最后让对象的计数器-1。

/*
 1.你想使用(占用)某个对象,就应该让对象的计数器+1(让对象做一次retain操作)
 2.你不想再使用(占用)某个对象,就应该让对象的计数器-1(让对象做一次release)
 3.谁retain,谁release
 4.谁alloc,谁release
 */
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Book.h"
int main(int argc, const char * argv[])
{
    //创建Book的对象,对象的计数器默认值是1
    Book *b = [[Book alloc] init];
    
    //创建Person的对象,对象的计数器默认值是1
    Person *p = [[Person alloc] init];
    
    //p拥有b,在setBook方法内部对b计数器做+1操作
    [p setBook:b];
    
    //b--> -1
    [b release];
    b = nil;//清空指针b
    
    //p--> -1
    [p release];//调用Person的dealloc方法是做  b--> -1
    p = nil;//清空指针p
    
    return 0;
}
/*运行结果是
 2014-04-10 14:50:22.329 8-内存管理的原则[576:303] Book被释放了
 2014-04-10 14:50:22.331 8-内存管理的原则[576:303] Person被释放了
 */
#import "Person.h"
@implementation Person
- (void)setBook:(Book *)book
{
    //把传进来的book对象的引用计数器值+1,想要拥有一个对象就要对对象的引用计数器的值做+1操作。
    _book = [book retain];
}
- (Book *)book
{
    return _book;
}
//当Person对象被销毁时,调用dealloc方法
- (void)dealloc
{
    //对book对象的引用计数器的值做-1操作
    [_book release];
    NSLog(@"Person被释放了");
    [super dealloc];
}
@end

4,set方法的内存管理
<1>set方法的实现
- (void)setBook:(Book *)book
{
    //判断新的book和之前的是否为同一个对象
    if (book != _book)
    {
        //如果不是,则对旧的对象做release操作,使就对象的计数器-1
        [_book release];
    //把传进来的book对象的引用计数器值+1,想要拥有一个对象就要对对象的引用计数器的值做+1操作。
    _book = [book retain];
        
    }
}
//对应的dealloc方法的实现
- (void)dealloc {
	[_book release];
	[super dealloc];
}

5,@property参数
<1>控制set方法的内存管理

#import <Foundation/Foundation.h>
#import "Book.h"
@interface Person : NSObject
//retain: release旧值,retain新值(用于OC对象)
@property (retain) Book *book;

//assign: 直接赋值,不做任何内存管理(默认,用于非OC对象类型)
@property (assign) int age;

//copy: release旧值,copy新值(一般用于NSString *)
@property (retain) NSString *name;
@end

<2>,控制需不需要生成set方法
//readwrite: 同时生成weigth的set方法和get方法(默认)
@property (readwrite) double weigth;

//readonly: 只会生成no的get方法
@property (readonly) int no;
<3>,多线程管理
//nonatomic : 性能高 (一般就用这个)
@property (nonatomic) int age;
//atomic : 性能低(默认的)
@property (nonatomic) int no;
<4>,控制set方法和get方法的名称
//setter : 决定了set方法的名称,一定要有个冒号 :
@property (setter = goob:) NSString *name;//可以通过[p good:@"jim"]来设置name
// getter : 决定了get方法的名称(一般用在BOOL类型),返回BOOL类型的方法名一般以is开头
@property (getter = isRich) BOOL rich;//可以通过[p isRich]来获取rich的值

6,循环引用
<1>@class
(1)使用场景:对于循环依赖关系来说,比如A类引用B类,同时B类也引用A类。当使用@class在两类相互声明,就不会出现编译报错。

使用 @class 类名; 就可以引用一个类,说明一下它是一个类,@class的作用:仅仅告诉编译器,某个名称是一个类。

//这种写法会报错
/*#import "B.h"
@interface A : NSObject
{
    B *b;
}
@end
#import "A.h"
@interface B : NSObject
{
    A *a;
}
@end*/
//正确写法
@class B;//@class B 仅仅是告诉编译器B是一个类
@interface A : NSObject
{
    B *b;
}
@end

@class A;//@class A 仅仅是告诉编译器A是一个类
@interface B : NSObject
{
    A *a;
}
@end

(2)开发中引用一个类的规范

 1> .h文件中用@class来声明类

 2> .m文件中用#import来包含类的所有东西

<2>@class和#import的区别

(1) #import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器在A.h文件中 B *b 只是类的声明,具体这个类里有什么信息,这里不需要知道,等实现文件中真正要用到时,才会真正去查看B类中信息。

(2)如果有上百个头文件都#import了同一个文件,或者这些文件依次被#improt,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍,这样的效率也是可想而知的,而相对来讲,使用@class方式就不会出现这种问题了。

(3)在.m实现文件中,如果需要引用到被引用类的实体变量或者方法时,还需要使用#import方式引入被引用类

<3>循环retain

如果a对象retain了b对象,b对象也retain了对象,这样会导致a对象和b对象永远都无法释放。

解决办法:当两端相互引用时,一端用retain,一端用assign。

//Card类的声明
@class Person;
@interface Card : NSObject
@property (nonatomic, assign) Person *person;
@end
//Person类的声明
@class Card;
@interface Person : NSObject
@property (nonatomic, retain) Card *card;
@end

7,autorelease
<1>autorelease的基本用法:
(1)autorelease会将对象方到一个自动释放池(autoreleasepool)中;
(2)当自动释放池被销毁时,会对池子里面的所有对象做一次release操作;
(3)调用autorelease会返回对象本身;
(4)调用完autorelease方法后,对象的计数器不变。
<2>autorelease的好处:
(1)不用关心对象释放的时间;
(2)不用关心什么时候调用release。
<3>autorelease的使用注意:
(1)占用内存较大的对象不要随便使用autorelease
(2)占用内存较小的对象使用autorelease,没有太大的影响。
<4>自动释放池的创建
(1)ios5.0后
@autoreleasepool
{
    // ....
}
(2)ios5.0前
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// .....
[pool release]; // 或[pool drain];
注意点:在程序运行过程中,可以创建多个自动释放池,他们是以栈的形式存在内存中;OC对象值需要发送一条autorelease消息,就会把这个对象添加到最近的自动释放池中(栈顶的释放池)
<5>autorelease的错误写法
(1) 调用了 autorelease ,又调用 release
@autoreleasepool
 {
    // 1
    Person *p = [[[Person alloc] init] autorelease];
 
    // 0
    [p release];
 }
(2)连续调用多次 autorelease
@autoreleasepool
 {
    Person *p = [[[[Person alloc] init] autorelease] autorelease];
 }
<6>使用规律
(1) 系统自带的方法里面没有包含 alloc new copy ,说明返回的对象都是被声明了 autorelease 的,不需要再release。
例如:
NSNumber *n = [NSNumber numberWithInt:100];
NSString *s = [NSString stringWithFormat:@"jack"];
NSString *s2 = @"rose";
(2) 开发中经常会提供一些类方法,快速创建一个已经 autorelease 过的对象
// 创建对象时不要直接用类名,一般用self
 + (id)person
 {
    return [[[self alloc] init] autorelease];
 }









-------------------------------------- android培训java培训ios培训期待与您交流! ---------------------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值