zl程序教程

您现在的位置是:首页 >  硬件

当前栏目

Objective-C基础笔记(3)OC的内存管理

内存基础笔记 管理 Objective OC
2023-09-11 14:14:59 时间

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];
        }
        
    }