---恢复内容开始---
MRC: Manul(手动) Reference(引用) Counting(计数)
什么是手动引用计数?
所有对象的内容都需要我们手动管理, 需要程序员自己编写release/retain等代码
内存管理的原则就是有加就有减
也就是说, 一次alloc/new/copy对应一次release, 一次retain对应一次relese
内存管理的重要性:
移动设备的内存极其有限,每个app的占用内存也是有限制的.
当我们创建一个OC对象,定义一个变量,调用一个函数或者方法,都会增加app的内存占用.
当app内存占用极多的时候,系统会发出内存警告,而这时候就需要回收一些不需要在使用的内存空间,比如"对象""变量";
如果app占用内存极大的时候,会造成程序崩溃,闪退等现象,极大的影响了用户体验
什么是内存管理?
>对内存进行管理
内存管理涉及的操作?
>分配内存:创建对象.增加内存占用
>清楚内存:释放对象,减少内存占用
OC中内存管理的范围?
>任何继承NSObject的对象
>对非对象类型无效
为什么只有OC对象才需要内存管理,本质是什么?
>OC对象存放于堆中
>非OC对象一般存放于栈中(存放于栈中的由系统自动回收)
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;
}

引用计数器
>每个OC对象都有一个计数器
>它是一个整数
>可以理解为"对象被引用的次数"或者"有多少人再用它"
引用计数器的作用?
>系统根据对象的引用计数器判断对象是否需要被回收
>当对象的引用计数器为0时,对象所占用的内存空间就会被系统回收
>如果对象的引用计数器不为0时,那么在整个程序运行期间,对象所占用的内存空间就不会被释放,除非整个程序已经退出.
任何一个对象刚创建出来,期引用计数器为1
>当使用alloc new copy创建对象时,对象的引用计数器默认就为1
引用计数器的常见操作
>给对象发送一条retain消息,可以使引用计数器值+1(retain方法返回对象本身)
>给对象发送一条release消息, 可以使引用计数器值-1
>给对象发送retainCount消息, 可以获得当前的引用计数器值
注意: release并不代表销毁\回收对象, 仅仅是计数器-1
dealloc方法
>当一个对象的引用计数器值为0时,这个对象即将被销毁,其占用的内存被系统回收
>对象即将被销毁时系统会自动给对象发送一条dealloc消息 (因此, 从dealloc方法有没有被调用,就可以判断出对象是否被销毁)
dealloc方法的重写
>一般会重写dealloc方法,在这里释放相关资源,dealloc就是对象的遗言
>一旦重写了dealloc方法, 就必须调用[super dealloc],并且放在最后面调用
注意
>不能直接调用dealloc方法
>一旦对象被回收了, 它占用的内存就不再可用,坚持使用会导致程序崩溃(野指针错误)
野指针/空指针/僵尸对象
野指针
>指向不可用内存(僵尸对象)的的指针,我们称之为野指针
>给野指针发消息会报EXC_BAD_ACCESS错误

空指针
>没有指向存储空间的指针(里面存的是nil, 也就是0)
>给空指针发消息是没有任何反应的
>避免野指针错误的常见办法(在对象被销毁之后, 将指向对象的指针变为空指针)
僵尸对象
>已经被释放的对象,不能在被使用的对象
Xcode打开僵尸对象检控


