Object-c 内存管理
内存管理
主要内容
1.内存管理的概念
2.引用计数
3.如何持有对象所有权
4.自动释放池
5.@property的使用
什么是内存管理
内存管理是关于如何管理对象生命周期的编程原则。
int main(int argc, char *argv[])
{
int value = 100;
Dog *mydog = [[Dog alloc]init];
[dog setNum:@"蓝眼儿"];
return 0
}
|
栈区 value 100 person 0x12345 |
堆区 Dog对象 _name |
oc中的内存管理只针对oc中的对象,所有对象都继承NSObject
基本数据类型不需要管理内存。
当一个对象没有人再使用,该对象应该从内存中销毁掉。
引用计数
所有oc对象都有一个计数器,这个计数器我们称为引用计数。
引用计数表示有几个人在使用当前对象。
|
NSObject对象 retainCount 引用计数 |
每个对象都有一个retainCount引用计数器,表示当前对象被引用的数量。
怎么是引用计数器加减呢?
1.调用retain方法, 计数+1,调用release方法,计数-1
2.当引用计数为0时,说明没有人使用此对象,此对象会被系统销毁。
alloc retain release
alloc用来创建对象,创建完成后,引用计数为1,只调用一次。
retain使引用计数+1,可以调用多次。
release使引用计数-1,可以调用多次。
当引用计数为0时,对象会被系统从内存中销毁,销毁之前,会自动调用此对象的dealloc方法。
retain, release示例
Person *person = [[Person alloc]init];//计数器为1
[person retain];//计数器为2
[person retain];//计数器为3
[person release];//计数器为2
[person release];//计数器为1
[person release];//计数器为0,对象从内存中销毁,调用dealloc方法。
为什么使用引用计数
遛狗的例子
对象所有权
当一个所有者(owner,本身是一个oc对象)做了如下动作,它就拥有了一个对象的所有权(ownership)
alloc retain [mutable]copy
使用如下方法
release autorelease
黄金法则
如果对一个对象使用了alloc,[mutable]copy, retain, 那么你必须使用相应的release或者autorelease释放。
如何持有对象
实例分析:如何让人持有狗的对象所有权
1.set方法持有对象的所有权
-(void)setDog: (Dog *)dog
{
if (_dog!=dog) {
[_dog release];
_dog = [dog retain];
}
}
2.自定义初始化方法,持有对象的所有权
-(id) initWithDog: (Dog *)dog
{
self = [super init];
if (self != nil) {
_dog = [dog retain];
}
return self;
}
dealloc方法
当对象的引用计数为0时,系统调用此对象dealloc。
我们应该在dealloc方法中,释放他持有的对象。
-(void)dealloc
{
[_dog release];
[_car release];
[super release];//为了释放父类持有的对象所有权。
}
property的使用
有一个类有如下属性
@interface User:NSObject
{
int userId;
NSString *_userName;
NSString *_passwd;
NSString *_email;
NSString *_birthday;
}
设置器
-(void) setUserName: (NSString *)name
{
_name = name;
}
-(NSString *)userName{
return _name;
}
每个属性都这样写set和get方法是很麻烦的。
property可以自动生成一个get和set方法,不需要我们手动编写
@interface User : NSObject
{
//不必在定义,@property自动生成
}
@property(nonatomic, retain) NSString *userName;
使用方法
@property(<1>nonatomic,<2>retain,<3>readwrite)NSString *userName;
1.原子性:
atomic:多线程模式下存在线程保护,默认
nonatomic:多线程模式下不存在线程保护
2.赋值
assign:直接赋值,默认
-(void)setUserName: (NSString *)name
{
_name = name;
}
retain:保留原对象
-(void)setUserName: (NSString *)name
{
if (_name != name) {
[_name release];
_name = [name retain];
}
}
copy:拷贝对象
-(void)setUserName: (NSString *)name
{
if (_name != name) {
[_name release];
_name = [name copy];
}
}
3.读写性
readwrite:生成getter,setter方法,默认
readonly:只生成getter方法
注意,我们也可以重新实现set和get方法,优先调用自己实现的set和get方法。
实战
1.理解引用计数,对象所有权,理解oc内存管理的机制。
2.设计如下几个类,Car自定义初始化方法,初始化方法传入引擎对象和车灯对象。当车启动的时候,会调用引擎转动,车灯亮灯,当车停止的时候调用引擎停止转动,车灯熄灭。(注意内存管理)
|
Car(汽车) |
Engine(引擎) |
lamp(车灯) |
|
属性: |
属性: |
属性: |
|
名字(name) |
型号(model) |
瓦数(wattage) |
|
车牌号(licence) |
排量(capacity) |
方法: |
|
引擎(engine) |
方法: |
亮(light) |
|
车灯(lamp) |
转动(turn) |
熄灭(dark) |
|
方法:启动(run) |
停止转动(stopTurn) |
|
|
停止(stop) |
数组的内存管理
如果一个对象被添加到数组,那么这个对象的引用计数会变化吗?
NSMutableArray *mutableArray = [NSMutableArray array];
Dog *dog1 = [[Dog alloc]init];
Dog *dog2 = [[Dog alloc]init];
[array addObject: dog1]; //dog1会被数组retain,计数+1
[array addObject:dog2];dog2会被数组retain计数+1
[dog1 release];
[dog2 release];
数组销毁,或者removeAllObjects,会给每一个元素发送release消息。
[array release];
//移除下标为1的元素,同时向它发送release消息
[array removeObjectAtIndext:1];
自动释放池
自动释放池是oc的一种内存自动管理机制。
当自动释放池销毁时,它会对池子中每一个对象调用一次release方法。
当我们向一个对象autorelease消息时,这个对象就会被放入自动释放池。
老的写法
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
User *user = [[user alloc]init];
//加入自动释放池
[user autorelease];
//自动释放池销毁
[pool release];
新语法的写法
@autorelesepool {
User *user = [[User alloc]init];
//加入自动释放池
[user autorelease];
}
自动释放池的原理
自动释放池就像一个数组,容纳了许多加入池的对象。
|
对象1 |
对象2 |
对象3 |
自动释放池可以嵌套使用
autorelease会将对象添加到离他最近的自动释放池中。
NSAutoreleasePool pool1 = [[NSAutoreleasePool alloc]init];
Person *person = [[Person alloc]init];
//添加到离他最近的pool1中
[person autorelease];
NSAutoreleasePool pool2 = [[NSAutoreleasePool alloc]init];
Dog *dog = [[Dog alloc]init];
[dog autorelease];
[pool2 release];
[pool1 release];
典型错误例子,如下代码有内存泄漏
[person setDog:[[Dog]alloc]init];
正确的写法是:
Dog *dog = [[[Dog alloc]init]release];
[person setDog:dog];
//这里不能[dog release]
错误的典型例子
-(NSString *)getInfo {
NSString *info = [[NSString alloc]initWithFormat:@"姓名:%s年龄:%d", _name, _age];
[info release];//返回的对象已经销毁
return info;
}
NSString *infoString = [person getInfo];
[infoString release];//违背了黄金法则
正确的写法是
-(NSString *)getInfo {
NSString *info = [[NSString alloc]initWithFormat:@"姓名:%s年龄:%d", _name, _age];
return [info autorelease];
}
NSString *infoString = [person getInfo];
//自动释放池销毁的时候,会向infoString对象发送release消息。
举例
for (i = 0; i < 1000000; i++) {
NSMutableArray *array = [[NSMutableArray alloc]init];
[array autorelease];
}
循环100万次,创建100万个对象,这些对象在该循环体内不会自动销毁,只会加到最近的自动释放池中。只有当自动释放池销毁了,100万个对象才会受到release消息。这样100万个对象同时存在内存当中,占用内存很大。
更好的写法是
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
for (i = 0; i < 1000000; i++) {
if (i % 1000 == 0) {
[pool release];
NSAutorelease *pool = [[NSAutorelease alloc]init];
}
NSMutableArray *array = [[NSMutableArray alloc]init];
[array autorelease];
}
该代码每执行1000次就会把pool释放一次,同时在穿件一个新的自动释放池。也就是累计不会超过1000个对象。
类方法创建的内存管理
+(Dog *)dog {
Dog *dog = [[Dog alloc]init];
return [dog autorelease];
};
Dog *dog = [Dog dog];
[person setDog: dog];
//这里不能[dog release];
类方法创建的内存管理
Foundation中的类可以通过alloc创建和类方法创建,区别在于内存管理不一样。
类方法创建的对象是加入了自动释放池
//alloc创建
NSArray *array = [[NSArray alloc]init];
//其他方法
[array release];
//类方法创建, 加入了自动释放池
NSArray *arr = [NSArrary array];
//不能再用[arr release];
新语法创建的内存管理
新语法创建的对象和类方法创建的对象相同,由自动释放池管理
NSArray *array = @[@“zhngsan”, @”lisi”];
NSNumber *number = @123;
常见的错误
1.错误
+(id) person{
Person *person = [[Person alloc]init];
[person release];
return person;
}
正确的写法
+(id)person {
Person *person = [[Person alloc]init];
return [person autorelease];
}
2.错误
+(void)description
{
NSString *string = [NSString stringWithFormat:@”lrz”];
[name release];
NSLog(“name:%@”, string);
}
正确
+(void)description
{
NSString *string = [NSString stringWithFormat:@”lrz”];
NSLog(“name:%@”, string);
}
3.错误
Laptop *laptop = [[Laptop alloc]init];
laptop.name = [NSString stringWithFormat:@”apple”];
NSArray *array = [NSArray arrayWithObject: laptop];
[array release];
正确
Laptop *laptop = [[Laptop alloc]init];
laptop.name = [NSString stringWithFormat:@”apple”];
NSArray *array = [NSArray arrayWithObject: laptop];
[laptop release];
4.错误
-(void)memory
{
Car *car = [[Car alloc]init];
if (gas == 0) {
return;
}
[car release];
}
正确
-(void)memory
{
Car *car = [[[Car alloc]init]autorelease];
if (gas == 0) {
return;
}
}
ARC
自动引用计数(automatic reference counting),提供自动管理内存的功能。
不需要手动管理引用计数,不需要也不允许retain, release, autorelease
注意版本的支持是在IOS4(不支持弱引用),ios5上
循环引用
对象A retain对象B。同时对象B retain对象A,这种情况我们称之为循环引用。
循环引用会导致两个对象都无法销毁掉。
-(void)setA: (A *a)
{
if (_a!=a) {
[_a release];
_a = [a retain];
}
}
-(void)setB:(B *b)
{
if (_b != b) {
[_b release];
_b = [b retain];
}
}
Object-c 内存管理的更多相关文章
- 12.Object-C--浅谈OC的内存管理机制
昨天学习了OC的内存管理机制,今天想总结一下,所以接下来我要在这里bibi一下:OC的内存管理. 首先我要说的是,内存管理的作用范围. 内存管理的作用范围: 任何继承了NSObject的对象,对其他基 ...
- .NET基础拾遗(1)类型语法基础和内存管理基础
Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开发基 ...
- cocos2d-x内存管理
Cocos2d-x内存管理 老师让我给班上同学讲讲cocos2d-x的内存管理,时间也不多,于是看了看源码,写了个提纲和大概思想 一. 为什么需要内存管理 1. new和delete 2. 堆上申 ...
- iOS开发系列—Objective-C之内存管理
概述 我们知道在程序运行过程中要创建大量的对象,和其他高级语言类似,在ObjC中对象时存储在堆中的,系统并不会自动释放堆中的内存(注意基本类型是由系统自己管理的,放在栈上).如果一个对象创建并使用后没 ...
- Android 内存管理 &Memory Leak & OOM 分析
转载博客:http://blog.csdn.net/vshuang/article/details/39647167 1.Android 进程管理&内存 Android主要应用在嵌入式设备当中 ...
- Python深入06 Python的内存管理
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 语言的内存管理是语言设计的一个重要方面.它是决定语言性能的重要因素.无论是C语言的 ...
- 【转】iOS夯实:ARC时代的内存管理
iOS夯实:ARC时代的内存管理 什么是ARC Automatic Reference Counting (ARC) is a compiler feature that provides autom ...
- 理解 iOS 的内存管理
远古时代的故事 那些经历过手工管理内存(MRC)时代的人们,一定对 iOS 开发中的内存管理记忆犹新.那个时候大约是 2010 年,国内 iOS 开发刚刚兴起,tinyfool 大叔的大名已经如雷贯耳 ...
- Objective-C内存管理之-引用计数
本文会继续深入学习OC内存管理,内容主要参考iOS高级编程,Objective-C基础教程,疯狂iOS讲义,是我学习内存管理的笔记 内存管理 1 内存管理的基本概念 1.1 Objective-C中的 ...
- 内存管理内幕mallco及free函数实现
原文:https://www.ibm.com/developerworks/cn/linux/l-memory/ 为什么必须管理内存 内存管理是计算机编程最为基本的领域之一.在很多脚本语言中,您不必担 ...
随机推荐
- Javascript判断object还是list/array的类型(包含javascript的数据类型研究)
前提:先研究javascript中的变量有几种,参考: http://www.w3school.com.cn/js/js_datatypes.asp http://glzaction.iteye.co ...
- Beta阶段第九次Scrum Meeting
情况简述 BETA阶段第九次Scrum Meeting 敏捷开发起始时间 2017/1/2 00:00 敏捷开发终止时间 2017/1/3 00:00 会议基本内容摘要 deadline临近 参与讨论 ...
- Mac OS使用brew安装Nginx、MySQL、PHP-FPM的LAMP开发环境
准备工作 新版的 Mac OS 内置了Apache 和 PHP,我的系统版本是OS X 10.9.3,可以通过以下命令查看Apache和PHP的版本号: httpd -v Server version ...
- IIS7.5使用web.config设置伪静态的二种方法
转自 网上赚钱自学网 .http://www.whosmall.com/post/121 近几天公司里开发的项目有几个运行在IIS7.5上,由于全站采用的是伪静态,因此从网上找到两两种方法来实现.这两 ...
- v-for遍历出的元素上添加click事件,获取对应元素上的属性id值
<span v-for="(n,nav) in floorList" data-id="{{nav.itemId}}" v-on:click=" ...
- redis 操作 hash 的测试
1>hset setname field value hset stuSet name zhangsan:1 2>hget setname field hget stuset ...
- Java中接口的实现问题
1.Java 接口的访问权限 interface A{}//接口A包访问权限 public interface A{}//接口A公有访问 interface A{ void function1(): ...
- sqlserver 数据库索引建立原则
1.始终包含聚集索引 当表中不包含聚集索引时,表中的数据是无序的,这会降低数据检索效率.即使通过索引缩小了数据检索的范围,但由于数据本身是无序的,当从表中提取实际数据时,会产生频繁的定位问题,这也使得 ...
- errored out in DoExecute, couldn't PrepareToExecuteJITExpression
error: Couldn't materialize struct: size of variable <varName> disagrees with the ValueObject' ...
- [原创] Delphi小工具(Windows资源管理器右键菜单扩展)
两个小工具 1. 项目临时文件清理 2. Android Ndk 编译 c/c++ jni 源码工具. 下载后,点击Reg.bat就可以完成注册安装.不需要时点击 UnReg.Bat 就可以删除菜单. ...