OC06 内存管理学习


主题1: 为什么管理内存
1.管理内存,管不好,会导致内存泄露,导致一运行就报错
2.一旦程序超出内存,系统会提示占用内存不足
3.1局部变量,函数一结束就自动释放
3.2堆:动态产生的数据;栈:刚定义就有的局部变量的数据
4.对象的内存是放在堆里的;指针放在栈里
5.堆空间是代码手动回收的
6.内存管理一般指的时堆空间的管理
7.栈的数据指向堆里的数据
8.{}代码块;一旦执行完毕,里面的栈数据自动被销毁
9.系统怎么知道什么回收呢?要理解对象的基本结构
10.结构:引用计数器:被人用了多少次;当等于0时,则判断可以回收了。这个计数器,存在对象里,用了4个字节的存储空间

11.对象刚刚诞生时,计数器默认为1.
12.如何控制计数器
13.内存管理
#import<Foundation/Foundation.h>
#import “Person.h”
{
    [p retainCount];//获取计数器的值;
}
14.程序运行时,回收。程序结束,回收
15.在main函数返回之前,怎么验证对象有没有回收?
16.重写-(void)dealloc 打印下
-(void)dealloc //对象在回收之前会自动去调用这个函数
{
    [super dealloc];一定要写这句
}
17.[p retain];//相当于==p;计数器- -
18.[p release];//计数器++
19.有增就有减:有alloc就有release;有retain就有release;想着用,就要想着及时释放它EXC_BAD_ACCESS:坏访问
20.release 过分调用:会报野指针错误(访问了不能用的内存)

21.什么叫野指针错误?指向不可用内存的指针:叫野指针(指向僵尸对象)
22.怎么防止这个错误呢?清空:p=nil;//相当于p=0;
23.清空后 再[p release];//不会报错的,相当于什么也没做,给空指针发送消息不报错
24.就是说,野指针并不能发送消息(调用任何方法)否则报错:message sent to deallocated instance 0x100109a10
25 release后 再 retain(这个方法:返回对象本身) ???同理,不能在发送任何消息;就是说对象一旦死了,就不能再复活
总结:
26.retain 计数器 ++; release 计数器 - -;retainCount: 获取当前的计数器的值;
dealloc:当一个对象被回收的时候,就会调用;重写时要写 [super dealloc];写在最后面
27.僵尸对象:所占用内存已经被回收的对象,不能在使用
28.野指针:指向僵尸对象的指针;发送消息 会报错
29.空指针:没有指向任何东西的指针(null nil 0);发送消息 不报错

主题2: 多对象内存管理

知识点1

1.对象之前存在千丝万缕的关系
如:人对象 书对象 人可以占有书 当两个人同时拥有同一本书时===》 用书的计数器 来记住有多少人在用自己 
当人不想要这本书了 让计数器减掉1
2.要计算多少人和这本书用关系 用这本书的计数器来标识
3.对对象的使用,有增就要有减 使用对象要负责任

案例分析:QQ堂 人 和 房间 的关系

1.人用一次房间A 房间 计数+1 当人离开 房间计数-1
2.同一个人只能占用一个房间 :A房 转房间B 房间B +1 房间 A-1
3.原则:
    1)只要还有人在用某个对象,那么这个对象就不会被回收
    2)只要你想用这个对象,就让对象的计数器+1
    3)当你不再使用这个对象时,就让对象的计数器-1
4.谁retain 谁 release (不用的时候)谁创建 谁release 

代码举例:<多个对象的内存管理>

