005-内存管理

一、两大系统

1.iOS系统

  如果忽略内存管理,消耗系统内存到iOS允许值范围时,你的App将得到一次内存警告;如不处理,将会再次接收到第二次警告;如再不处理,程序将会闪退。一些大的游戏能在iOS系统上能够运行得很顺畅,也是完全得益于iOS系统的手动管理内存机制(iPhone 4刚出来时用的就是iOS 4的系统,iOS 4没有自动内存管理,只有手动内存管理,所以内存仅有512M的iPhone 4完全可以很流畅的运行2个多G的大游戏)

2.安卓系统

  Java采用的是自动垃圾回收机制,是将内存管理直接交给了它的虚拟机,而不是程序员自己控制,这也就导致了安卓在内存管理上的不确定,不能很精准的释放和开辟内存,所以安卓也就在拼命的追求硬件的性能

 

二、内存管理基本概念

1.创建对象的过程

  1)分配内存空间,存储对象

  2)初始化成员变量

  3)返回对象的指针地址

2.OC的内存管理机制

  1)对象在完成创建的同时,内部会自动创建一个引用计数器(retainCount),这个计数器是系统用来判断是否回收对象的唯一标识,当我们的引用计数retainCount = 0时,系统会毫不犹豫地回收当前对象

  2)[对象 retain];     // retainCount + 1

  3)[对象 release];   // retainCount - 1

  4)当对象的retainCount = 0时,对象就会被销毁

  5)dealloc函数:当一个对象要被销毁的时候,系统会自动调用dealloc函数,通知对象将要被销毁

3.内存管理原则

  1)配对原则:只要出现了new、alloc、retain,就一定会配对出现一个release或是autorelease

  2)谁杀谁埋:谁创建了对象,谁对该对象进行retain操作,谁就有责任在自己不去使用的时候进行释放

 

三、内存管理的三大模式

1.MRC模式(手动内存管理)

  1)注意:现在Xcode已经默认是自动内存管理(ARC),如果我们需要设置到MRC,则需要作如下操作:

    TARGETS -----> Build Settings -----> Apple LLVM 7.0 - Language - Objective C -----> Objective - C Automatic Reference Counting -----> "Yes" 改为"No"

  2)手动内存管理中的常见问题

    a.野指针错误:对象内存地址已被系统回收,但是指向对象的指针依然存放着这块内存的地址,如果此后再对该块内存进行操作,就会出现野指针错误

    b.内存泄露:对象使用完毕,但是没有使对象retainCount = 0,也就是对象一直在内存中,这样就会造成内存泄露

    实例一:野指针错误

 1     // 野指针错误
 2     Person *p = [[Person alloc] init]; // retainCount = 1
 3     p.age = 10;
 4     [p release]; // retainCount = 0
 5     [p run];
 6     p.age = 30;
 7     // 分析:
 8     // [p release]操作后,指针p所指向的对象引用计数retainCount = 0,此时该对象的内存空间已被系统回收,对象销毁,然而指针p却还存放着这块内存的地址,只不过这块内存是不可用的,如果后面使用指针p再次操作这块内存就会造成野指针错误
 9     // 可现象是:Xcode时而报错,时而不报错
10     // 这是Xcode自身的原因,我们被回收的对象称之为僵尸对象,而Xcode为提高编码效率,不会时时去检测僵尸对象,我们可以自己打开Xcode的时时检测僵尸对象功能(选中点击“Edit Scheme”,勾选复选框“Enable Zombie Objects”)

    实例二:野指针错误

 1     // 野指针错误
 2     Person *p = [[Person alloc] init]; // retainCount = 1
 3     p.age = 10;
 4     [p run];
 5     Person *p1 = p;  // 在这里只不过是增加了一个指针指向同一个对象,该对象的引用计数不会+1
 6     [p1 release];    // retainCount = 0
 7     [p run];
 8     p1.age = 60;
 9     // 分析:
10     // 在[p1 release]后,该对象就被销毁了,另外在这里操作[p release]和[p1 relese]效果是一样的,不管对p还是p1作release操作,后果都是p和p1所指向的同一块内存地址被回收,p和p1也就都成为了野指针,既然都是p和p1都是野指针,那么后续对p和p1的操作都是野指针错误

    实例三:野指针错误

