常见的内存问题有以下几种:
1.野指针异常:访问没有所有权的内存,如果想要安全的访问,必须确保空间还在
2.内存泄露:空间使用完之后没有及时释放
3.过度释放:对同一块存储空间释放多次,立刻crash
4.内存溢出:所有存储空间被占用
管理内存的三种方式:
1.垃圾回收机制:程序员只需要开辟存储空间,系统会自动回收内存,Java采用该机制
2.MRC:手动引用计数机制,由开发人员开辟空间,手动添加影响引用计数增加或减少的方法,能够灵活的控制空间何时释放。
3.ARC:自动引用计数机制,是iOS5.0退出的,基于MRC,不需要程序员手动添加管理内存的代码,编译器会在合适的位置自动添加管理内存的代码。
MRC和ARC都是采用引用计数来管理对象内存的
// 引用计数机制:
iOS采用引用计数来管理内存,当你想拥有这个对象的时候,需要使该对象的引用计数+1,使用完这个对象的时候,使该对象的引用计数-1,当对象的引用计数为0的时候,表示没有任何对象对该对象持有,那么这时候系统会自动调用dealloc方法来回收该对象的存储空间。
// 影响引用计数的方法
1.使用引用计数+1的方法:alloc,retain,copy
2.使用引用计数-1的方法:release,autorelease
下面我们创建一个Person类,还有一个结婚协议(顺道说下协议的问题)
Person.h
1 #import <Foundation/Foundation.h> 2 #import "MarryProtocol.h" // 引入协议所在的.h文件 3 4 // 让Person类遵守MarryProtocol协议 5 @interface Person : NSObject <MarryProtocol, NSCopying> 6 7 @property (nonatomic, retain) NSString *name; 8 @property (nonatomic, retain) NSString *gender; 9 10 @end
Person.m
1 #import "Person.h" 2 3 @implementation Person 4 5 // 重写dealloc方法 6 // dealloc相当于临终遗言 7 - (void)dealloc 8 { 9 10 NSLog(@"该对象%@被销毁", self); 11 [super dealloc]; // 让父类回收存储空间,这句话通常写在最后 12 } 13 14 15 - (NSString *)description 16 { 17 return [NSString stringWithFormat:@"name = %@, gender = %@", _name, _gender]; 18 } 19 20 21 // 实现协议中的copyWithZone:方法 22 //- (id)copyWithZone:(NSZone *)zone { 23 // 24 // // 伪拷贝:拷贝地址,相当于retain,引用计数+1 25 // return [self retain]; 26 // 27 //} 28 29 //- (id)copyWithZone:(NSZone *)zone { 30 // 31 // // 浅拷贝:对象开辟新的空间,但两个对象的实例变量指向同一块存储空间(zone是系统申请的内存池) 32 // Person *person = [[Person allocWithZone:zone] init]; 33 // person.name = self.name; 34 // person.gender = self.gender; 35 // 36 // return person; 37 //} 38 39 - (id)copyWithZone:(NSZone *)zone { 40 41 // 深拷贝:对象开辟新的存储空间,并且对象的实例变量指向不同的存储空间(zone是系统申请的内存池) 42 Person *person = [[Person allocWithZone:zone] init]; 43 person.name = [self.name mutableCopy]; 44 person.gender = [self.gender mutableCopy]; 45 return person; 46 } 47 48 49 // 实现协议中的方法 50 51 // 不能家暴 52 - (void)noViolence { 53 NSLog(@"不能家暴"); 54 } 55 56 // 不能出轨 57 - (void)noDerail { 58 NSLog(@"不能出轨"); 59 } 60 61 // 不能抽大烟 62 - (void)noSmoking { 63 NSLog(@"不能抽烟"); 64 } 65 66 // 不能藏私房钱 67 - (void)noSelfMoney { 68 NSLog(@"不能藏私房钱"); 69 } 70 71 72 // 做家务(可实现也可以不实现) 73 - (void)doHousework { 74 NSLog(@"做家务"); 75 } 76 77 78 @end
MarryProtocol.h(结婚协议)
1 #import <Foundation/Foundation.h> 2 3 // 定义一个协议:@protocol...@end 4 @protocol MarryProtocol <NSObject> 5 6 // 定义方法 7 8 @required // 指定协议中的方法必须遵守 9 // 不能家暴 10 - (void)noViolence; 11 12 // 不能出轨 13 - (void)noDerail; 14 15 // 不能抽大烟 16 - (void)noSmoking; 17 18 // 不能藏私房钱 19 - (void)noSelfMoney; 20 21 22 @optional // 方法可以实现也可以不实现 23 // 做家务 24 - (void)doHousework; 25 26 @end
main.m
1 #import <Foundation/Foundation.h> 2 #import "Person.h" 3 4 int main(int argc, const char * argv[]) { 5 @autoreleasepool { 6 7 // alloc的作用:开辟空间,让对象的引用计数由0变成1 8 Person *person = [[Person alloc] init]; 9 10 // 查看对象的引用计数retainCount 11 // retainCount是MRC才有的机制,所以如果使用的话,需要将ARC转化为MRC 12 NSUInteger count = person.retainCount; // 1 13 NSLog(@"%lu", count); 14 15 16 // retain 使对象的引用计数+1 17 [person retain]; // 2 18 NSLog(@"%lu", person.retainCount); 19 20 21 // release 使对象的引用计数-1 22 [person release]; // 1 23 NSLog(@"%lu", person.retainCount); 24 25 [person release]; // 0 当对象的引用计数为0,系统自动调用dealloc方法 26 person = nil; // 对象置为nil,防止野指针异常 27 28 // 如果对象已经被回收,当前代码无效 29 // 开启僵尸模式检查野指针异常 30 // 如果一个对象被销毁,这个对象就称为僵尸对象 31 NSLog(@"%lu", person.retainCount); 32 // 这句话默认情况下不报错,如果要报错,需要开启僵尸模式 33 34 35 // autorelease 36 Person *person1 = [[Person alloc] init]; // 1 37 Person *person2 = [person1 retain]; // 2 38 NSLog(@"person1 = %lu, person2 = %lu", person1.retainCount, person2.retainCount); 39 40 Person *person3 = [person1 retain]; // 3 41 NSLog(@"person1 = %lu, person2 = %lu, person3 = %lu", person1.retainCount, person2.retainCount, person3.retainCount); 42 43 [person3 release]; // 2 立刻-1 44 NSLog(@"person1 = %lu, person2 = %lu, person3 = %lu", person1.retainCount, person2.retainCount, person3.retainCount); 45 46 47 [person2 autorelease]; // 在未来的某个时刻-1 48 NSLog(@"person1 = %lu, person2 = %lu, person3 = %lu", person1.retainCount, person2.retainCount, person3.retainCount); 49 50 51 // 自动释放池 52 // 定义一个自动释放池 53 // 第一种形式 54 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 55 NSLog(@"%lu", pool.retainCount); 56 57 Person *p1 = [[Person alloc] init]; // 1 58 NSLog(@"%lu", p1.retainCount); 59 60 [p1 retain]; // 2 61 62 [p1 autorelease]; // 2 63 NSLog(@"%lu", p1.retainCount); 64 65 [pool release]; // 销毁自动释放池 66 NSLog(@"%lu", p1.retainCount); // 1 67 68 69 // 对象使用autorelease是在未来的某一个时刻让对象的引用计数-1,这个某一时刻指的是喷到自动释放池之后才会释放 70 71 72 // 第二种形式 (在iOS5.0之后推荐使用的) 73 @autoreleasepool { 74 Person *p2 = [[Person alloc] init]; // 1 75 p2.name = @"p2"; 76 [p2 autorelease]; // 0 77 78 Person *p3 = [[Person alloc] init]; 79 p3.name = @"p3"; 80 //[p3 autorelease]; 81 } 82 83 84 // 自动释放池的作用:自动释放池会在销毁之前检查内部有没有autorelease操作,如果有autorelease操作,让该对象的引用计数做一次-1操作 85 86 87 // 内存管理原则: 88 // 凡是使用alloc,retain让对象的引用计数+1,相应的就该使用release或者autorelease让对象的引用计数-1,也就是说增加的次数要和减少的次数相等,才能保证对象的引用计数最终为0,对象才会被销毁。 89 90 91 // 增加次数和减少次数不一致,会出现内存问题 92 93 // 1.内存泄露:增加的次数大于减少的次数 94 // 情况1 95 Person *per1 = [[Person alloc] init]; 96 97 // 情况2 98 Person *per2 = [[Person alloc] init]; 99 [per2 retain]; 100 [per2 release]; 101 102 // 情况3 103 Person *per3 = [[Person alloc] init]; 104 per3 = nil; 105 [per3 release]; 106 107 108 // 2.过度释放:增加的次数小于减少的次数 109 Person *q = [[Person alloc] init]; 110 [q retain]; 111 [q release]; 112 [q release]; 113 [q release]; // 过度释放 114 115 116 // 3.野指针异常:增加的次数等于减少的次数,还将继续访问 117 Person *x = [[Person alloc] init]; 118 [x retain]; 119 [x release]; 120 [x release]; 121 //x = nil; // 没有这一句,就会野指针异常 122 NSLog(@"%lu", x.retainCount); 123 124 125 // 并不是所有的对象都能进行copy操作,只有遵循NSCopying协议并实现copyWithZone:方法的对象才能进行copy 126 Person *pe = [[Person alloc] init]; 127 pe.name = @"hhh"; 128 [pe retain]; 129 NSLog(@"%lu", pe.retainCount); 130 Person *pe1 = [pe copy]; 131 NSLog(@"%lu, %lu", pe.retainCount, pe1.retainCount); 132 NSLog(@"%p, %p", pe.name, pe1.name); 133 134 135 // copy和mutableCopy 136 // copy出来的指针副本是不可变的 137 NSString *str1 = @"詹姆斯"; 138 NSString *str2 = [str1 copy]; 139 NSLog(@"str1 = %p, str2 = %p", str1, str2); 140 141 NSMutableString *mStr1 = [str1 copy]; 142 NSLog(@"mStr1 = %p", mStr1); 143 144 // [mStr1 appendString:@"威武"]; 145 // NSLog(@"%@", mStr1); 146 147 148 // mutableCopy拷贝出来的内容是可变的 149 NSString *str3 = [str1 mutableCopy]; 150 NSLog(@"str3 = %p, str1 = %p", str3, str1); 151 152 153 NSMutableString *mStr2 = [str1 mutableCopy]; 154 [mStr2 appendString:@"威武"]; 155 NSLog(@"%@", mStr2); 156 157 158 // NSArray *array = @[@"1", @"2", @"3"]; 159 // NSMutableArray *mArray = [array mutableCopy]; 160 161 162 // 深拷贝和浅拷贝 163 // 如果是深拷贝,那么成员对象的地址和之前的地址不同,如果是浅拷贝,成员变量的地址和之前的相同 164 165 } 166 return 0; 167 }