#import<Foundation/Foundation.h>
@interface Person:NSObject
{
    Book * _book;
}
- (void)setBook:(Book *)book;
- (Book *)book;
- (void) dealloc;
@end
@implementation Person
- (void)setBook:(Book *)book//一般情况 直接赋值;
{
    //_book=book;
          _book=[book retain];//返回对象本身 bcount++
}
- (Book *)book
{
    return _book;
}
- (void)dealloc
{
    [_book release];
    NSLog(@“Person对象被回收”);
}
@end
@interface Book:NSObject
{
    int _price;
}
- (void)setPrice:(int)price;
- (int)price;
- (void)dealloc;
@end
@implementation Book
- (void)setPrice:(int)price
{
    _price=price
}
- (int)price
{
    return _price;
}
- (void)dealloc
{
    NSLog(@“Book对象被回收”);
}
@end
int main()
{
    Book *b=[[Book alloc] init];//Book 对象 bcount=1; b指向了它
       Person *p1=[[Person alloc] init];
       //设置人有一本书
    [p1 setBook:b];//bcount++
       //[b release];//bcount- -; 放在这 问题1来了:bcount=0 但是人还在拥有它;
       //解决:人在拥有它时,同让它retain一下 count++,这些动作放在set方法里去做 (1*)好比级联回收
    [b release];//当(1*)解决此句合法;bcount=1;
       b=nil;//清空指向关系

    [p1 release];//人被销毁 释放 其书在人的dealloc 的 release 掉的
    p1=nil;//对象指针不再指向对象了
    return 0;
}

知识点2

1.拥有者挂掉:所以在回收时,也把拥有的对象的回收 重写拥有者的dealloc 否则 拥有者不负责任 
2.创建者 不想要对象了 release  对象指针不想指向对象了 赋值nil
一旦计数器变为0 会自动调用dealloc 
3.当创建一本书 bcount=1;人拥有这本书 bcount=2
    情况1:创建者 release bcount=1;人 release 书也release 下 bcount=0
    情况2:人 release 书也release 下 bcount=1;创建者 release bcount=0;
关键就是让bcount=0;才能彻彻底底把书对象回收
4.原则
    1)你拥有对象一次 让对象retain一下 对应3
    2)你不想拥有时,让release 一下 对应4
    3)就是谁retain 谁release
    4)谁alloc 谁realease

知识点3:set方法内存管理

1.严谨的写法
2.@property 屏蔽内存管理细节 所以先不用它
3.Car对象

代码举例

#import<Foundation/Foundation.h>
@interface Person:NSObject
{
    Car * _car;
    int _age;
}
- (void)setCar:(Car *)car;
- (Car *)car;
- (void)setAge:(int)age;
- (int)age;
- (void) dealloc;
@end
@implementation Person
- (void)setCar:(Car *) car
{
         if(car !=_car)//判断是否是新车(*3)
    {
    //对当前使用的车(旧车)release 一下
          [_car release];//(没车)第一次赋值时,_car是nil 但是[nil release];不报错
    //对新车retain一下(*2)
          _car=[car retain];
    }
}
- (Car *)car
{
    return _car;
}
- (void)setAge:(int)age;//基本数据类型不是对象,不用retain来管理内存
{
    _age=age;
}
- (int)age;
{
    return  _age;
}
- (void)dealloc
{
    [_car release];//当人不在了 代表不用车了 release一下车
    NSLog(@“%d岁的Person对象被回收”,_age);
    [super  dealloc];
}
@end
@interface Car:NSObject
{
    int _price;
}
- (void)setPrice:(int)price;
- (int)price;
- (void)dealloc;
@end
@implementation Car
- (void)setPrice:(int)price
{
    _price=price
}
- (int)price
{
    return _price;
}
- (void)dealloc
{
    NSLog(@“价格为%d对象被回收”,self->_price);
    [super  dealloc];
}
@end
int main()
{

       Person *p=[[Person alloc] init];
    p.age=20;
    Person *p1=[[Person alloc] init];
    p1.age=30;
       //设置人有一车
    Car *c1=[[Car alloc] init];
    c1.price=250;

    [p setCar:c1];

    //p.car=c1;又写了一次 问题3来了:(重复赋值)导致 当前car被撤销 (*3)
    Car *c2=[[Car alloc] init];
    c2.price=250;      
    [p setCar:c2];//换了车 没对原来的车p1 release 解决:(*2)

    [c2release];
       c2=nil;
    [c1 release];
       c1=nil;
    [p release];
    p=nil;
    return 0;
}

知识点4:内存管理代码规范

1.只要调用了alloc 必须有release(autorelease)
2.set方法的代码规范
   _age=age;//直接赋值
3.OC对象类型 包括NSString对象类型
- (void) setCar:(Car *)car//内存管理原则代码
{
    //先判断传进来的是不是新对象 【重复赋值】
    if(_car != car)
    {
        //对旧对象(当前使用的对象)做一次release 【换车】
        [_car release];
        //对新对象做一次 retain 【防止本身被销毁时,还被拥有者占用,所以在占有它的时候增强下它的寿命】
        _car=[car retain];
    }
}
4.dealloc代码规范
    1>一定要调用[super dealloc] ,而且放到最后面
    2>对self(当前)所拥有的其他对象做一次 release
