# 内存管理简介
##1.内存管理的重要性
- 移动设备的内存极其有限,每个app所能占用的内存是有限制的
- 下列行为都会增加一个app的内存占用
+创建一个OC对象
+定义一个变量
+调用一个函数或者方法
- 当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不需要再使用的内存空间。比如回收一些不需要使用的对象、变量等
- 如果app占用内存过大, 系统可能会强制关闭app,造成闪退现象, 影响用户体验
---
##2.什么是内存管理
-如何回收那些不需要再使用的对象?
+那就得学会OC的内存管理
- 所谓内存管理, 就是对内存进行管理, 涉及的操作有:
+分配内存 : 比如创建一个对象, 会增加内存占用
+清除内存 : 比如销毁一个对象, 能减小内存占用
- 内存管理的管理范围
+任何继承了NSObject的对象
+对其他非对象类型无效(int、char、float、double、struct、enum等 )
- 只有OC对象才需要进行内存管理的本质原因
+OC对象存放于堆里面
+非OC对象一般放在栈里面(栈内存会被系统自动回收)
---
##3.堆和栈
- 栈(操作系统):由操作系统自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈(先进后出);
- 堆(操作系统):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收,分配方式类似于链表。
- 示例:
int main(int argc, const char * argv[])
{
@autoreleasepool {
int a = 10; // 栈
int b = 20; // 栈
// p : 栈
// Person对象(计数器==1) : 堆
Person *p = [[Person alloc] init];
}
// 经过上一行代码后, 栈里面的变量a\b\c都会被回收
// 但是堆里面的Person对象还会留在内存中,因为它是计数器依然是1
return 0;
}
```
![](http://7xj0kx.com1.z0.glb.clouddn.com/dhz.png)
[更多关于堆栈信息](http://baike.baidu.com/link?url=VJz-vKfY_ASVHCB17nWR_uQ4Adhuasl2WOrW4O7ZgFdgq5Tl_kBGfUYzbNSzkSBkHAbAH8qzfDpl5Wm4rxQ1Ka)
# 野指针\空指针
##1.僵尸对象
- 已经被销毁的对象(不能再使用的对象)
##2.野指针
- 指向僵尸对象(不可用内存)的指针
- 给野指针发消息会报EXC_BAD_ACCESS错误
##3.空指针
- 没有指向存储空间的指针(里面存的是nil, 也就是0)
- 给空指针发消息是没有任何反应的
- 为了避免野指针错误的常见办法
+ 在对象被销毁之后, 将指向对象的指针变为空指针
# 内存管理原则
##1.内存管理原则
- 苹果官方规定的内存管理原则
+谁创建谁release :
* 如果你通过alloc、new或[mutable]copy来创建一个对象,那么你必须调用release或autorelease
+谁retain谁release:
* 只要你调用了retain,就必须调用一次release
- 总结一下就是
+有加就有减
+曾经让对象的计数器+1,就必须在最后让对象计数器-1
---
##2.多对象内存管理
- 单个对象的内存管理, 看起来非常简单
- 如果对多个对象进行内存管理, 并且对象之间是有联系的, 那么管理就会变得比较复杂
- 其实, 多个对象的管理思路 跟很多游戏的房间管理差不多
+比如斗地主 \ 劲舞团 \ QQ音速
![](http://pic2.52pk.com/files/120414/534347_001415_6_lit.jpg)
- 总的来说, 有这么几点管理规律
+只要还有人在用某个对象,那么这个对象就不会被回收
+只要你想用这个对象,就让对象的计数器+1
+当你不再使用这个对象时,就让对象的计数器-1
---
##3.set方法内存管理
- (1)retain需要使用的对象
- (2)release之前的对象
- (3)只有传入的对象和之前的不同才需要release和retain
```
- (void)setRoom:(Room *)room
{
// 避免过度释放
if (room != _room)
{
// 对当前正在使用的车(旧车)做一次release
[_room release];
// 对新车做一次retain操作
_room = [room retain];
}
}
```
##4.dealloc方法的内存管理
```
- (void)dealloc
{
// 当人不在了,代表不用房间了
// 对房间做一次release操作
[_roomrelease];
[super dealloc];
}
```
# @Property练习
##1.@Property练习
- 微博类(Status)
+文字内容(text)
+配图(picture)
+发表时间(createTime)
+作者(author)
+转发的说说(repostStatus)
+评论数(commentCount)
+转发数(retweetCount)
+赞数(likeCount)
- 作者类(Author)
+昵称(name)
+头像(icon)
+生日(birthday)
+账号(account)
- 账号(Account)
+账号名称(name)
+账号密码(pwd)
+账号注册时间(registerTime)
```
模拟场景:
* 老王在2010-1-1 17:56:34注册了一个账号
(名称:xiaomage@520it.com,密码:haomage)
* 老王的生日是1986-3-818:18:18
* 老王发布一条说说
* 文字内容 @“爆米花手机比逼格更有逼格”
* 图片 @“phone.png”
* 发表时间: 2015-6-20 10:23:23
* 作者: 老王
* 被转发的说说: 没有
* 评论数: 100
* 转发数: 90
* 点赞数: 200
* 王大锤在2012-8-819:26:54注册了一个账号
(名称:dachuimeimei@520it.com, 密码:654321)
* 王大锤的生日是1989-9-614:16:28
* 王大锤在2015-6-2120:47:09时,转发了张三之前发布的说说,并且还附带了一句话:@“真的很有逼格”
![](http://www.youqudian.com/upload/you/others/2014/07/20140725150032_6gsf04q6.jpg)
---
# @class
##1.@class基本概念
- 作用
+可以简单地引用一个类
- 简单使用
+@class Dog;
+仅仅是告诉编译器:Dog是一个类;并不会包含Dog这个类的所有内容
- 具体使用
+在.h文件中使用@class引用一个类
+在.m文件中使用#import包含这个类的.h文件
---
##2.@class其它应用场景
- 对于循环依赖关系来说,比方A类引用B类,同时B类也引用A类
- 这种嵌套包含的代码编译会报错
```
#import "B.h"
@interface A : NSObject
{
B*_b;
}
@end
#import “A.h"
@interface B : NSObject
{
A*_a;
}
@end
```
- 当使用@class在两个类相互声明,就不会出现编译报错
```
@class B;
@interface A : NSObject
{
B*_b;
}
@end
@class A;
@interface B : NSObject
{
A*_a;
}
@end
```
##3.@class和#import
- 作用上的区别
+#import会包含引用类的所有信息(内容),包括引用类的变量和方法
+@class仅仅是告诉编译器有这么一个类, 具体这个类里有什么信息, 完全不知
- 效率上的区别
+如果有上百个头文件都#import了同一个文件,或者这些文件依次被#import,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍 , 编译效率非常低
+相对来讲,使用@class方式就不会出现这种问题了
---
# dealloc方法
##1.dealloc方法基本概念
- 当一个对象的引用计数器值为0时,这个对象即将被销毁,其占用的内存被系统回收
- 对象即将被销毁时系统会自动给对象发送一条dealloc消息
(因此, 从dealloc方法有没有被调用,就可以判断出对象是否被销毁)
- dealloc方法的重写
+一般会重写dealloc方法,在这里释放相关资源,dealloc就是对象的遗言
+`一旦重写了dealloc方法, 就必须调用[super dealloc],并且放在最后面调用
- 使用注意
+不能直接调用dealloc方法
+一旦对象被回收了, 它占用的内存就不再可用,坚持使用会导致程序崩溃(野指针错误)