内存管理细节:http://blog.sina.com.cn/s/blog_814ecfa90102vus2.html

学习目标

1.【理解】内存管理

2.【掌握】第一个MRC程序

3.【掌握】内存管理的原则

4.【理解】野指针与僵尸对象

5.【理解】单个对象的内存管理

6.【理解】多个对象的内存管理

7.【掌握】set方法的内存管理

8.【掌握】@property参数

9.【掌握】@class关键字

10.【理解】循环retain

一、内存管理

程序在运行过程中会在堆空间创建大量的对象,当对象不再使用的时候,系统并不会自动释放堆空间中的对象(基本数据类型是由系统自动管理的)。如果一个对象创建并使用后没有得到及时释放,那么这对象会直到程序结束才会被释放,这样就会占用大量内存空间。其他高级语言如C#、Java都是通过垃圾回收机制(GC)来解决这个问题的,但在OC中并没有类似的垃圾回收机制,因此它的内存管理就需要由程序员手动维护。并且栈空间、BSS段、数据段、代码段中的数据都是由系统自动管理的,所以这些区域的数据不需要由程序员来管理,我们只需要管理堆空间中的数据,也就是OC对象的释放需要我们来管理。


引用计数器

OC中内存的管理是依赖对象引用计数器来进行的,在OC中每个对象内部都有一个与之对应的整数(retainCount),叫“引用计数器”。当一个对象在创建之后它的引用计数器默认值为1,如果一个对象的引用计数器为0,则系统会自动调用这个对象的dealloc方法来销毁这个对象。


内存泄露

一个对象在使用完没有回收,直到程序结束的时候才被回收,这种现象就叫做内存泄露。

如何操作对象的引用计数器?

调用对象的retain方法,对象的引用计数器就会+1。

调用对象的release方法,对象的引用计数器就会-1。

调用对象的retainCount方法,就会得到这个对象的引用计数器的值。


总结:

当我们要使用一个对象的时候,应该为这个对象发送一条retain消息。当我们不再使用一个对象的时候,就应该为这个对象发送一条release消息。当对象的引用计数器为0的时候,系统就会调用这个对象的dealloc方法来销毁这个对象,所以我们一般会重写dealloc方法来监控对象的销毁。

二、第一个MRC程序

OC内存管理的分为MRC(Manual
Reference Counting)手动引用计数ARC(Automatic
Reference Counting)自动引用计数。见名知意,MRC是程序员自己手动记录对象的引用计数,而ARC则是系统自动记录对象的引用计数。由于ARC机制是在Xcode4.2后推出的,所以默认情况下Xcode是自动开启ARC机制的,我们要使用MRC就必须关闭ARC。

为了便于监控对象释放,我们经常需要重写dealloc方法。并且重写dealloc方法需要遵循一些规范,在子类的dealloc方法最后必须调用父类的dealloc方法。

//Person.h文件

#import

@interface Person : NSObject

@end

//Person.m文件

#import "Person.h"

@implementation Person

//重写dealloc方法

- (void)dealloc {

NSLog(@"人挂了...");

//在子类dealloc方法最后必须调用父类dealloc方法

[super dealloc];

}

@end

//main.m文件

#import

#import "Person.h"

int main(int argc, const char * argv[]) {

//使用alloc创建对象,默认对象引用计数器的值为
1

Person *p = [[Person alloc]
init];

//给对象发送release消息只是让引用计数器
-1,并不是一定会让对象引用计数器变为0

[p release];

//调用对象的dealloc方法,输出

//人挂了...

return 0;

}

三、内存管理的原则

只在有人使用这个对象的时候才为对象发送retain消息,只在一个人不再使用对象的时候才为对象发送release消息。遵守谁创建谁release,谁retain谁release的原则。

//Person.h文件

#import

@interface Person : NSObject

@end

//Person.m文件

#import "Person.h"

@implementation Person

//重写dealloc方法

- (void)dealloc {

NSLog(@"人挂了...");

//在子类dealloc方法最后必须调用父类dealloc方法

[super dealloc];

}

@end

//main.m文件

#import

#import "Person.h"

