程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> Objective-C基礎筆記(3)OC的內存管理

Objective-C基礎筆記(3)OC的內存管理

編輯:關於C語言

Objective-C基礎筆記(3)OC的內存管理


Objective-C的內存基本管理

在OC中每個變量都保存著引用計數器,當這個對象的引用計數器為0的時候該對象會被回收。當使用alloc、new或者copy創建一個對象的時候,對象的引用計數器被置為1.

給對象發送一條retain消息,可以使引用計數器+1.

給對象發送一條release消息,可以使引用計數器-1.

當OC被銷毀的時候會發送一條dealloc消息(不要直接調用,由系統調用),可以重寫dealloc方法,在該方法中釋放相關資源。

可以給對象發送retainCount消息獲取對象的當前引用計數器。

首先我們新建一個工程

\

接下來將工程的設置裡面將ARC禁掉

\

Book.h文件

#import 

@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 
#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 
#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的學生被釋放了

@class關鍵字

通常引用一個類有兩種方法,一種是通過#import,另一種是通過@class.

#import 的方式會將頭文件中的所有信息引入。

@class 的方式只是說明它是一個類(假如只是聲明一個類就不用使用#import).

#import 

@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,...) 類型 名字;

參數主要分為3類:

讀寫屬性:readwrite / readonly

setter處理: assign / retain / copy

原子性:atomic / nonatomic

說明:

readonly代表只生成getter方法,默認是readwrite

assing默認(直接賦值),copy是setter方法release舊值,再copy新值

atomic(默認),保證getter和setter的原子性,提供多線程安全訪問,nonatomic性能高,所以一般是選擇nonatomic.

#import 

@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. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved