zoukankan      html  css  js  c++  java
  • Objective-C基础笔记(3)OC的内存管理

    Objective-C的内存基本管理

    在OC中每一个变量都保存着引用计数器,当这个对象的引用计数器为0的时候该对象会被回收。当使用alloc、new或者copy创建一个对象的时候,对象的引用计数器被置为1.

    给对象发送一条retain消息,能够使引用计数器+1.

    给对象发送一条release消息,能够使引用计数器-1.

    当OC被销毁的时候会发送一条dealloc消息(不要直接调用,由系统调用),能够重写dealloc方法。在该方法中释放相关资源。

    能够给对象发送retainCount消息获取对象的当前引用计数器。

    首先我们新建一个project


    接下来将project的设置里面将ARC禁掉


    Book.h文件

    #import <Foundation/Foundation.h>
    
    @interface Book : NSObject
    
    @property float price;
    
    - (id)initBook:(float)price;
    
    @end
    Book.m文件

    #import "Book.h"
    
    @implementation Book
    
    @synthesize price = _price;
    
    //构造函数
    - (id)initBook:(float)price {
        if(self = [super init]){
            _price = price;
        }
        NSLog(@"价格是%f的书购买了", _price);
        return self;
    }
    
    //析构函数
    - (void)dealloc {
        NSLog(@"价格为%f的书被释放了", _price);
        [super dealloc];
    }
    
    @end
    Student.h文件

    #import <Foundation/Foundation.h>
    #import "Book.h"
    
    @interface Student : NSObject
    
    @property int age;
    @property Book *book;
    
    - (void)setBook:(Book *)book;
    
    - (id)initStudent:(int)age;
    
    @end
    Student.m文件

    #import "Student.h"
    #import "Book.h"
    
    @implementation Student
    
    @synthesize age = _age;
    @synthesize book = _book;
    
    - (void)setBook:(Book *)book {
        if(_book != book){
            //先对原来的书计数器减一
            //假设之前为nil不会出错(和java中的空指针不同)
            [_book release];
            [book retain];
            _book = book;
        }
    }
    
    //构造函数
    - (id)initStudent:(int)age {
        if(self = [super init]) {
            _age = age;
        }
        NSLog(@"年龄为%d的学生被创建了", _age);
        return self;
    }
    
    //析构函数
    - (void)dealloc{
        [_book release];
        NSLog(@"年龄为%d的学生被释放了", _age);
        [super dealloc];
    }
    
    @end
    
    main.m文件

    #import <Foundation/Foundation.h>
    #import "Student.h"
    #import "Book.h"
    
    void buyBook(Student *stu) {
        Book *book1 = [[Book alloc] initBook:101.5]; //谁创建谁释放
        stu.book = book1;
        [book1 release];
        Book *book2 = [[Book alloc] initBook:98.5];
        stu.book = book2;
        [book2 release];
    }
    
    void readBook(Student *stu) {
        NSLog(@"年龄是%i的学生在读价格为%f的书", stu.age, stu.book.price);
    }
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            //计数器为1
            Student *stu = [[Student alloc] initStudent:21];
            //买书
            buyBook(stu);
            //看书
            readBook(stu);
            //计数器清0,释放内存
            [stu release];
        }
        return 0;
    }
    
    输出结果:

    2014-11-13 23:11:19.510 内存管理[698:46519] 年龄为21的学生被创建了

    2014-11-13 23:11:19.512 内存管理[698:46519] 价格是101.500000的书购买了

    2014-11-13 23:11:19.512 内存管理[698:46519] 价格是98.500000的书购买了

    2014-11-13 23:11:19.512 内存管理[698:46519] 价格为101.500000的书被释放了

    2014-11-13 23:11:19.512 内存管理[698:46519] 年龄是21的学生在读价格为98.500000的书

    2014-11-13 23:11:19.512 内存管理[698:46519] 价格为98.500000的书被释放了

    2014-11-13 23:11:19.512 内存管理[698:46519] 年龄为21的学生被释放了

    @classkeyword

    通常引用一个类有两种方法。一种是通过#import,还有一种是通过@class.

    #import 的方式会将头文件里的全部信息引入。

    @class 的方式仅仅是说明它是一个类(假如仅仅是声明一个类就不用使用#import).

    #import <Foundation/Foundation.h>
    
    @class Book; //声明Book是一个类
    
    @interface Student : NSObject {
        Book *_book;
    }
    
    @property int age;
    @property Book *book;
    
    - (void)setBook:(Book *)book;
    
    - (id)initStudent:(int)age;
    
    @end
    另外。Student.m中的析构函数我们能够做例如以下改动

    //析构函数
    - (void)dealloc{
        self.book = nil; //调用setter方法
        [_book release];
        NSLog(@"年龄为%d的学生被释放了", _age);
        [super dealloc];
    }
    self.book = nil; 会调用setter方法。释放对象并将当前类Student的属性_book设为nil.

    @property的參数

    @property的參数格式: @property (參数1, 參数2,...) 类型 名字;

    參数主要分为4类:

    读写属性:readwrite / readonly (是否生成get和set方法)

    setter处理: assign / retain / copy  (和内存管理相关)

    原子性:atomic / nonatomic   (这两个和多线程相关)

    set和get方法名称相关參数:setter(设置生成的set方法名称)/ getter(设置生成的get方法名称)

    改变set和get名称,多是用于BOOL类型的变量

    @property (nonatomic, assign, setter = abc:) int height;  设置set方法名称为 abc:

    说明:

    readonly代表仅仅生成getter方法,默认是readwrite

    assing默认(set方法直接赋值),copy是setter方法release旧值,再copy新值

    retain 要生成符合内存管理原则的set方法(应用于对象类型)(注意:在dealloc方法中释放属性对象)

    atomic(默认),保证getter和setter的原子性。提供多线程安全訪问,nonatomic性能高。所以通常是选择nonatomic.

    #import <Foundation/Foundation.h>
    
    @class Book; //声明Book是一个类
    
    @interface Student : NSObject
    //assign參数代表set方法直接赋值(默认的)
    //getter方法是指定getter方法的名字
    @property (nonatomic, assign, getter=getStudentAge) int age;
    //这里的retain代表:release旧值,retain新值
    //(注意,基本数据类型不能写retain參数)
    @property (nonatomic, retain) Book *book;
    
    - (void)setBook:(Book *)book;
    
    - (id)initStudent:(int)age;
    
    @end
    #import "Student.h"
    #import "Book.h"
    
    @implementation Student
    
    //构造函数
    - (id)initStudent:(int)age {
        if(self = [super init]) {
            _age = age;
        }
        NSLog(@"年龄为%d的学生被创建了", _age);
        return self;
    }
    
    //析构函数
    - (void)dealloc{
        self.book = nil; //调用setter方法
        [_book release];
        NSLog(@"年龄为%d的学生被释放了", _age);
        [super dealloc];
    }
    
    @end

    自己主动释放池

    自己主动释放池是OC里面的一种内存自己主动回收机制,一般能够将一些暂时变量加入到自己主动释放池中。统一回收释放。当自己主动释放池销毁时。池里的全部对象都会调用一次release方法。OC对象仅仅须要发送一条autorelease消息。就会把这个对象加入到近期的自己主动释放池中(栈顶的释放池)。

    autorelease实际上仅仅是把release的调用延迟了,对于每一次autorelease,系统仅仅是把该对象放入当前的autorelease pool中,当该pool被释放时,该pool中的全部对象会调用一次release方法。

    #import "Student.h"
    #import "Book.h"
    
    @implementation Student
    
    //创建静态方法构造对象
    + (id)student {
        Student *stu = [[[Student alloc] init] autorelease];
        return stu;
    }
    
    //创建带參数的静态方法构造对象
    + (id)studentWithAge:(int)age {
        Student *stu = [self student];
        stu.age = age;
        return stu;
    }
    
    //构造函数
    - (id)initStudent:(int)age {
        if(self = [super init]) {
            _age = age;
        }
        NSLog(@"年龄为%d的学生被创建了", _age);
        return self;
    }
    
    //析构函数
    - (void)dealloc{
        self.book = nil; //调用setter方法
        [_book release];
        NSLog(@"年龄为%d的学生被释放了", _age);
        [super dealloc];
    }
    
    @end
    int main(int argc, const char * argv[]) {
        //代表创建一个自己主动释放池
        @autoreleasepool {
            //第一种写法
            Student *stu = [[[Student alloc] initStudent:21] autorelease];
            Student *stu1 = [[[Student alloc] initStudent:21] autorelease];
    
        } //当括号结束后,池子将被销毁
        //假设自己主动释放池被销毁。池里面的全部对象都会调用release方法
        @autoreleasepool {
            //另外一种写法
            Student *stu2 = [[Student alloc] initStudent:21];
            
            [stu2 autorelease];
        }
        @autoreleasepool {
            //第三种写法(推荐)
            //不用手动释放
            Student *stu3 = [Student student];
        }
        return 0;
    }

    注意:

    1、假设某个对象调用了多次autorelease方法,则在自己主动释放池销毁的时候会调用多次release方法进行释放。

    例如以下:

        @autoreleasepool {
            Person *p = [[Person alloc] init];
            
            [p autorelease];
            
            [p autorelease];
        }
    p对象会被释放两次。

    2、假设嵌套autoreleasepool。那么对象将在调用了autorelease(加入到池子)的每层自己主动释放池结束的时候调用一次autorelease,例如以下:

        Person *p = [[Person alloc] init];
        
        @autoreleasepool {
            
            @autoreleasepool {
               [p autorelease];
            }
            
        }
    对象将在第7行被释放,而以下的情况会出现野指针异常

        Person *p = [[Person alloc] init];
        
        @autoreleasepool {
            [p autorelease];
            
            @autoreleasepool {
               [p autorelease];
            }
            
        }

  • 相关阅读:
    mysql数据库中的锁
    HihoCoder
    旅游规划(双权连通图)
    单调栈的原理
    战争联盟(并查集)
    点赞狂魔(巧用STL容器事半功倍)
    这是二叉搜索树吗?
    好像是两种权的dijkstra
    pat--046.整除光棍(除法模拟)
    幸运数字 2
  • 原文地址:https://www.cnblogs.com/clnchanpin/p/7069053.html
Copyright © 2011-2022 走看看