1     // 野指针错误
2     Person *p = [[Person alloc] init];  // retainCount = 1
3     p.age = 20;
4     [p release];                        // retainCount = 0
5     [p retain];
6     // 分析:
7     // 当一个对象retainCount = 0时,再调用retain方法时,不会让对象起死回生,反而会造成野指针错误

    实例四:内存泄露

1     // 内存泄露
2     Person *p = [[Person alloc] init]; // retainCount = 1
3     p.age = 20;
4     [p retain];                        // retainCount = 2
5     [p retain];                        // retainCount = 3
6     [p release];                       // retainCount = 2
7     // 分析:
8     // 只要对象的retainCount != 0,对象就会一直存在于内存中,这样就会造成内存泄露

    实例五:内存泄露

1     // 内存泄露
2     Person *p = [[Person alloc] init];  // retainCount = 1
3     p.age = 20;
4     [p run];
5     p = nil;
6     [p release]; // 相当于 [nil release];
7     // 分析:
8     // 提前将指针设为nil,而此时指针所指向的对象retainCount != 0,那么在后面对指针p进行release操作也就没有什么意义啦

    实例六:内存泄露

 1  - (void)viewDidLoad
 2 {
 3     [super viewDidLoad];
 4     
 5     // 内存泄露
 6     Person *p = [[Person alloc] init];  // retainCount = 1
 7     p.age = 20;
 8     test(p);      // 将对象作为参数传递
 9     [p release];  // retainCount = 1
10     // 分析:
11     // test(p)是将对象作为参数传递,那么在该外部函数中,如果对该对象有使retainCount+1的操作,就必须要对其作retainCount-1的操作,否则很容易造成内存泄露
12     // 另外不可以随意的对该对象作release操作,否则很容易造成对象的提前释放,造成野指针错误
13 }
14 
15 void test(Person *p)
16 {
17     p.age = 50;
18     [p retain];     // retainCount = 2
19 }

    实例七:如何解决

      a.野指针:只要是确定了某一个对象被回收,那么我们就讲指向该对象内存空间的所有指针设为nil,那么野指针就会变为空指针,操作空指针就会无影响啦

          Person *p = [[Person alloc] init];  // retainCount = 1

          p.age = 20;

          [p release];     // retainCount = 0,对象被销毁,指针p变成野指针

          p = nil;          // 野指针p变成空指针

          p.age = 30;   // 相当于 nil.age = 30 --> 无影响

      b.内存泄露:只要内存管理的配对原则,就不会出现这个问题

    实例八:多个对象的内存问题

  1 // Car.h 文件
  2 #import <Foundation/Foundation.h>
  3 
  4 @interface Car : NSObject
  5 
  6 #pragma mark - 方法
  7 - (void)run;
  8 
  9 @end
 10 
 11 
 12 // Car.m 文件
 13 #import "Car.h"
 14 
 15 @implementation Car
 16 
 17 #pragma mark - 方法
 18 - (void)run
 19 {
 20     NSLog(@"Car开走啦");
 21 }
 22 
 23 #pragma mark - 当对象被销毁的时候调用
 24 - (void)dealloc
 25 {
 26     [super dealloc];
 27     
 28     NSLog(@"Car被销毁啦");
 29 }
 30 
 31 @end
 32 
 33 
 34 // Person.h 文件
 35 #import <Foundation/Foundation.h>
 36 #import "Car.h"
 37 
 38 @interface Person : NSObject
 39 {
 40     Car *_car;
 41 }
 42 
 43 #pragma mark - 成员变量的setter方法
 44 - (void)setCar:(Car *)car;
 45 
 46 #pragma mark - 成员变量的getter方法
 47 - (Car *)car;
 48 
 49 #pragma mark - 方法
 50 - (void)drive;
 51 
 52 @end
 53 
 54 
 55 // Person.m 文件
 56 #import "Person.h"
 57 
 58 @implementation Person
 59 
 60 #pragma mark - 成员变量的setter方法
 61 - (void)setCar:(Car *)car
 62 {
 63     _car = [car retain];  // retainCount(car) = 2
 64 }
 65 
 66 #pragma mark - 成员变量的getter方法
 67 - (Car *)car
 68 {
 69     return _car;
 70 }
 71 
 72 #pragma mark - 方法
 73 - (void)drive
 74 {
 75     [_car run];
 76 }
 77 
 78 #pragma mark - 当对象被销毁的时候调用
 79 - (void)dealloc
 80 {
 81     [super dealloc];
 82     
 83     [_car release];  // retainCount(car) = 0(与Car的retain成对)
 84     
 85     NSLog(@"Person被销毁啦");
 86 }
 87                                     
 88 @end                                                                                                                                                                89 
 90 
 91 // ViewController.m 文件
 92 #import "ViewController.h"
 93 #import "Person.h"
 94 #import "Car.h"
 95 
 96 @implementation ViewController
 97 
 98 - (void)viewDidLoad {
 99     [super viewDidLoad];
100     
101     Person *p = [[Person alloc] init];   // retainCount(person) = 1
102     Car *car = [[Car alloc] init];       // retainCount(car) = 1
103     [p setCar:car];  
104     [car release];     // retainCount(car) = 1(与Car的alloc成对)
105     [p drive];
106     [p release];    // retainCount(person) = 0(与Person的alloc成对)
107     // 分析:
108     // Car作为Person的成员变量,Person在自己还没有被释放的时候时候,按道理是可以随意调用自己的方法,可问题是Person在调用自己的方法drive中,还会嵌套调用其成员变量Car的方法run,这时如果Person对Car的引用仅仅是一个弱应用(Person没有对Car的引用计数+1),且如果Car过早释放,Person再去调用其自身方法而引发嵌套调用Car的方法,势必会造成Car的野指针错误
109     //  未解决这类问题,我们通常会在Person中Car的setter方法中,对Car进行retain操作,使其retainCount+1;那么有了retain,就必须配对出现release或是autorelease,这个操作一般放在只有当Person被释放的时候,也就是Person的dealloc方法中
110     
111 }
112 
113 @end

    实例九:setter方法的内存分析

  1 // Car.h 文件
  2 #import <Foundation/Foundation.h>
  3 
  4 @interface Car : NSObject
  5 
  6 #pragma mark - 方法
  7 - (void)run;
  8 
  9 @end
 10 
 11 
 12 // Car.m 文件
 13 #import "Car.h"
 14 
 15 @implementation Car
 16 
 17 #pragma mark - 方法
 18 - (void)run
 19 {
 20     NSLog(@"Car开走啦");
 21 }
 22 
 23 #pragma mark - 当对象被销毁的时候调用
 24 - (void)dealloc
 25 {
 26     [super dealloc];
 27     
 28     NSLog(@"Car被销毁啦");
 29 }
 30 
 31 @end
 32 
 33 
 34 // Person.h 文件
 35 #import <Foundation/Foundation.h>
 36 #import "Car.h"
 37 
 38 @interface Person : NSObject
 39 {
 40     Car *_car;
 41 }
 42 
 43 #pragma mark - 成员变量的setter方法
 44 - (void)setCar:(Car *)car;
 45 
 46 #pragma mark - 成员变量的getter方法
 47 - (Car *)car;
 48 
 49 #pragma mark - 方法
 50 - (void)drive;
 51 
 52 @end
 53 
 54 
 55 // Person.m 文件
 56 #import "Person.h"
 57 
 58 @implementation Person
 59 
 60 #pragma mark - 成员变量的setter方法
 61 - (void)setCar:(Car *)car
 62 {
 63     if (_car != car) {
 64         [_car release];       // release旧值
 65         _car = [car retain];  // retain新值
 66     }
 67 }
 68 
 69 #pragma mark - 成员变量的getter方法
 70 - (Car *)car
 71 {
 72     return _car;
 73 }
 74 
 75 #pragma mark - 方法
 76 - (void)drive
 77 {
 78     [_car run];
 79 }
 80 
 81 #pragma mark - 当对象被销毁的时候调用
 82 - (void)dealloc
 83 {
 84     [super dealloc];
 85     
 86     [_car release];  // retainCount(car) = 0(与Car的retain成对)
 87     
 88     NSLog(@"Person被销毁啦");
 89 }
 90                                     
 91 @end
 92 
 93 
 94 // ViewController.m 文件
 95 #import "ViewController.h"
 96 #import "Person.h"
 97 #import "Car.h"
 98 
 99 @implementation ViewController
