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方法重命名)

浙公网安备 33010602011771号