020-OC内存管理-OC笔记

本文详细阐述了Objective-C中的内存管理原则、野指针与僵尸对象的概念、单个对象与多个对象的内存管理策略,以及如何正确使用@property和@class关键字。重点介绍了如何避免内存泄露,包括设置正确的引用计数器操作、处理循环引用和对象的生命周期管理。

学习目标

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方法

 

三、内存管理的原则

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

 

注意:

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

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

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

四、野指针与僵尸对象

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

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

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

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

QQ20150725-3

QQ20150725-2

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

 

五、单个对象的内存管理

 

 

六、多个对象的内存管理

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

 

七、set方法的内存管理

 

 

八、@property参数

QQ20150725-4

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

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

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

assign,用于基本数据类型

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

copy,通常用于字符串对象

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

实际应用案例

 

九、@class关键字

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

 

十、循环retain

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

//Book.h文件 #import <Foundation/Foundation.h> @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 <Foundation/Foundation.h> @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 <Foundation/Foundation.h> #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; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
//Book.h文件
#import <Foundation/Foundation.h>
@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 <Foundation/Foundation.h>
@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 <Foundation/Foundation.h>
#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 ;
}

 

 

转载于:https://www.cnblogs.com/liehuntianshi/p/4862407.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值