100 
101 - (void)viewDidLoad {
102     [super viewDidLoad];
103     
104     Person *p = [[Person alloc] init];   // retainCount(person) = 1
105     
106     Car *car1 = [[Car alloc] init];       // retainCount(car1) = 1
107     [p setCar:car1];  // 这里对car1进行了一次retain操作,retainCount(car1) = 2
108     [car1 release];     // retainCount(car1) = 1(与Car的alloc成对)
109     [p drive];
110 
111     Car *car2 = [[Car alloc] init]; // retainCount(car2) = 1
112     [p setCar:car2]; // 这里对car1(旧值)进行了一次release操作,retainCount(car1) = 0;对car2(新值)进行了一次retain操作,retainCount(car2) = 2
113     [car2 release];  // retainCount(car2) = 1(与Car的alloc成对)
114     [p drive];
115     [p release];    // retainCount(person) = 0(与Person的alloc成对),在这里调用dealloc的时候对car2还进行了一次release操作,retainCount(car2) = 0
116     // 分析:
117     // 关键在于setter方法中,显示release旧值,然后再retain新值且完成赋值。这样做好处是,旧值替换新值,旧值没有机会调用release,会造成内存泄露问题,另外判断新旧值的判断是为防止野指针错误
118 }
119 
120 @end                                                                                                                                                          

