溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

手動內(nèi)存管理怎樣編碼

發(fā)布時間:2020-07-20 00:21:03 來源:網(wǎng)絡 閱讀:456 作者:Im劉亞芳 欄目:開發(fā)技術

我建了一個iOS開發(fā)QQ交流群:188647173,大家可以一起來相互學習。

還有一個群里面大神的個人站點www.mylonly.com,大家有不會的可以向他請教。


iOS的對象都繼承自NSObject,NSOjbect對象有一個方法retainCount,用以獲取對象的內(nèi)存引用計數(shù)。一般情況下,一下幾種情況會使對象的引用計數(shù)發(fā)生改變,

(1)alloc(或new) 對象分配后,引用計數(shù)為1

(2)retain 調(diào)用retain方法(或者說向?qū)ο蟀l(fā)送retain消息),對象的引用計數(shù)增加1

(3)copy 調(diào)用copy方法(或者說向?qū)ο蟀l(fā)送copy消息),創(chuàng)建一個新的對象,其引用計數(shù)為1;原來對象的引用計數(shù)不變。

(4)release 向?qū)ο蟀l(fā)送release消息,對象內(nèi)存引用計數(shù)較少1,如果引用計數(shù)為0,那么就釋放對象所占有的內(nèi)存

(5)autorelease 想對象發(fā)送autorelease消息,對象引用計數(shù)較少1,如果引用計數(shù)為0,不會馬上釋放對象內(nèi)存,等到最近一個自動釋放池autoreleasepool時候釋放。


那么我們以代碼添加一個UIImageView為例,看看內(nèi)存管理的代碼應該怎么寫,

第一種情況,局部變量的內(nèi)存管理

- (void)viewDidLoad

{

    [super viewDidload];

    UIImageView *p_w_picpathView = [[UIImageView alloc] initWithImage:...];//通過alloc/init創(chuàng)建對象,retainCount=1

    [self.view addSubview:p_w_picpathView];//將其添加到superView,引用計數(shù)加1,retainCount=2

    [p_w_picpathView release];//向?qū)ο蟀l(fā)送release消息,引用計數(shù)減1,retainCount=1

}

上面的代碼[self.view addSubview:p_w_picpathView];造成p_w_picpathView引用計數(shù)增加1,可能讓人不明白,所以這里需要說明一下,在子視圖添加到父視圖的時候,子視圖的引用計數(shù)會自動的增加1,當父視圖被release的時候,該父視圖上面的所有子視圖的引用計數(shù)都會被release一次,使子視圖的引用計數(shù)減1,這樣一增一減保持了retain/release的平衡。

這段話很重要,請仔細理解:關于上面的UIImageView對象p_w_picpathView的引用計數(shù)變化和生命周期是這樣的,當使用alloc/init創(chuàng)建p_w_picpathView對象的時候,它的引用計數(shù)retainCount=1;將它將入到父視圖的時候,引用計數(shù)增加1,retainCount=2;向其發(fā)送release消息時候,引用計數(shù)減少1,retainCount=1;最后,在ViewController的dealloc方法中,self.view被釋放,self.view就是p_w_picpathView的父視圖,這時候self.view上面的所有子視圖都會收到release消息,當p_w_picpathView收到release消息的時候,引用計數(shù)減少1, retainCount=0。這時候p_w_picpathView對象內(nèi)存被安全釋放,該對象的生命周期到此結束。

好了,上面的情況講述的就是在一對括號內(nèi){}創(chuàng)建局部變量時候遵循的手動管理內(nèi)存的規(guī)則,那就是你通過alloc/init創(chuàng)建了對象,那你就需要在使用結束以后向其發(fā)送release消息


第二種情況,成員變量的內(nèi)存管理

什么是成員變量,以一個ViewController1舉例說明,

@interface ViewController1:NSObject

{

    //括號內(nèi)的變量就是成員變量

    Person *_person;

    NSString *_name;

    NSString *_schoolName;

}

@end

上面括號內(nèi)就定義了三個成員變量,分別是_person、_name、_schoolName,成員變量最好以'_'下劃線開頭,接著我們在viewDidLoad中對三個對象分別初始化,成員如下所示,

- (void)viewDidLoad

{

    [super viewDidLoad];

    //alloc/init創(chuàng)建對象

    _person = [[Person alloc] init];

    _schoolName = [[NSString alloc] initWithFormat:@"yyy"];

    //類方法創(chuàng)建對象

    _name = [NSString stringWithFormat:@"xxx"];    

}