- (void)dealloc
{
    [_car release];//被人拥有
    [super dealloc];
}

知识点5

1*Xcode会自动生成 内存管理原则代码
2*没有用alloc产生,就不需要release
3.@property参数内存管理 
目的:利用Xcode自动生成代码,精简自己需要写的代码量
之前所学:@property Book *book;

生成如下代码:

- (void)setBook:(Book *)book
{
    _book=book;
}
- (Book *)book
{
    return _book;
}
4.获取计数器[b retainCount]; 输出 %ld
现在想生成含有内管管理原则的代码:release 旧值 retain 新值
要这样写:@property (retain)Book* book;
    @property (retain)NSString * name;

会自动生成如下代码

- (void)setBook:(Book *)book
{
    if(book != _book)
    {
           [_book release];
    _book=[book retain];
    }
}
- (Book *)book
{
    return _book;
}

缺点是:dealloc 还是得自己写


主题3: @property参数其他作用

知识点1

1.传内存管理相关参数 @property (参数) 类型 变量名//只能传一个参数
*retain :release 旧的值 retain新的值
*assign: 直接复制 (默认,适用于非OC对象)
*copy:release旧的值 copy新的值
2.传多线程管理相关参数
nonatomic:性能高  不要加锁 (建议使用这个)
atomic:性能低(默认)
@property(nonatomic,assign) int height;//不冲突,可以传了两个参数 合法的
3.setter和getter方法名的名称 不会影响成员变量的名称的
@property (getter=abc) int height;//修改生成的get方法名,get还是按照默认方式生成

注意

1.@property (getter=abc,setter=setAbc:) int height;//修改生成的get方法,名注意set方法后,有个冒号

2.一般用在BOOL类型:因BOOL类型的方法一般是以is开头
@property (getter=isRich) BOOL rich;


3.是否生成set方法

@property (readonly) int height;//只生成get
@property(readwrite,assign) int height;//不冲突,可以传了两个参数 合法的
*readwrite:setter 和 getter 声明和实现
*readonly:只会生成getter的声明和实现

知识点2:循环retain和@class

1.@class 类名 告诉编译器 这是个类 但是不知道这个类的构造是不知道的

2.循环引用 你引用我 我引用你 一般两端都用到 @class 用在头文件里
3.真的要引用 再导入 #import

4.@class 名称 的作用:仅仅告诉编译器 某一个名称是个类 
5.开发中引用一个类的规范:
    1)在.h文件中用@class来声明一个类 好处
    ①解决循环引用的问题
    ②当一个类文件改变了 头文件不要改 因为在头文件里没用#import 导入这个类的头文件
    2)在.m文件中用#import来包含这个类所有东西
6.你retain我 我retain你 两端循环引用怎么解决???p.card=c;c.person=p;
一端用retain 一端用assign
@property(nonatomic,retain)Person * person
@property (nonatomic,assign) Card * card 直接赋值 就用不到内存管理代码 
就是说在Person的dealloc里就不用release了

主题4: autorelease 半自动释放

知识点1:为了解决 释放对象后 还在使用对象的情况 用autorelease

Person *p=[[[Person alloc] int] autorelease]//把Person对象扔到自动释放池里,
//当池子销毁时,会对池子里所有对象做一次release操作
@autoreleasepool//创建了一个自动释放池
{
    Person *p=[[[Person alloc] int] autorelease]/*把Person对象扔到自动释放池里,
    当池子销毁时,会对池子里所有对象做一次release操作*/
}//销毁池子
好处:不用关心代码要写在release之前了(不用关心对象释放的时间)
池子可以嵌套使用
注意:池子对计数器没任何影响
池子是放在栈里 所以销毁过程 符合先进后出的规律
本质:延迟了对象被销毁的时间 但是不能精确控制这个时间
适用:对象很少的时候 小的对象
那么怎么控制的呢???用release

知识点2:autorelease的基本用法