2.autoreleasepool(自动释放池)

  自动释放池工作原理:对象存入到自动释放池中,当该自动释放池销毁时,它会对它里面的所有对象进行一次release操作

  自动释放池:@autoreleasepool { }    // 大括号代表自动释放池的作用域,大括号结束代表自动释放池被销毁

  1)autorelease的常见问题

    实例一:

1     @autoreleasepool {
2         Person *p = [[Person alloc] init];
3         [p autorelease];
4     }
5     // 分析:
6     // retain操作是即刻对retainCount-1,而autorelease只是将对象放入到自动释放池池,只有当该自动释放池被销毁时,该对象才会被release一次

    实例二:

1     Person *p = [[Person alloc] init];
2     @autoreleasepool {
3         [p autorelease];
4     }
5     // 分析:
6     // 不管你是在@autoreleasepool外部调用的对象创建方法,还是内部创建,只要你在@autoreleasepool内部调用该对象的autorelease方法,它就符合我们的内存管理原则
7     // autorelease方法必须在自动释放池内部进行

    实例三:

1     Person *p = [[Person alloc] init];
2     @autoreleasepool {
3         [p autorelease];  // 第一次加入
4         [p autorelease];  // 第二次加入
5     }
6     // 分析:
7     // 两次加入释放池,也就是该对象的内存地址被两次加入内存池,那么在自动释放池销毁时,就会向它里面的所有对象作一次release操作,第一次对p执行release操作,它的retainCount=0,此时p也就成为了野指针,再次对p执行release操作,也就                                                              导致了野指针错误,所以一定要成对出现

    实例四:

1     Person *p = [[Person alloc] init];
2     @autoreleasepool {
3         @autoreleasepool {
4             [p autorelease];
5         }
6     }
7     // 分析:
8     // 嵌套自动释放池:autorelease操作在哪一级释放池,那么对象就被加入到哪一级释放池

    实例五:

1     Person *p = [[Person alloc] init];
2     @autoreleasepool {
3         [p autorelease];
4         @autoreleasepool {
5             [p autorelease];
6         }
7     }
8     // 分析:
9     // 最里面的释放池先被销毁,它一旦释放那么它里面的对象也就被release操作,那么这个时候的指针p也就是个野指针啦,最外面的释放池被销毁时,再次对p进行一次release操作,这样也就是野指针错误啦

   2)autorelease在开发中的应用

 1 // Food.h文件
 2 #import <Foundation/Foundation.h>
 3 
 4 @interface Food : NSObject
 5 #pragma mark - 成员属性
 6 @property (nonatomic, retain) NSString *name;
 7 
 8 @end
 9 