上面用兩種不同的方法創(chuàng)建了對象,一是使用alloc/init創(chuàng)建,二是使用類方法創(chuàng)建,因為成員變量我們需要在整個ViewController作用域都需要使用到,所以我們不能立即向其發(fā)送release消息,我們應該在dealloc時候向其發(fā)送release消息,如下代碼,

- (void)dealloc

{

    [_person release];

    [_schoolName release];

    [super dealloc];

}

我們在dealloc中向兩個通過alloc/init方式創(chuàng)建的對象_person和_schoolName發(fā)送release消息,為什么不向_name發(fā)送release消息呢,這是因為_name是通過NSString的類方法stringWithFormat:創(chuàng)建的,而類方法返回的對象已經(jīng)在方法內(nèi)部加入了自動釋放池,它的引用計數(shù)和生命周期已經(jīng)交給了自動釋放池來管理,不需要向其發(fā)送release消息了。

上面描述了“成員變量”內(nèi)存管理的規(guī)則,如果是通過alloc/init創(chuàng)建的對象,那么需要在dealloc時候向其發(fā)送release消息;如果不是通過alloc/init方法創(chuàng)建的對象,例如通過類方法創(chuàng)建的,那么不需要在dealloc時候向其發(fā)送release消息,比如_name = [NSString stringWithFormat:@"xxx"];或者數(shù)組_petArray = [NSArray arrayWithObject:@"dog",@"cat",@"duck",nil];。


第三種,屬性@property對象的內(nèi)存管理

上面說的兩種對象都是ViewController類內(nèi)部管理的對象,這些對象不會與其他類有交互,所以說內(nèi)存管理的規(guī)則相對簡單。如果類中的變量需要與其他類進行交互,這時候就需要用到@property屬性來定義變量。我們一般這樣編寫@property的代碼,

@property (nonatomic, assign) TestObject *testObject;//默認情況是assign

//或者

@property (nonatomic, retain) TestObject *testObject;

//又或者

@property (nonatomic, copy) TestObject *testObject;

我們知道,當我們定義屬性的時候,Xcode會根據(jù)修飾關鍵字(例如retain、assign、copy)自動生成getter/setter,不同修飾符的getter方法沒什么差別,主要就是setter有很大的不同,那么我們來不同修飾符時候setter方法不同的地方,

//assign修飾符

- (void)setTestObject:(id)newValue

{

    testObject = newValue;

}

assign修飾符,相當于指針賦值。對象的引用計數(shù)不發(fā)生改變。注意源對象不用了,一定要把這個對象設置為nil

//retain修飾符

- (void)setTestObject:(id)newValue

{

    if(testObject != newValue){

        [testObject release];

        [newValue retain];

        testObject = newValue;

    }

}

retain修飾符的setter方法表示,先向原來的值發(fā)送release,然后向新的值newValue發(fā)送retain消息,使其引用計數(shù)增加1,然后新的值newValue賦值給原來的值。

//copy修飾屬性

- (void)setTestObject:(id)newValue

{

    if(testObject != newValue){

        [testObject release];

        testObject = [newValue copy];

    }

}

copy修飾符的setter方法就是,先向原來的值發(fā)送release,然后復制一份新的值賦值給testObject,它的引用計數(shù)為1。

下面主要說一說retain修飾符修飾的屬性,在使用的時候需要注意的地方,我們知道當我們使用self.testObject的時候有兩種情況,一種是左值例如self.testObject = [[TestObject alloc] init];,另一種是又值例如TestObject *tempTestObject = self.testObject。

當我們使用self.testObject作為右值的時候,相當于調(diào)用了getter方法,這沒有什么需要注意的地方;當使用self.testObject作為左值的時候,就有一些注意的地方了,例如下面的語句,

self.testObject = [[TestObject alloc] init];//這是錯誤的,因為alloc/init創(chuàng)建的對象,在setter內(nèi)部又被retain了一次,導致引用計數(shù)retainCount=2,造成內(nèi)存泄露

//修改代碼如下

TestObject *tempTestObject = [[TestObject alloc] init];

self.testObject = tempTestObject;

[tempTestObject release];

這樣就保證了self.testObject引用計數(shù)為1,在整個類的作用域都可以放心安全使用,然后在dealloc的時候向該對象再發(fā)送release對象,釋放其內(nèi)存空間,

- (void)dealloc

{

    [self.testObject release];

    [super dealloc];

}

還有一種情況,self.testObject是通過TestObject的類方法初始化的,這時候就可以直接初始化,如下代碼,

self.testObject = [TestObject testObjectWithClassMehtod:xxx];//這樣寫沒有問題