内存管理原则苹果官方规定的内存管理原则
谁创建谁release :
- 如果你通过alloc、new、copy或mutableCopy来创建一个对象,那么你必须调用release或autorelease
谁retain谁release:
- 只要你调用了retain,就必须调用一次release
总结:
- 有加就有减
- 曾经让对象的计数器+1,就必须在最后让对象计数器-1
多对象内存管理
set方法内存管理- (1)retain需要使用的对象
- (2)release之前的对象
- (3)只有传入的对象和之前的不同才需要release和retain
- (void)setRoom:(Room *)room
{
// 避免过度释放
if (room != _room)
{
// 对当前正在使用的房间(旧房间)做一次release
[_room release];
// 对新房间做一次retain操作
_room = [room retain];
}
}
dealloc方法的内存管理
- (void)dealloc
{
// 当人不在了,代表不用房间了
// 对房间做一次release操作
[_room release];
[super dealloc];
}
多个对象示例:
要求:
/*
微博类(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-8 18:18:18
老王发布一条说说
文字内容 @“爆米花手机比逼格更有逼格”
图片 @“phone.png”
发表时间: 2015-6-20 10:23:23
作者: 老王
被转发的说说: 没有
评论数: 100
转发数: 90
点赞数: 200
王大锤在2012-8-8 19:26:54注册了一个账号
(名称:dachuimeimei@520it.com, 密码:654321)
王大锤的生日是1989-9-6 14:16:28
王大锤在2015-6-21 20:47:09时,转发了张三之前发布的说说, 并且还附带了一句话:@“真的很有逼格”
*/
Account.h
#import <Foundation/Foundation.h>
typedef struct {
int year;
int month;
int day;
int hour;
int minute;
int second;
} MyDate;
@interface Account : NSObject
// 账号名称
@property(nonatomic, retain)NSString *eaml;
// 账号密码
@property(nonatomic, retain)NSString *pwd;
// 账号注册时间
@property(nonatomic, assign)MyDate registerTime;
@end
Account.m
#import "Account.h"
@implementation Account
- (void)dealloc {
NSLog(@"%s", __func__);
self.eaml = nil;
self.pwd = nil;
[super dealloc];
}
@end
Author.h
#import <Foundation/Foundation.h>
#import "Account.h"
@interface Author : NSObject
// 昵称
@property(nonatomic, retain)NSString *name;
// 头像
@property(nonatomic, retain)NSString *icon;
// 生日
@property(nonatomic, assign)MyDate birthday;
// 账号
@property(nonatomic, retain)Account *account;
@end
Author.m
#import "Author.h"
@implementation Author
- (NSString *)description {
return [NSString stringWithFormat:@"\n作者:%@\n", _name];
}
- (void)dealloc {
NSLog(@"%s", __func__);
self.name = nil;
self.icon = nil;
self.account = nil;
[super dealloc];
}
@end
Status.h
#import <Foundation/Foundation.h>
#import "Author.h"
@interface Status : NSObject
// 文字内容
@property(nonatomic, retain)NSString *text;
// 配图
@property(nonatomic, retain)NSString *picture;
// 发表时间
@property(nonatomic, assign)MyDate createTime;
// 作者
@property(nonatomic, retain)Author *author;
// 转发的微博
@property(nonatomic, retain)Status *repostStatus;
// 评论数
@property(nonatomic, assign)int commentCount;
// 转发数
@property(nonatomic, assign)int retweetCount;
// 赞数
@property(nonatomic, assign)int likeCount;
@end
Status.m
#import "Status.h"
@implementation Status
- (NSString *)description {
return [NSString stringWithFormat:@"\n微博内容:%@\n发表时间:%d-%d-%d-%d-%d-%d\n评论:%d 转发:%d 点赞:%d",_text, _createTime.year, _createTime.month, _createTime.day, _createTime.hour, _createTime.minute, _createTime.second, _commentCount, _retweetCount, _likeCount];
}
- (void)dealloc {
NSLog(@"%s", __func__);
self.text = nil;
self.picture = nil;
self.repostStatus = nil;
self.author = nil;
[super dealloc];
}
@end
main.m
#import <Foundation/Foundation.h>
#import "Status.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 老王
// 1.创建账号
Account *lwAccount = [[Account alloc] init];
lwAccount.eaml = @"xiaomage@520it.com";
lwAccount.pwd = @"haomage";
lwAccount.registerTime = (MyDate){2010, 1, 1, 17, 56, 34};
// 2.创建作者
Author *lwAuthor = [[Author alloc] init];
lwAuthor.name = @"老王";
lwAuthor.icon = @"lw.png";
lwAuthor.birthday = (MyDate){1986, 3, 8, 18, 18, 18};
lwAuthor.account = lwAccount;
// 3.发微博
Status *lwStatus = [[Status alloc] init];
lwStatus.text = @"爆米花手机比逼格更有逼格";
lwStatus.picture = @"phone.png";
lwStatus.createTime = (MyDate){2015, 6, 20, 10, 23, 23};
lwStatus.author = lwAuthor;
lwStatus.repostStatus = nil;
lwStatus.commentCount = 100;
lwStatus.retweetCount = 90;
lwStatus.likeCount = 200;
// 王大锤
Account *wdcAccount = [[Account alloc] init];
wdcAccount.eaml = @"dachuimeimei@520it.com";
wdcAccount.pwd = @"654321";
wdcAccount.registerTime = (MyDate){2012, 8, 8, 19, 26, 54};
Author *wdcAuthor = [[Author alloc] init];
wdcAuthor.name = @"王大锤";
wdcAuthor.icon = @"wdc.png";
wdcAuthor.birthday = (MyDate){1989, 9, 6, 14, 16, 28};
wdcAuthor.account = wdcAccount;
Status *wdcStatus = [[Status alloc] init];
wdcStatus.text = @"真的很有逼格";
lwStatus.picture = nil;
wdcStatus.createTime = (MyDate){2015, 6, 21, 20, 47, 9};
wdcStatus.author = lwAuthor;
wdcStatus.repostStatus = lwStatus;
wdcStatus.commentCount = 0;
wdcStatus.retweetCount = 0;
wdcStatus.likeCount = 0;
NSLog(@"%@%@", lwStatus, lwAuthor);
[lwAccount release];
[lwAuthor release];
[lwStatus release];
[wdcAccount release];
[wdcAuthor release];
[wdcStatus release];
}
return 0;
}
@property参数
控制set方法的内存管理
>retain : release旧值,retain新值(用于OC对象)
>assign : 直接赋值,不做任何内存管理(默认,用于非OC对象类型)
>copy : release旧值,copy新值(一般用于NSString *)
控制需不需要生成set方法
>readwrite :同时生成set方法和get方法(默认)
>readonly :只会生成get方法
多线程管理
>atomic :性能低(默认)
>nonatomic :性能高
控制set方法和get方法的名称
>setter : 设置set方法的名称,一定有个冒号:
>getter : 设置get方法的名称
>注意: 不同类型的参数可以组合在一起使用
循环retian基本概念
循环retain的场景
>比如A对象retain了B对象,B对象retain了A对象
循环retain的弊端
>这样会导致A对象和B对象永远无法释放
循环retain的解决方案
>当两端互相引用时,应该一端用retain、一端用assign

本文链接:OC基础 05-手动内存管理(MRC),转载请注明。