10 
11 // Food.m文件
12 #import "Food.h"
13 
14 @implementation Food
15 
16 #pragma mark - 当前类的对象被销毁时被调用
17 - (void)dealloc
18 {
19     [super dealloc];
20     NSLog(@"Food 被销毁啦");
21 }
22 
23 @end
24 
25 
26 // Chief.h文件
27 #import <Foundation/Foundation.h>
28 #import "Food.h"
29 
30 @interface Chief : NSObject
31 
32 #pragma mark - 类方法返回对象
33 + (Chief *)chief;
34 
35 #pragma mark - 对象方法返回另外一个对象
36 - (Food *)makeFood;
37 
38 @end
39 
40 
41 // Chief.m文件
42 #import "Chief.h"
43 
44 @implementation Chief
45 
46 #pragma mark - 类方法返回对象
47 + (Chief *)chief
48 {
49     // 用self代替Chief,意在其继承的子类中调用该父类方法创建对象时,self指的是当前调用方法的类(也就是该子类),那么所创建的对象也就是该子类方法
50     Chief *chief = [[self alloc] init];
51     
52     // 如果在这里使用release,就会造成对象的提前释放
53     // [chief release];
54     
55     // 还有一点就是不可以在这个方法里面再给他套一个释放池,因为外界调用该方法完毕后,该对象就被提前释放,同样是野指针错误
56     return [chief autorelease];
57 }
58 // 分析:
59 // 1.在类方法中返回该类的对象,为防止外部出现野指针错误,我们只能用autorelease,而不能使用release
60 // 2.在类方法中不可以随意添加释放池,只需要在外界创建的释放池中调用此方法即可
61 
62 #pragma mark - 对象方法返回另外一个对象
63 - (Food *)makeFood
64 {
65     // 如果在方法中需要初始化对象,最好是初始化该指针对象为nil,否则很容易出现野指针错误
66     // 比如:Food *food;
67     // Food *food = nil;
68     Food *food = [[Food alloc] init];
69     food.name = @"郧县三合汤";
70     [food autorelease];
71     return food;
72 }
73 // 分析:
74 // NSString *string;        野指针
75 // NSString *string = nil;  空指针
76 // 另外,我们在调用NSFoundation框架的NSString、NSArray等等的类方法创建对象时,其实它内部都是进行了autorelease操作,而不是release操作,这样也是防止野指针错误
77 
78 #pragma mark - 当前类的对象被销毁时被调用
79 - (void)dealloc
80 {
81     [super dealloc];
82     NSLog(@"Chief 被销毁啦");
83 }
84 
85 @end

3.ARC模式(自动内存管理)

  ARC是iPhone 4、iOS 4.3出现之后苹果公司才推出的,ARC出现之后程序员就不用再去管理内存啦

  在ARC中,我们不可以手动调用release或是autorelease操作,也不能在对象的dealloc方法中调用[super dealloc]

  ARC模式的原则:

    a.强指针(strong)

      只要有强指针指向一个对象,那么系统就不会回收该对象

      只要没有强指针指向,系统就会立即回收该对象

      默认所有的指针都是强指针

    b.弱指针(weak)

      弱指针不会影响对象被回收

  1)ARC下的常见问题

    实例一:

 1 // Person.h文件
 2 #import <Foundation/Foundation.h>
 3 
 4 @interface Person : NSObject
 5 
 6 #pragma mark - 成员属性
 7 @property (nonatomic, copy) NSString *name;
 8 
 9 @end