這樣創(chuàng)建的對象是被類方法放入自動釋放池來管理其引用計數(shù)和生命周期,而不用擔心內(nèi)存泄露的問題。雖然這時候內(nèi)存不需要我們來手動釋放了,但是為了編碼規(guī)范,還是在dealloc時候向其發(fā)送release消息,

- (void)dealloc

{

    [self.testObject release];

    [super dealloc];

}


下面的內(nèi)容可能比較多,但是我是詳細的列出了每一個細節(jié)和不同,讀者也可以按照這種方式去總結,應該會有幫助,我自己就是這樣總結,感覺對于內(nèi)存管理的了解越來越明確了。

通過實際的例子來說明問題,下面我來描述一個場景:點擊ViewController1導航欄上面的rightBarButtonItem,push到ViewController2頁面,ViewContorller2需要用到Student類,那么我們在ViewController2內(nèi)部定義Student時候就有很多細節(jié)需要注意了,我會分情況討論,

(1)Student作為ViewController2的成員變量

前面我說過,為了讓自己和別人一眼就看出這是一個成員變量,最好在成員變量的前面加上'_'下劃線,所以這時候我們這樣在ViewController2中使用Student作為成員變量,

ViewController2.h file

@interface ViewController2:UIViewController

{

    Student *_student;

}

@end

ViewController2.m file

@implementation ViewController2

- (void)dealloc

{

    [_student release];

    [super dealloc];

}

- (void)viewDidLoad

{

    [super viewDidLoad];

    _student = [[Student alloc] init];//引用計數(shù)為1,即retainCount=1

}

@end

(2)不定義Student成員變量_student,定義Student屬性,不寫@synthesie代碼

ViewController2.h file

@interface ViewController:UIViewController

{

    //這里不定義成員變量_student

}

@property (nonatomic, retain) Student *student;

@end

ViewController2.m file

@implementation ViewController2

//沒有寫@synthesize

- (void)viewDidLoad

{

    [super viewDidLoad];   

    //第一種實例化方法

    Student *tempStudent = [[Student alloc] init];//tempStudent引用計數(shù)為1

    self.student = tempStudent;//tempStudent引用計數(shù)為2,self.student引用計數(shù)為2

    [tempStudent release];//tempStudent引用計數(shù)為1,self.student引用計數(shù)為1

    //這時候定義的self.student引用計數(shù)為1,可以在ViewController2整個作用域范圍內(nèi)使用,要在dealloc中向其發(fā)送release消息


    //第二種實例化方法

    _student = [[Student alloc] init];

    //需要解釋一下這個代碼,我們這時候沒有定義成員變量,為什么還可以使用_student呢,我也不明白為什么,應該是屬性property自動幫助我們生成了對應的成員變量,我之前還有所疑惑,后來查看了(注釋1)_student和self.student的內(nèi)存地址,發(fā)現(xiàn)兩者的內(nèi)存地址相同。所以,我們定義了屬性@property (nonatomic, retain) Student *student;,就可以直接使用_student成員變量。

    //那么接著解釋一下此時的內(nèi)存吧,這時候_student的引用計數(shù)為1,self.student引用計數(shù)為1

}

- (void)dealloc

{

    //dealloc方法怎么寫?

    //如果你是使用第一種方式實例化self.student,為了對應,那么就用這種方式釋放對象

    [self.student release];

    //如果你使用第二種方式實例化,那么就用下面的方式發(fā)送release消息

    [_student release];

    //需要說明的是,其實上面兩種方式,釋放對象都是可以的,這里只是為了與實例化方法對應。

    [super dealloc];

}

@end

(3)不定義Student成員變量_student,定義Student屬性,寫@synthesize sutdent;,而不寫@synthesize sutdent = _student;

ViewController2.h file

@interface ViewController:UIViewController

{

    //不定義成員變量_student

}

@property (nonatomic, retain) Student *student;

@end

ViewController2.m file

@implementation ViewController2

//注意這里不是@synthesize student = _student;這是下面會說到的

@synthesize student;

- (void)viewDidLoad

{

    [super viewDidLoad];

    //這時候有兩種實例化方法

    //第一種實例化方法

    Student *tempStudent = [[Student allloc] init];//引用計數(shù)retainCount=1

    self.student = tempStudent;//引用計數(shù)self.student's retainCount=2,tempStudent's retainCount=2

    [tempStudent release];//引用計數(shù)tempStudent's retainCount=1,self.student's retainCount=1

    //第二種實例化方法

    student = [[Student alloc] init];

}

@end

- (void)dealloc

{

    //釋放對象,對應第一種實例化方法

    [self.student release];

    //釋放對象,對應第二種實例化方法

    [super dealloc];

    //當然,這兩種釋放對象的方式其實都可以,只是為了對應實例化方法

}

