首页 > 代码库 > Obj-C内存管理初级

Obj-C内存管理初级

内存管理初级


为什么要管理内存

  我们的iOS APP 出现Crash(闪退),90%以上的原因是内存问题。我们使用Xcode编译运行程序时常见到的一个EXC_BAD_ACCESS问题就是个典型的内存错误--访问了一块僵尸内存,当然这里不对僵尸内存进行深入讨论。那么内存问题主要体现在哪些方面呢,那就是“内存溢出”和“野指针异常”。

 

  内存溢出

  iOS给每个应?程序提供了?定的内存,?于程序的运?。iPhone 3GS内存30M左右,iPhone 5S 内存80M左右。?旦超出内存上限,程序就会Crash。

  程序中最占内存的就是图?、?频、视频等资源?件。3.5??Retina屏幕(320*480)放?张全屏图?,占?字节数320*480*4(?个像素占4个字节,存放RGBA),即:600k Bytes。iPhone 3GS同时读取60张图?就会crash。

  4?屏幕(320*568),实际像素640*1136,程序存放?张全屏图?,占?字节数640*1136*4,即2.77M Bytes。iPhone 5S同时读取40张图?就会crash。

  所以说初学者在开发App时,使用图片一定要谨慎,注意性能。

 

  野指针异常

  对象内存空间已经被系统回收,仍然使?指针操作这块内存。野指针异常是程序crash的主要原因。代码量越?的程序,越难找出出现野指针的位置。

内存管理主要方式介绍

垃圾回收(gc):java;

  在垃圾回收机制下程序员只需要开辟内存空间,不需要?代码显?地释放,系统来判断哪些空间不再被使?,并回收这些内存空间,以便再次分配。整个回收的过程不需要写任何代码,由系统?动完成垃圾回收。Java开发中?直使?的就是垃圾回收技术。

  OC中有也gc机制,但是在IOS和MAC OS 10.4以后被苹果禁用。

人工引用计数MRC(Manual Reference Count):

  内存开辟和释放都由程序代码控制。相对垃圾回收来说,对内存的控制更加灵活,可以在需要释放的时候及时释放,对程序员的要求较?,程序员要熟悉内存管理的机制。

特点:相对gc内存控制更灵活,对程序员要求高

自动引用计数ARC ( Auto Reference Count ) :

  iOS 5.0 的编译器特性,允许用户只开辟空间,不用释放空间。但ARC不是垃圾回收!它的本质还是MRC,只是编译器帮程序员默认加了释放(release)的代码。


OC内存管理机制

  在C语言中,我们使用malloc和free来管理堆内存的创建和释放。堆内存也只有正在使用和销毁这两种状态。然而在实际的开发中,我们可能会遇到两个以上的指针指向同一块内存,这时C语言就无法记录这一块内存使用者的个数。Obj-C中采用的引用计数机制则很好地解决了这个问题。

  OC采?引?计数机制管理内存,当?个新的引?指向对象时,引?计数器就递增,当去掉?个引?时,引?计数就递减。当引?计数到零时,该对象就将释放占有的资源。

影响引用计数的方法

+ alloc :开辟内存空间,让被开辟的内存空间的引用计数+1,这是0到1的过程。

(只有在见到alloc的时候才会开辟内存空间),

在类方法中无法使用实例变量、无法调用实例方法,因为还没有开辟内存空间

- retain :引用计数加1, 返回对象本身

- copy :把某?内存区域的内容拷??份,拷?到新的内存空间?去,原被拷?区域的引?计数不变,新的内存区域的引?计数为1。

- release : 引?计数减1。release之后配合nil使用可以安全释放。

- dealloc : 继承自父类的方法,当对象引用计数为0时,由对象自动调用。

- autorelease :在autoreleasepool中,不立即执行retain减1。

以下是个代码实例demo,只是为了列出知识点,如果不注释一些代码块可能无法运行:

main.m