10 
11 
12 // Person.h文件
13 #import "Person.h"
14 
15 @implementation Person
16 
17 #pragma mark - 对象被销毁时调用
18 - (void)dealloc
19 {
20     NSLog(@"Person 被销毁啦");
21 }
22 
23 @end
24 
25 
26 // ViewController.m文件
27 #import "ViewController.h"
28 #import "Person.h"
29 
30 @implementation ViewController
31 
32 - (void)viewDidLoad {
33     [super viewDidLoad];
34 
35     // 实例一:强指针
36     Person *p = [[Person alloc] init];  // 强指针p指向Person的对象
37     p.name = @"Frank";
38     // 分析:
39     // 只有当超过指针p的作用域之后,Person的对象才会被释放 
40 }
41 
42 @end

    实例二:

 1 // Person.h文件
 2 #import <Foundation/Foundation.h>
 3 
 4 @interface Person : NSObject
 5 
 6 #pragma mark - 成员属性
 7 @property (nonatomic, copy) NSString *name;
 8 
 9 @end
10 
11 
12 // Person.h文件
13 #import "Person.h"
14 
15 @implementation Person
16 
17 #pragma mark - 对象被销毁时调用
18 - (void)dealloc
19 {
20     NSLog(@"Person 被销毁啦");
21 }
22 
23 @end
24 
25 
26 // ViewController.m文件
27 #import "ViewController.h"
28 #import "Person.h"
29 
30 @implementation ViewController
31 
32 - (void)viewDidLoad {
33     [super viewDidLoad];
34 
35     // 实例二:强指针
36     Person *p = [[Person alloc] init];  // 强指针p指向Person的对象
37     p = nil;   // 断开强指针p的指向,没有强指针指向Person对象,Person对象会被立即回收
38 }
39 
40 @end

    实例三:

 1 // Person.h文件
 2 #import <Foundation/Foundation.h>
 3 
 4 @interface Person : NSObject
 5 
 6 #pragma mark - 成员属性
 7 @property (nonatomic, copy) NSString *name;
 8 
 9 @end
10 
11 
12 // Person.h文件
13 #import "Person.h"
14 
15 @implementation Person
16 
17 #pragma mark - 对象被销毁时调用
18 - (void)dealloc
19 {
20     NSLog(@"Person 被销毁啦");
21 }
22 
23 @end
24 
25 
26 // ViewController.m文件
27 #import "ViewController.h"
28 #import "Person.h"
29 
30 @implementation ViewController
31 
32 - (void)viewDidLoad {
33     [super viewDidLoad];
34 
35     // 实例三:强指针
36     Person *p = [[Person alloc] init];  // 强指针p指向Person的对象
37     Person *p1 = p;                     // 强指针p1指向Person的对象
38     p = nil;          // 断开强指针p的指向
39     // 分析:
40     // 在p = nil之后,还有一个强指针p1指向Person对象,所以Person对象还是不会被释放
41 }
42 
43 @end

    实例四:

 1 // Person.h文件
 2 #import <Foundation/Foundation.h>
 3 
 4 @interface Person : NSObject
 5 
 6 #pragma mark - 成员属性
 7 @property (nonatomic, copy) NSString *name;
 8 
 9 @end
10 
11 
12 // Person.h文件
13 #import "Person.h"
14 
15 @implementation Person
16 
17 #pragma mark - 对象被销毁时调用
18 - (void)dealloc
19 {
20     NSLog(@"Person 被销毁啦");
21 }
22 
23 @end
24 
25 
26 // ViewController.m文件
27 #import "ViewController.h"
28 #import "Person.h"
29 
30 @implementation ViewController
31 
32 - (void)viewDidLoad {
33     [super viewDidLoad];
34 
35     // 实例四:强指针
36     Person *p = [[Person alloc] init];  // 强指针p指向Person的对象
37     [self test:p];                      // 相当于 Person *p1 = p;
38     // 分析:
39     // 在内部函数中好像是p1 = p,然后将p1 = nil,相当于断开强指针p对Person对象的指向,实则不是,p1和p完全是两个不同的强指针,p1 = nil操作也只是断开强指针p1对Person对象的指向
40 }
41 
42 #pragma mark - 内部函数
43 - (void)test:(Person *)p1
44 {
45     p1 = nil;    // 断开强指针p1指向Person对象
46 }
47 
48 @end

    实例五:

 1 // Person.h文件
 2 #import <Foundation/Foundation.h>
 3 
 4 @interface Person : NSObject
 5 
 6 #pragma mark - 成员属性
 7 @property (nonatomic, copy) NSString *name;
 8 
 9 @end