int main(int argc, const char * argv[]) {

//使用alloc创建对象,引用计数器 +1

Person *p = [[Person alloc]
init];

//给对象发送一条retain消息,引用计数器 +1

[p retain];

//给p对象发送一条retainCount消息,返回引用计数器的值

NSUInteger count = [p
retainCount];

NSLog(@"count =
%lu",count);

//给对象发送release消息只是让引用计数器
-1,并不是一定会让对象引用计数器变为0

[p release];

//再发送一条release,此时引用计数器就为0了

[p release];

//调用对象的dealloc方法,输出

//人挂了...了...

return 0;

}


注意:

1.为对象发送release消息不是让对象释放,而是让对象的引用计数器-1。

2.当对象引用计数器的值为0时,系统会立即调用对象的dealloc方法释放对象。

3.创建谁release,谁retain谁release。

四、野指针与僵尸对象


C语言中的野指针
:一个指针变量指向一块随机的内存空间,也就是未初始化的指针变量,这个指针就叫做野指针。

OC语言中的野指针:指针指向的对象已经被释放了,这个指针就叫做野指针。


僵尸对象
:已经被释放的对象,叫做僵尸对象,也就是通过野指针访问的对象就是僵尸对象。注意一旦一个指针成为了野指针,就不要通过野指针去访问对象的成员了。

每次通过一个指针去访问一个对象的时候,都会去检查这个对象是否是僵尸对象,如果是就报错。默认情况下Xcode是不会自动检测僵尸对象,那么如何开启Xcode对僵尸对象的自动检测?

当一个对象被回收后,就为指向这个对象的指针赋值nil,可避免访问僵尸对象。

//Person.h文件

#import

@interface Person : NSObject

- (void)eat;

@end

//Person.m文件

#import "Person.h"

@implementation Person

- (void)eat {

NSLog(@"人吃饭");

}

//重写dealloc方法

- (void)dealloc {

NSLog(@"人挂了...");

//调用父类dealloc方法

[super dealloc];

}

@end

//main.m文件

#import

#import "Person.h"

int main(int argc, const char * argv[]) {

Person *p = [[Person alloc]
init];

//release让引用计数器
-1,此时对象引用计数器为0.对象被销毁

[p release];

//p指针赋值nil

p = nil;

//给p对象发送消息不会报错,只会啥都不发生

[p eat];

return 0;

}

五、单个对象的内存管理

#import
#import
"Person.h"

int main(int
argc, const char * argv[]) {
    Person *p = [[Person alloc]
init];//retainCount == 1
    [p retain];//retainCount ==
2
    [p retain];//retainCount ==
3

//
   //只有retain没有release
    [p release];//retainCount ==
2
    [p release];//retainCount ==
1
    
//
   //在不适当的时候为指针赋值nil
//
   p =
nil;//对象还没释放,就将指针赋值nil
    [p release];//retainCount ==
0
    return 0;
}

五、单个对象的内存管理

#import
#import
"Person.h"

int main(int
argc, const char * argv[]) {
    Person *p = [[Person alloc]
init];//retainCount == 1
    [p retain];//retainCount ==
2
    [p retain];//retainCount ==
3

//
   //只有retain没有release
    [p release];//retainCount ==
2
    [p release];//retainCount ==
1
    
//
   //在不适当的时候为指针赋值nil
//
   p =
nil;//对象还没释放,就将指针赋值nil
    [p release];//retainCount ==
0
    return 0;
}

六、多个对象的内存管理

当一个对象的属性是另一个对象的时候,在这个属性的setter方法中,将传入的对象先retain一次,再赋值给属性。并在dealloc方法中对属性进行一次release。

//Car.h文件

#import

@interface Car : NSObject

- (void)run;

@end

//Car.m文件

#import "Car.h"

@implementation Car

-(void)run {

NSLog(@"车启动");

}

- (void)dealloc {

NSLog(@"车挂了...");

//调用父类dealloc方法

[super dealloc];

}

@end

//Person.h文件

#import

#import "Car.h"

@interface Person : NSObject

@property Car *car;

- (void)drive;

@end

//Person.m文件

#import "Person.h"

@implementation Person

- (void)setCar:(Car *)car {

//retain传入的对象,因为多了一个指针(_car属性)指向这个传入的对象

//retain返回值是对象本身

_car = [car retain];

}

- (void)drive {

NSLog(@"车启动");

//调用_car的run方法

[self.car run];

}

- (void)dealloc {

NSLog(@"人挂了...");

//在人挂的时候让车也挂

[self.car release];

//调用父类dealloc方法

[super dealloc];

}

@end

//main.m文件

#import

#import "Person.h"

int main(int argc, const char * argv[]) {

//实例化人和车对象

Person *p = [[Person alloc]
init];

Car *bmw = [[Car alloc]
init];

//给人一辆车

p.car = bmw;

//后创建的对象先release

[bmw release];

bmw = nil;

[p release];

p = nil;

return 0;

}

七、set方法的内存管理

//Car.h文件
#import

@interface
Car : NSObject
@property int
speed;

-
(void)run;
@end

//Car.m文件
#import
"Car.h"

@implementation Car
-(void)run
{
    NSLog(@"车启动");
}

-
(void)dealloc {
   
NSLog(@"时速为%d车挂了...",_speed);
    
    [super dealloc];
}
@end

//Person.h文件
#import
#import
"Car.h"

@interface
Person : NSObject
@property Car
*car;
@property
NSString *name;
-
(void)drive;
@end

//Person.m文件
#import
"Person.h"

@implementation Person

-
(void)setCar:(Car *)car {
    //判断传入对象是否和属性的旧值是同一对象
    if (_car != car) {
     
  //不是同一对象就release旧值
     
  [_car release];
     
  //retain新值
     
  _car = [car retain];
    }
}

- (void)drive
{
    //调用_car的run方法
    [_car run];
}

-
(void)dealloc {
   
NSLog(@"%@挂了...",_name);
   
//对象释放前将属性指向的对象release一次,表示不指向那个对象了
    [_car release];
    
    [super dealloc];
}
@end

//main.m文件
#import
#import
"Person.h"

int main(int
argc, const char * argv[]) {
    //实例化两个人对象
    Person *p1 = [[Person alloc]
init];//p1的retainCount == 1
    Person *p2 = [[Person alloc]
init];//p2的retainCount == 1
    //分别为两个人对象的_name赋值
    p1.name = @"jack";
    p2.name = @"rose";
    
    //实例化两个车对象
    Car *bmw = [[Car alloc]
init];//bmw的retainCount == 1
    Car *benz = [[Car alloc]
init];//benz的retainCount == 1
    //分别为两个车对象的_speed赋值
    bmw.speed = 100;
    benz.speed = 80;
    
    //p1指向bmw
    p1.car =
bmw;//bmw的retainCount == 2
    
    //p2也指向bmw
    p2.car =
bmw;//bmw的retainCount == 3
    
    //p2不指向bmw了,指向benz
    p2.car =
benz;//bmw的retainCount == 2 , benz的retainCount == 2
    
    [benz
release];//benz的retainCount == 1
    [p2 release];//p2的retainCount
== 0 调用了p2的delloc方法,再调用benz的release. benz的retainCount == 0
 所以rose挂了,benz也挂了
    [bmw
release];//bmw的retainCount == 1
    [p1 release];//p1的retainCount
== 0 调用了p1的delloc方法,再调用bmw的release. bmw的retainCount == 0
 所以jack挂了,bmw也挂了
    return 0;
}

八、@property参数

@property的参数分为三类,中间用逗号分隔,每类参数可以从上图三类参数中任选一个。如果不进行设置或者只设置其中一类参数,程序会使用三类中的各类的默认参数,默认参数:(atomic,readwrite,assign)。

格式:@property
([参数1,参数2,参数3]) 数据类型 名称;  
 // []里表示可选

一般情况下如果在多线程开发中一个属性可能会被两个及两个以上的线程同时访问,此时可以考虑atomic属性,否则建议使用nonatomic,不加锁,效率较高。readwirte方法会生成getter、setter两个方法,如果使用readonly则只生成getter方法。关于setter方法处理需要特别说明,假设我们定义一个属性a,这里列出三种方式的生成代码:


assign,用于基本数据类型

- (void)setA:(int)a {

_a = a;

}


retain,通常用于非字符串对象

- (void)setA:(Car *)a {

if(_a! = a){

[_a release];

_a = [a retain];

}

}


copy,通常用于字符串对象

- (void)setA:(NSString *)a{

if(_a! = a){

[_a release];

_a = [a copy];

}

}

我们可以在参数中指定@property生成的setter、getter的方法名称,一般情况下,当属性的类型是一个BOOL类型的时候,我们可以指定getter方法名以is开头,这样增强代码可阅读性。

@property (nonatomic,assign,readwrite,getter=isRich) int rich;

//生成的setter、getter方法声明

- (void)setRich:(BOOL)rich;

- (BOOL)isRich;

//生成的setter、getter方法实现

- (void)setRich:(BOOL)rich {

}

- (BOOL)isRich {

}


实际应用案例

//Book.h文件

#import

@interface Book : NSObject

@property (nonatomic, copy) NSString *bookName;

@end

//Book.m文件

#import "Book.h"

@implementation Book

- (void)dealloc {

NSLog(@"<<%@>>被烧了",_bookName);

[_bookName release];

[super
dealloc];

}

@end

//Student.h文件

#import

#import "Book.h"

@interface Student : NSObject

@property (nonatomic, retain) Book *book;

@end

//Student.m文件

#import "Student.h"

@implementation Student

- (void)dealloc {

NSLog(@"<<%@>>",_book.bookName);

[_book release];

[super
dealloc];

}

@end

//main.m文件

#import

#import "Student.h"

int main(int argc, const char * argv[]) {

Student *stu = [[Student
alloc] init];//stu的retainCount == 1

Book *book = [[Book alloc]
init];//book的retainCount == 1

stu.book
= book;//book的retainCount == 2

stu.book.bookName =
@"风流少女";

[book
release];//book的retainCount == 1

[stu
release];//stu的retainCount ==
0,调用stu的dealloc方法,再调用book的release,则book的retainCount == 0

return 0;

}

九、@class关键字

当两个类相互包含的时候,就会造成死循环。在.h文件中不能包含和自己关联的那个类的头文件,因为一旦包含就会造成死循环。应该使用@class来声明一个类存在,如果在.m文件中需要使用用@class声明的类的成员时,就在.m文件中包含即可。

//Book.h文件

#import

@class Student;//如果不需要使用类中的成员可以使用@class告诉编译器,Student是一个类

@interface Book : NSObject

@property (nonatomic, copy) NSString *bookName;

@property (nonatomic, assign) Student *owner;

- (void)showOwner;

@end

//Book.m文件

#import "Book.h"

#import "Student.h"

@implementation Book

- (void)showOwner {

//使用了Student类的成员_name,直接包含头文件即可

NSLog(@"%@的主人是%@",self.bookName,self.owner.name);

}

- (void)dealloc {

NSLog(@"书挂");

[_bookName release];

[super dealloc];

}

@end

//Student.h文件

#import

@class Book;//如果不需要使用类中的成员可以使用@class告诉编译器,Book是一个类

@interface Student : NSObject

@property (nonatomic, copy) NSString *name;

@property (nonatomic, retain) Book *book;

- (void)showBook;

@end

//Student.m文件

#import "Student.h"

#import "Book.h"

@implementation Student

- (void)showBook {

//使用了Book类的成员_bookName,直接包含头文件即可

NSLog(@"%@的书名叫%@",self.name,self.Book.bookName);

}

- (void)dealloc {

NSLog(@"学生挂");

[_name release];

[_book release];

[super dealloc];

}

@end

十、循环retain

当两个类互相引用的时候,如果两个类的@property的参数都是retain就会出现循环引用造成内存泄露。解决办法是一个类使用retain,另一个类使用assign。使用assign的类不用在dealloc方法中对那个属性release。

//Book.h文件

#import

@class Student;//告诉Book,Student是一个类

@interface Book : NSObject

@property (nonatomic, copy) NSString *bookName;

@property (nonatomic, assign) Student *owner;//使用assign

@end

//Book.m文件

#import "Book.h"

@implementation Book

- (void)dealloc {

NSLog(@"<<%@>>释放",self.bookName);

[super dealloc];

}

@end

//Student.h文件

#import

@class Book;//告诉Student,Book是一个类

@interface Student : NSObject

@property (nonatomic, copy) NSString *name;

@property (nonatomic, retain) Book *book;//使用retain

@end

//Student.m文件

#import "Student.h"

@implementation Student

- (void)dealloc {

NSLog(@"<<%@>>释放了",self.name);

[_book release];

[super dealloc];

}

@end

//main.m文件

#import

#import "Book.h"

#import "Student.h"

int main(int argc, const char * argv[]) {

Book *book = [[Book alloc]
init];//book的retainCount == 1

book.bookName = @"风流少女";

Student
*student = [[Student alloc] init];//student的retainCount ==
1

student.name = @"牛逼哥";

book.owner
= student;//因为是assign,所以不会retain传入的student,则student的retainCount ==
1

student.book =
book;//因为是retain,会retain传入的book,则book的retainCount == 2

[student
release];//student的retainCount ==
0,会调用student的dealloc方法,再调用一次book的release。则book的retainCount ==
1

student = nil;

[book
release];//book的retainCount == 0

book = nil;

return
0;

}

还不够清楚怎么办?嘿嘿我早就想到了点击:http://blog.sina.com.cn/s/blog_814ecfa90102vus2.html

欢迎分享本文,转载分享请注明出处!

OC内存管理-OC笔记的更多相关文章

  1. OC 内存管理机制总结

    OC 内存管理机制总结 一:OC内存管理机制目前分为两块,其一自动内存管理机制,其二手动内存管理机制: 1.首先我们从自动内存管理机制讲起: 1)什么是自动内存管理机制,自动内存管理机制就是程序中所创 ...

  2. OC内存管理基础

    OC 内存管理基础 一. retain和release基本使用 使用注意: 1.你想使用(占用)某个对象,就应该让对象的计数器+1(让对象做一次retain操作) 2.你不想再使用(占用)某个对象,就 ...

  3. QF——OC内存管理详解

    堆的内存管理: 我们所说的内存管理,其实就是堆的内存管理.因为栈的内存会自动回收,堆的内存需要我们手动回收. 栈中一般存储的是基本数据类型变量和指向对象的指针(对象的引用),而真实的对象存储在堆中.因 ...

  4. OC内存管理-黄金法则

    1.内存管理-黄金法则 The basic rule to apply is everything that increases the reference counter with alloc, [ ...

  5. OC内存管理总结,清晰明了!

    <span style="font-size:18px;">OC内存管理 一.基本原理 (一)为什么要进行内存管理. 由于移动设备的内存极其有限.所以每一个APP所占的 ...

  6. 31 (OC)* 内存管理

    31 (OC)  内存管理 一:内存管理黄金法则. 如果对一个对象使用了alloc.[Mutable]copy,retain,那么你必须使用相应的realease或者autorelease 二:内存管 ...

  7. C++内存管理学习笔记(5)

    /****************************************************************/ /*            学习是合作和分享式的! /* Auth ...

  8. C++内存管理学习笔记(6)

    /****************************************************************/ /*            学习是合作和分享式的! /* Auth ...

  9. C++内存管理学习笔记(7)

    /****************************************************************/ /*            学习是合作和分享式的! /* Auth ...

随机推荐

  1. 使用ffmpeg转码时遇到aac报错

    今天尝试用ffmpeg转一个视频的格式,结果报出这个错误: The encoder 'aac' is experimental but experimental codecs are not enab ...

  2. ES6(类)

    类的概念 1.基本定义 2.继承 继承如何传递参数?(super) 定义自己属性的时候调用 this 一定要在 super 之后(在继承关系中,子类的构造函数如果用 super 传递参数的过程中,su ...

  3. Android广播的发送与接收

    Android广播的发送与接收 效果图 广播发送 广播分为有序广播和无序广播 有序广播与无序广播的区别 无序广播:只要是广播接收者指定了接收的事件类型,就可以接收到发送出来的广播消息.不能修改消息. ...

  4. [boost] build boost with intel compiler 16.0.XXX

    Introduction There are few information about how to compile boost with Intel compiler. This article ...

  5. jdbc批量插入

    分享牛,分享牛原创.有这样一个需求,文本文件中的数据批量的插入mysql,怎么用jdbc方式批量插入呢? jdbc默认提供了批量插入的方法,可能用一次就忘记了,这里做笔记记录一下jdbc批量插入吧. ...

  6. Redis工作系列之一 与 Memcached对比理解

         近期公司项目在使用Redis,这几年Redis很火,Redis也常常被当作Memcached的挑战者被提到桌面上来.关于Redis与Memcached的比较更是比比皆是.然而,Redis真的 ...

  7. static 变量(静态变量)

    在C++的面向对象编程中,static还可以加在类的数据成员或成员函数之前.这样定义的数据成员或成员函数就被类所拥有,而不再属于类的对象. #include <iostream> usin ...

  8. Servlet - 基础

    Servlet 标签 : Java与Web HTTP协议 HTTP(hypertext transport protocol),即超文本传输协议.这个协议详细规定了浏览器(Browser)和万维网服务 ...

  9. Android简易实战教程--第三十四话《 自定义SeekBar以及里面的一些小知识》

    转载本专栏文章,请注明出处尊重原创:博客地址http://blog.csdn.net/qq_32059827/article/details/52849676:小杨的博客 许多应用可能需要加入进度,例 ...

  10. C/C++与Matlab混合编程初探

    ================================================================== % 欢迎转载,尊重原创,所以转载请注明出处. % http://b ...