关闭

OC内存管理-OC笔记

865人阅读 评论(0) 收藏 举报
分类:

内存管理细节: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。

QQ20150725-1

为了便于监控对象释放,我们经常需要重写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对僵尸对象的自动检测?

QQ20150725-3

QQ20150725-2

当一个对象被回收后,就为指向这个对象的指针赋值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参数

QQ20150725-4

@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

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

0
0
查看评论

关于OC中的Block使用以及ARC和MAR下的内存管理方式

1.什么是Block?     Block是属于C语言框架     Block是一种数据类型(类似int)     Block是一段代码块,只有在被调用的时候被执行(类似函数和方...
  • XieEXiaoZhang
  • XieEXiaoZhang
  • 2016-07-25 15:19
  • 976

OC的手动内存管理基础(IOS开发)

OC的内存管理分为三种:          1,手动内存管理(MRC).          2,自动内存管理(ARC)  ...
  • aa605397965
  • aa605397965
  • 2015-07-13 23:39
  • 768

OC学习笔记五 内存管理(property 参数)

@property不带参数会默认生成标准的setter getter方法,不会帮助管理内存。 加上了参数才会帮忙管理内存。 基本数据类型(int float....)不需要写参数,或者也可以加上(assign)参数 (写不写实际没什么区别);如果是对象的话一般加上retain
  • wangjiwei2010
  • wangjiwei2010
  • 2015-08-12 11:25
  • 556

OC学习篇之---内存管理介绍和使用

在之前的一片文章我们说了OC中谓词操作:http://blog.csdn.net/jiangwei0910410003/article/details/41923507,从今天开始我们就来看一下OC中最难的一部分内容:内存管理为什么说他难呢?因为内存如果需要我们程序员去管理的话,那个难度肯定是很大的...
  • jiangwei0910410003
  • jiangwei0910410003
  • 2014-12-14 16:16
  • 9007

Swift OC 内存管理

???? 1.??????? ???????,???????????,?????????????????alloc?free,?????????????????????,????,????????????????,????(memory leak),???????????????????????...
  • JoeTongOwn
  • JoeTongOwn
  • 2016-08-02 15:01
  • 143

C++/Objective-C内存管理对比学习

C++内存管理内存分为五个区:堆、栈、自由存储区、全局/静态存储区和常量存储区。 栈。存储函数中局部变量,函数结束时会自动释放。 堆。由new分配的内存块,由应用程序控制它的回收(需要手动回收),即一个new应该对应一个delete。如果程序员没释放,则程序结束系统会自动回收。 自由存储区。使用ma...
  • linyousong
  • linyousong
  • 2016-02-27 22:38
  • 345

OC-内存管理的一些要点

创建一个BOOK对象,对其属性进行声明 定义。 @property 属性声明 定义了对属性的赋值  -(void) dealloc 方法在对象销毁的时候进行调用; #import @interface Book : NSObject @property (nonatomi...
  • qq285016127
  • qq285016127
  • 2014-06-12 00:21
  • 806

探讨OC的内存管理 以及防止循环引用retain cycle 代理为什么用weak block为什么用copy

首先声明OC的内存管理机制:引用计数。当一个对象的引用计数为0时,这个对象就会被释放。铭记以上两点,我们开始来探讨。 换句话说,如果一个对象,没有强指针指向,就会被释放。举个很形象的例子,假如每一个对象都是一只狗,每一个强指针都是狗链子,狗链子的数量就是引用计数。那么情况就会变成这样:当一个狗没有被...
  • qinqi376990311
  • qinqi376990311
  • 2016-10-26 15:17
  • 672

OC之内存管理(笔记)

为什么要进行内存管理:  手持设备内存小; 对象只开辟不释放,程序容易出错或崩溃 管理原则:谁创建,谁销毁 管理方式:手动管理 管理机制:retainCount 引用计数 方式管理对象所占用的内存 1. 使用 alloc 为对象分配内存;使用 dealloc 释放对象所...
  • u013364937
  • u013364937
  • 2014-07-10 15:06
  • 216

OC课堂笔记->内存管理

内存管理 Person * p1 = [[Person alloc]init];         [p1 retain];         NSLog(@"%ld",[p...
  • shichangbu123
  • shichangbu123
  • 2014-10-22 16:42
  • 304
    个人资料
    • 访问:595952次
    • 积分:7038
    • 等级:
    • 排名:第3886名
    • 原创:240篇
    • 转载:10篇
    • 译文:1篇
    • 评论:214条
    我的微信公众号

    iOS开发者交流群: 官方付费群
    ①群:446310206
    ②群:426087546


    QQ: 点击这里给我发消息


    关注我的技术公众号,每天都有优质技术文章推送。
    微信关注我的公众号
    关注我的微博
    博客专栏
    我的小仓鼠

    有空帮我喂下仓鼠,谢谢!