#import <Foundation/Foundation.h>#import "Person.h"#import "Student.h"#define RELEASE_SAFE(_point) [_point release];_point = nil;int main(int argc, const char * argv[]) {    //自动释放池    //是一种栈结构,每当遇到一个autorelease就将他入栈    //当出了自动释放池,将栈中所有对象引用计数减一    @autoreleasepool {             //p1的引用计数为1        Person *p1 = [[Person alloc] init];        NSLog(@"%ld",[p1 retainCount]);                //retain        Person *p2 = [p1 retain];        NSLog(@"%ld",[p2 retainCount]);//        Person *p3 = [p1 copy];//        NSLog(@"%ld",[p3 retainCount]);        [p1 release];        NSLog(@"%ld",[p1 retainCount]);        [p2 release];        //retainCount不记录0        NSLog(@"%ld",[p2 retainCount]);        //引用计数为1        Person *p1 = [[Person alloc] init];        //引用计数为2        Person *p2 = [p1 retain];        //引用计数为1        [p1 release];        //安全释放        p1 = nil;        //引用计数为0        //这个release没有效果        [p1 release];        //利用带参宏便捷,安全地释放空间        RELEASE_SAFE(p2);           Person *p1 = [[Person alloc] init];        Person *p2 = [[Person alloc] init];        p1.name = @"庞麦龙";        p1.books = @[@"十万个冷笑话",@"尸兄"];        //        [p1 retain];                //autorelease不会立即对引用计数减一        //离开自动释放池后,引用计数减一        [p1 autorelease];        [p2 autorelease];//        NSLog(@"%ld",[p1 retainCount]);             }    //凡是一次alloc,配套一次release或者autorelease    //凡是一次retain,配套一次release或者autorelease    //凡是一次copy,配套一次release或者autorelease    //谁污染谁治理     Person *p1 = [[Person alloc] init];    Person *p2 = [p1 copy];        p1.name = @"如花";    p2.name = @"铁柱";       NSLog(@"%@",p2.name);    RELEASE_SAFE(p2);    RELEASE_SAFE(p1);    [p1 copy];      Student *s1 = [[Student alloc] init];    s1.name =@"xuesheng";    s1.age = 1;    Person *s2 = [s1 copy];    s2.name = @"taibao";    NSLog(@"%@",s1.name);    RELEASE_SAFE(s2);    RELEASE_SAFE(s1);   /*     *不建议使用实例对象的方式建立AutoreleasePool     */    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];    //池区,无作用域问题,autorelease的对象需要使用安全释放    Person *p1 = [[Person alloc] init];    [p1 autorelease];    [pool release];    pool = nil;    p1 = nil;    return 0;}

Person.h

#import <Foundation/Foundation.h>@interface Person : NSObject<NSCopying>@property(nonatomic,copy)NSString *name;@property(nonatomic,retain)NSArray *books;@end

Person.m

#import "Person.h"@implementation Person//销毁对象- (void)dealloc{    //当对象引用计数为0时,自动调用dealloc方法    NSLog(@"啊,I‘m over!");    //在一个对象销毁之前,清楚自己实例变量指向的内存    [_name release];    [_books release];        //最后销毁从父类继承的东西    [super dealloc];}- (id)copyWithZone:(NSZone *)zone{    //浅拷贝//    return [self retain];        //深拷贝    Person *p = [[Person allocWithZone:zone] init];    p.name = [NSString stringWithFormat:@"%@",self.name];    p.books = [NSArray arrayWithArray:self.books];    return p;}@end

Student.h

#import <Foundation/Foundation.h>@interface Student : NSObject<NSCopying>@property(nonatomic,copy)NSString *name;@property(nonatomic,assign)NSInteger age;@end

Student.m

#import "Student.h"@implementation Student- (void)dealloc{    [_name release];    [super dealloc];}- (id)copyWithZone:(NSZone *)zone{    Student *s = [[Student allocWithZone:zone] init];    s.name = [NSString stringWithFormat:@"%@",_name];    s.age = _age;    return s;}@end

 

 

autoreleasepool是一个栈空间


内存管理原则

凡是一次alloc,配套一次release或者autorelease

凡是一次retain,配套一次release或者autorelease

凡是一次copy,配套一次release或者autorelease

谁污染谁治理

Obj-C内存管理初级