10 
11 
12 // Person.h文件
13 #import "Person.h"
14 
15 @implementation Person
16 
17 #pragma mark - 对象被销毁时调用
18 - (void)dealloc
19 {
20     NSLog(@"Person 被销毁啦");
21 }
22 
23 @end
24 
25 
26 // ViewController.m文件
27 #import "ViewController.h"
28 #import "Person.h"
29 
30 @implementation ViewController
31 
32 - (void)viewDidLoad {
33     [super viewDidLoad];
34     
35     // 实例五:弱指针
36     __weak Person *p = [[Person alloc] init];  // 对象创建完毕即刻被释放,因无强指针指向
37     // 修饰弱指针就是用__weak,另外没有人回去在创建对象时,用弱指针去接收,这样就会导致对象创建完毕即被释放,多此一举
38 }
39 
40 @end

 

四、实际项目中的常见内存问题

1.MRC转ARC

  本想使用MRC机制装一下逼,结果发现根本装不下去,就只好知难而退啦,转成ARC机制

  Xcode中操作:Edit  ---> Convert ---> To Objective - C ARC... ---> 弹出对话框,选中该项目下所有文件 ---> 点击"Check" ---> 点击"Next" ---> 点击"Save"

  我们可以看到这样操作之后,我们可以发现我们项目中有关MRC手动管理内存的代码都被修改啦,而且项目中设置MRC机制的参数也被改为Yes啦

2.MRC与ARC共存

  原有的一些第三方库是MRC模式下的,但是年久失修,需要将它导入到ARC机制下的项目中

  Xcode中操作:选中"TARGETS" ---> 选中"Build Phases" ---> 打开"Complie Sources(XXX items)" ---> 找到你需要导入的第三方库的所有文件,双击弹出文本框,键入"-fno-objc-arc"

 3.循环引用

  在OC中经常会出现一些互相引用的问题,互相引用很容易造成循环引用。

  比如:类Person有一个成员属性Car,而Car中也有一个成员属性Person,那么我们需要做到:

    1)一方的头文件引用中,需要使用@class,而不是#import,否则就会造成循环引用

    2)在相同的一方中设置成员属性的参数时,使用weak,不可以同时使用strong,否则就是循环强引用,造成内存泄露问题

 

五、@property参数

  例:@property Car *car;

    1)生成get和set方法的声明

    2)生成get和set方法的简单实现,不能满足我们在内存管理中的需求

    3)如果我们没有声明同名的成员变量,会自动帮我们生成下划线开头的私有成员变量

  所以为满足我们在内存管理中的需求,iOS给我们的@property添加了四种类型的参数,以供我们自己选择

1.与set方法内存管理相关的参数

  retain:生成符合内存管理原则的set方法(MRC机制下,通常用于对象类型)

  assign:set方法中是直接赋值(MRC、ARC机制下通用,通常用于基本数据类型,MRC机制下的对象类型)

  copy:拷贝(MRC、ARC机制下通用,通常用于NSString类型,block类型)

  strong:对应手动内存管理中的retain,强指针(ARC机制下,通常应用在对象类型上)

  weak:对应手动内存管理中的assign,弱指针(ARC机制下,通常应用在对象类型上)

2.多线程相关

  nonatomic:set方法不生成多线程管理的代码,也就是不生成线程锁,运行效率相对于atomic要高(实际开发中使用这个)

  atomic:set方法生成多线程管理的代码,也就是生成线程锁(默认就是这个)

3.是否要生成set方法和get方法

  readwrite:可读可写,同是生成get方法和set方法

  readonly:只读,仅仅生成get方法

4.与set、get方法名称相关的参数

  setter:设置生成set方法的名字(给set方法重命名)

  getter:设置生成get方法的名字(给get方法重命名) 

posted @ 2016-01-14 17:06  Frank9098  阅读(111)  评论(0)    收藏  举报