1)会将对象放到一个自动释放池中
2)会返回对象本身
3)调用autorelease方法后,对象的计数器不变
注意:
1)占用内存较大的对象不要随便使用autorelease
2)较小的可以,没有太大影响
常见错误
1.池子里不要再 [p release];//否则有野指针错误
2.alloc 调用了autorelease 又调用rele
3.连续多次调用autorelease
@autorelease
{
    Person *p=[[[Person alloc] int ]autorelease] autorelease];//错误
}

知识点3:对象都是放在栈顶的池子里的

1.自动释放池 ios 5.0开始的
    1*在IOS程序运行过程中,会创在无数个池子,这些池子都是以栈结构存在
    2*在一个对象调用了autorelease方法时,会将这个对象放到栈顶的释放池
    3*闪退和内存挂钩

autorelease的应用:写一个类方法 返回一个autorelease的对象

+ (id) person//1方法名以类名开头
{
    return [[Person alloc] init autorelease];/*[[self alloc] init autorelease];创建对象时不要直接用类名,
    一般要用self*/
}
+ (id) personWithAge:(int) age//2
{
    Person *p= [[Person alloc] init autorelease];
    p.age=age;
}

下次使用的时候(节省编码时间)

@autorelease//1
{
    Person *p=[Person person];
}

@autorelease//2
{
    Person *p=[Person personWithAge:10];
}
NSString *str=@“222222222//这样写自动就有autorelease

方法
规律:系统带的方法里没有包含alloc new copy 说明返回对象都是autore的 就是不要单写release了

int main()
{
    @autorelease{

    }
}

知识点3

1.怎么调错?不能识别发送消息的对象
创建对象时不要直接用类名,一般要用self
2.内存管理小结
1)计数器的基本操作
retain +1 release -1 retainCount 获得计数器
2)set方法的内存管理
set 方法的实现
- (void)setCar : (Car *)car
{
    if(_car !=car)
    {
        [_car release];
        _car =[car retain];
    }
}

dealloc 方法的实现

- (void)dealloc
{
    [_car release];
    [super dealloc];
}
3)@property参数
OC对象
@property(nonatomic,retain) 类型 * 属性名
@property(nonatomic,retain) Car * car
@property(nonatomic,retain) id 属性名
//被retain过的属性,必须在dealloc方法中release属性
- (void)dealloc
{
    [_car release];
    [super release];
}
非OC对象
@property(nonatomic,retain) 基本类型  属性名
@property(nonatomic,retain) int age;
4)autorelease
    1*.系统自带的方法中,如果不包含alloc new copy 不用再写autorelease
    2*.开发中经常写一些方法快速创建一个autorelease对象
*创建对象的时候不要使用类名,一般写self

主题5: ARC原理

知识点1

1.解决retain重复编写 和 dealloc 里重复写release 的代码重复的问题,ARC自动生成这些内容
2.它怎么知道什么情况下+release呢?
3.ARC判断准则:只要没有强指针(就是弱指针存在也不行)指向对象 ,就会释放对象
4.强指针:默认情况下,所有的指针都是强指针
5.弱指针:对象销毁后,指向的它弱指针对自动清空 __weak
__weak Person *p2=[[Person alloc] init]
__strong Person *p=[[Person alloc] init]
6.在ARC中
要这样写
@property (nonatomic,strong)Dog *dog;//_dog 是强指针
7.注意
    1)不允许调用 release、retain、retainCount
    2)允许重写dealloc,但是不允许 调用[super dealloc]
3)@property的参数

* strong:成员变量是强指针(适用于 OC对象类型)
* weak :成员变量是弱指针(适用于对象类型)
* assign:适用于非OC对象


知识点2

1.好处:代码少了,不用关心内存管理
2.把整个项目非ARC改为ARC : Edit→Refactor→Convert ARC
3.怎么看是否ARC 了 点击项目 搜索Auto
4.怎么既存在ARC又存在非ARC:编译器选项 build phases 对编译文件 双击 写:-fno-objc-arc//不要ARC
要ARC : -f-objc-arc//要ARC

知识点3

非 ARC的 循环引用 :你retain我 我retain你 一端用retain 一端用rassign
注意点
类之间 相互拥有的 时候 且都是强指针 ARC的 循环引用:解决:在各自的头文件里:一端用week 一端用strong
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值