需要特別注意的是,我們寫了@synthesize student;而不是@synthesize student = _student;這時候,就不可以在ViewController2作用域范圍內(nèi)使用_student這個成員變量了。

(4)不定義Student成員變量,定義Student屬性,寫@synthesize student = _student,而不是@synthesize sutdent;

ViewController2.h file

@interface ViewController2:UIViewControler

{

    //不定義_student成員變量

}

@property (nonatomic, retain) Student *student;

@end

ViewController2.m file

@implementation ViewContorller2

//這里不是@synthesize sutdent;

@synthesze student = _student;

- (void)viewDidLoad

{

    [super viewDidLoad];

    //第一種實例化方法,不在解釋引用計數(shù)

    Student *tempStudent = [[Student alloc] alloc];

    self.student = tempStudent;

    [tempStudent release];

    //第二種實例化方法

    _student = [[Student alloc] release];

}

- (void)dealloc

{

    //釋放對象,對應第一種實例化方法

    [self.student release];

    //釋放對象,對應第二種實例化方法

    [_student release];

    [super dealloc];

}

@end

(5)定義成員變量_student,定義student屬性,不寫@synthesize

ViewController2.h

@interface ViewController2:UIViewContorller

{

    Student *_student;

}

@property (nonatomic, retain) Student *student

@end

ViewContorller2.m

@implementation ViewContorller2

//不寫@synthesize

- (void)viewDidLoad

{

    [super viewDidLoad];

    //第一種實例化方法

    Student *tempStudent = [[Student alloc] init];

    self.student = tempStudent;

    [tempStudent release];

    //第二種實例化方法

    _student = [[Student alloc] init];

}

- (void)deallc

{

    //釋放對象,對應第一種實例化方法

    [self.student release];

    //釋放對象,對應第二種實例化方法

    [_student release];

    [super dealloc];

}

@end

(6)定義Student成員屬性_student,定義Student屬性,寫@synthesize student;,而不寫@syntheise student = _student;

ViewController.h

@interface ViewContorller:UIViewContorller

{

    Student *_sutdent;

}

@property (nonatomic, retain) Student *student

@end

ViewController2.m file

@implementation ViewController2

//這里不是@synthesize student = _student;

@synthesieze student;

- (void)viewDidLoad

{

    [super viewDidLoad];

    //第一種實例化方法

    Student *tempStudent = [[Student alloc] init];

    self.student = tempStudent;

    [tempStudent release];

    //第二種實例化方法

    student = [[Student alloc] init];

}

- (void)dealloc

{

    //釋放對象,對應第一種實例化方法

    [self.student release];

    //釋放對象,對應第二種實例化方法

    [student release];

    [super dealloc];

}

@end

(7)定義Student成員屬性_student,定義Student屬性,寫@synthesize student = _student;,而不寫@syntheise student;

ViewController.h

@interface ViewController2:UIViewContorller

{

    Student *_student;

}

@property (nonatomic, retain) Student *student;

@end

ViewController2.m file

@implementation ViewController2

@synthesize student = _student;

- (void)viewDidLoad

{

    [super viewDidLoad];

    //第一種實例化方法

    Student *tempStudent = [[Student alloc] init];

    self.student = tempStudent;

    [tempStudent release];

    //第二種實例化方法

    _student = [[Student alloc] init];

}

- (void)dealloc

{

    //釋放對象,對應第一種實例化方法

    [self.student release];

    //釋放對象,對應第二種實例化方法

    [_student release];

    [super dealloc];

}

@end

上面我自己羅列了我所知道的定義成員變量和屬性時候用到的所有方式,如果有遺漏還請讀者指出,這里面的代碼我都驗證了有效性,可能在寫博客的時候會有一些書寫方面的疏漏,有錯誤也請讀者指正。

總結:

如果定義了屬性student,則自動生成_student成員變量;如果寫了@synthesize student,此時只能在ViewController2作用域范圍內(nèi)使用student和self.student,而不能使用_student;如果寫了@synthesize student = _student,則在ViewController2作用域范圍內(nèi)只能使用_student和self.studetn,而不能使用student,也就是說_student和student不能共存,只能使用其一。

注釋1:怎樣查看一個對象的內(nèi)存呢?在使用該對象的地方打一個斷點,當運行到斷點時候,在控制臺使用po命令工具,例如我想知道self.student內(nèi)存地址,那么我可以在斷點時候在控制臺中這樣做,po self.student,如下圖,

手動內(nèi)存管理怎樣編碼


向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI