• 【OC基础】03-OC内存管理


    概述

    跟Java和C#类似,OC创建的对象在堆上。与Java和C#不同的是Java和C#有垃圾回收机制,所以不需要程序员手动释放堆上的内存。而OC没有垃圾回收机制,必须手动管理内存的创建和释放。下面介绍一下OC内存管理内存管理的方式。

    引用计数器

    OC管理内存的方式类似C++中的智能指针,创建一个对象时,在ObjC中每个对象内部都有一个与之对应的整数(retainCount),叫“引用计数器”,当一个对象在创建之后初始化它的引用计数器为1,当调用这个对象的alloc、retain、new、copy方法之后引用计数器自动在原来的基础上加1(ObjC中调用一个对象的方法就是给这个对象发送一个消息),当调用这个对象的release方法之后它的引用计数器减1,如果一个对象的引用计数器为0,则系统会自动调用这个对象的dealloc方法来销毁这个对象。

    Student.h文件:

    1 #import <Foundation/Foundation.h>
    2 
    3 @interface Student : NSObject
    4 
    5 #pragma mark - 属性
    6 @property (nonatomic,copy)NSString *name;
    7 @property (nonatomic,assign)float age;
    8 
    9 @end

    Student.m文件:

    1 #import "Student.h"
    2 
    3 @implementation Student
    4 
    5 -(void)dealloc{
    6     NSLog(@"Invoke Student dealloc method");
    7     [super dealloc];
    8 }
    9 @end

    main.m文件:

     1 #import <Foundation/Foundation.h>
     2 #import "Student.h"
     3 int main(int argc, const char * argv[])
     4 {
     5 
     6     @autoreleasepool {
     7         
     8         // insert code here...
     9         Student *stu = [[Student alloc]init];
    10         stu.name=@"lisi";
    11         stu.age=12;
    12         NSLog(@"retainCount=%lu",[stu retainCount]);
    13         //结果为1
    14         [stu retain];
    15         NSLog(@"retainCount=%lu",[stu retainCount]);
    16         //结果为2
    17         [stu release];
    18         NSLog(@"retainCount=%lu",[stu retainCount]);
    19         //结果为1
    20         [stu release];
    21         stu=nil;
    22         [stu release];
    23     }
    24     return 0;
    25 }

    我们可以看出,在创建一个对象之后,初始化引用计数器为1,retain之后,计数器增加1,然后每release一次,计数器都会减1,直到这个对象被释放。

    内存释放的原则

    对象之间引用可以是复杂的,有时候会出现相互引用既循环引用的情况,所以对象的释放不能紧紧依靠自动化来完成。内存释放的原则是谁创建,谁释放。如下:

    Book.h:

    1 #import <Foundation/Foundation.h>
    2 
    3 @interface Book : NSObject
    4 @property (nonatomic,copy) NSString *bookName;
    5 -(void)read;
    6 -(id)initWithName:(NSString*) bname;
    7 @end

    Book.m

     1 #import "Book.h"
     2 
     3 @implementation Book
     4 -(void)read{
     5     NSLog(@"now read this book %@...",self.bookName);
     6 }
     7 -(id)initWithName:(NSString*) bname{
     8     [super init];
     9     _bookName=bname;
    10     return self;
    11 }
    12 -(void)dealloc{
    13     NSLog(@"Invoke book %@ dealloc method...",self.bookName);
    14     [super dealloc];
    15 }
    16 @end

    Student.h

     1 #import <Foundation/Foundation.h>
     2 #import "Book.h"
     3 @interface Student : NSObject{
     4     Book *_book;
     5 }
     6 
     7 #pragma mark - 属性
     8 @property (nonatomic,copy)NSString *name;
     9 @property (nonatomic,assign)float age;
    10 -(id)initWithNameAndAge:(NSString*)sname andAge:(float) sage;
    11 -(void)setBook:(Book*)book;
    12 -(Book*)book;
    13 @end

    Student.m

     1 #import "Student.h"
     2 
     3 @implementation Student
     4 
     5 -(id)initWithNameAndAge:(NSString *)sname andAge:(float)sage{
     6     self.name=sname;
     7     self.age=sage;
     8     return self;
     9 }
    10 -(void)setBook:(Book *)book{
    11     if (_book!=book) {
    12         [_book release];
    13         _book = [book retain];
    14     }
    15 }
    16 
    17 -(Book*)book{
    18     return _book;
    19 }
    20 
    21 -(void)dealloc{
    22     NSLog(@"Invoke Student dealloc method");
    23     [_book release];
    24     [super dealloc];
    25 }
    26 @end

    main.m

     1 #import <Foundation/Foundation.h>
     2 #import "Student.h"
     3 int main(int argc, const char * argv[])
     4 {
     5 
     6     @autoreleasepool {
     7         
     8         // insert code here...
     9         Student *stu = [[Student alloc]initWithNameAndAge:@"sili" andAge:12];
    10       
    11         Book *book1 = [[Book alloc]initWithName:@"AAA"];
    12         [book1 release];
    13         [stu.book read];
    14         
    15         Book *book2 = [[Book alloc]initWithName:@"BBB"];
    16         [stu setBook:book2];
    17         [book2 release];
    18         [stu.book read];
    19         [stu release];
    20     }
    21     return 0;
    22 }

    运行结果:

    1 2015-05-03 11:59:24.939 first[1814:303] Invoke book AAA dealloc method...
    2 2015-05-03 11:59:24.941 first[1814:303] now read this book BBB...
    3 2015-05-03 11:59:24.942 first[1814:303] Invoke Student dealloc method
    4 2015-05-03 11:59:24.942 first[1814:303] Invoke book BBB dealloc method...

    属性参数

    其实我们可以直接利用OC提供的属性来达到同样的效果:

    @property (nonatomic,retain)Book *book;

    属性包含的所有参数和用法如下:

    如果不进行设置或者只设置其中一类参数,程序会使用三类中的各个默认参数,默认参数:(atomic,readwrite,assign)。一般情况下如果是基本类型使用assign,非字符串对象使用retain,字符串对象使用copy。原子性和读写属性根据需要设置即可。

    自动释放池

    自动内存释放使用@autoreleasepool关键字声明一个代码块,如果一个对象在初始化时调用了autorelase方法,那么当代码块执行完之后,在块中调用过autorelease方法的对象都会自动调用一次release方法。

     1 #import <Foundation/Foundation.h>
     2 #import "Student.h"
     3 int main(int argc, const char * argv[])
     4 {
     5 
     6     @autoreleasepool {
     7         
     8         // insert code here...
     9         Student *stu = [[Student alloc]initWithNameAndAge:@"lisi" andAge:12];
    10         Student *stu1 = [[[Student alloc]initWithNameAndAge:@"wanger" andAge:14]autorelease];
    11         [stu1 retain];
    12         [stu autorelease];
    13     }
    14     return 0;
    15 }

    打印结果:

    1 2015-05-03 12:27:39.949 first[1938:303] Invoke Student dealloc lisi method

    我们可以看出,stu对象被完全释放,而stu1没有被释放,这是因为每个对象的release方法只被调用了一次,而stu1的引用计数为2,所以在代码块结束的时候stu1对象不会被释放(造成了内存泄露)。对于自动内存释放有以下几点需要注意:

    1. autorelease方法不会改变对象的引用计数器,只是将这个对象放到自动释放池中;
    2. 自动释放池实质是当自动释放池销毁后调用对象的release方法,不一定就能销毁对象(如果对象的引用计数器>1,就无法销毁);
    3. 由于自动释放池最后统一销毁对象,因此如果一个操作比较占用内存(对象比较多或者对象占用资源比较多),最好不要放到自动释放池或者考虑放到多个自动释放池;
    4. ObjC中类库中的静态方法一般都不需要手动释放,内部已经调用了autorelease方法;
  • 相关阅读:
    CentOS7 64位下MySQL5.7安装与配置(YUM)
    在windows 7中vagrant up 无反应,没任何信息输出
    vagrant在windows下的安装和配置
    html中嵌入flvplayer.swf播放器,播放视频
    FileItem 出现部分中文乱码解决办法
    华为P6-C00电信版,刷机总是失败? FAIL
    MyEclipse发布按钮无效的办法
    Ubuntu 下建立WiFi热点的方法
    Android系统源码学习步骤
    android源代码在线阅读
  • 原文地址:https://www.cnblogs.com/desheng-Win/p/4474529.html
Copyright © 2020-2023  润新知