iOS開(kāi)發(fā)系列—Objective-C之內(nèi)存管理_第1頁(yè)
iOS開(kāi)發(fā)系列—Objective-C之內(nèi)存管理_第2頁(yè)
iOS開(kāi)發(fā)系列—Objective-C之內(nèi)存管理_第3頁(yè)
iOS開(kāi)發(fā)系列—Objective-C之內(nèi)存管理_第4頁(yè)
iOS開(kāi)發(fā)系列—Objective-C之內(nèi)存管理_第5頁(yè)
已閱讀5頁(yè),還剩8頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、iOS開(kāi)發(fā)系列Objective-C之內(nèi)存管理概述我們知道在程序運(yùn)行過(guò)程中要?jiǎng)?chuàng)建大量的對(duì)象,和其他高級(jí)語(yǔ)言類(lèi)似,在ObjC中對(duì)象時(shí)存儲(chǔ)在堆中的,系統(tǒng)并不會(huì)自動(dòng)釋放堆中的內(nèi)存(注意基本類(lèi)型是由系統(tǒng)自己管理的,放在棧上)。如果一個(gè)對(duì)象創(chuàng)建并使用后沒(méi)有得到及時(shí)釋放那么就會(huì)占用大量?jī)?nèi)存。其他高級(jí)語(yǔ)言如C#、Java都是通過(guò)垃圾回收來(lái)(GC)解決這個(gè)問(wèn)題的,但在OjbC中并沒(méi)有類(lèi)似的垃圾回收機(jī)制,因此它的內(nèi)存管理就需要由開(kāi)發(fā)人員手動(dòng)維護(hù)。今天將著重介紹ObjC內(nèi)存管理:引用計(jì)數(shù)器屬性參數(shù)自動(dòng)釋放池引用計(jì)數(shù)器在Xcode4.2及之后的版本中由于引入了ARC(Automatic Reference Coun

2、ting)機(jī)制,程序編譯時(shí)Xcode可以自動(dòng)給你的代碼添加內(nèi)存釋放代碼,如果編寫(xiě)手動(dòng)釋放代碼Xcode會(huì)報(bào)錯(cuò),因此在今天的內(nèi)容中如果你使用的是Xcode4.2之后的版本(相信現(xiàn)在大部分朋友用的版本都比這個(gè)要高),必須手動(dòng)關(guān)閉ARC,這樣才有助于你理解ObjC的內(nèi)存回收機(jī)制。ObjC中的內(nèi)存管理機(jī)制跟C語(yǔ)言中指針的內(nèi)容是同樣重要的,要開(kāi)發(fā)一個(gè)程序并不難,但是優(yōu)秀的程序則更測(cè)重于內(nèi)存管理,它們往往占用內(nèi)存更少,運(yùn)行更加流暢。雖然在新版Xcode引入了ARC,但是很多時(shí)候它并不能完全解決你的問(wèn)題。在Xcode中關(guān)閉ARC:項(xiàng)目屬性Build Settings-搜索“garbage”找到Objecti

3、ve-C Automatic Reference Counting設(shè)置為No即可。內(nèi)存管理原理我們都知道在C#、Java中都有GC在自動(dòng)管理內(nèi)存,當(dāng)我們實(shí)例化一個(gè)對(duì)象之后通常會(huì)有一個(gè)變量來(lái)引用這個(gè)對(duì)象(變量中存儲(chǔ)對(duì)象地址),當(dāng)這個(gè)引用變量不再使用之后(也就是不再引用這個(gè)對(duì)象)此時(shí)GC就會(huì)自動(dòng)回收這個(gè)對(duì)象,簡(jiǎn)單的說(shuō)就是:當(dāng)一個(gè)對(duì)象沒(méi)有任何變量引用的時(shí)候就會(huì)被回收。例如下面的C#代碼片段using System;namespace GC class Program private static void Test() object o=new object(); static void Main(

4、string args) Test(); 上面是一段C#代碼,在Test()方法中,通過(guò)new Object()創(chuàng)建了一個(gè)對(duì)象,o是一個(gè)對(duì)象的引用(存儲(chǔ)了對(duì)象的地址),它是一個(gè)局部變量,作用范圍就是Test()方法內(nèi)部。image當(dāng)執(zhí)行完Test()方法之后o就會(huì)被釋放,此時(shí)由于沒(méi)有變量在引用new Object()這個(gè)對(duì)象,因此GC會(huì)自動(dòng)回收這個(gè)對(duì)象所占用的空間。但是在ObjC中沒(méi)有垃圾回收機(jī)制,那么ObjC中內(nèi)存又是如何管理的呢?其實(shí)在ObjC中內(nèi)存的管理是依賴對(duì)象引用計(jì)數(shù)器來(lái)進(jìn)行的:在ObjC中每個(gè)對(duì)象內(nèi)部都有一個(gè)與之對(duì)應(yīng)的整數(shù)(retainCount),叫“引用計(jì)數(shù)器”,當(dāng)一個(gè)對(duì)象在創(chuàng)

5、建之后它的引用計(jì)數(shù)器為1,當(dāng)調(diào)用這個(gè)對(duì)象的alloc、retain、new、copy方法之后引用計(jì)數(shù)器自動(dòng)在原來(lái)的基礎(chǔ)上加1(ObjC中調(diào)用一個(gè)對(duì)象的方法就是給這個(gè)對(duì)象發(fā)送一個(gè)消息),當(dāng)調(diào)用這個(gè)對(duì)象的release方法之后它的引用計(jì)數(shù)器減1,如果一個(gè)對(duì)象的引用計(jì)數(shù)器為0,則系統(tǒng)會(huì)自動(dòng)調(diào)用這個(gè)對(duì)象的dealloc方法來(lái)銷(xiāo)毀這個(gè)對(duì)象。下面通過(guò)一個(gè)簡(jiǎn)單的例子看一下引用計(jì)數(shù)器的知識(shí):Person.h/ Person.h/ MemoryManage/ Kenshin Cui on 14-2-15./ Copyright (c) 2014年 Kenshin Cui. All rights reserved

6、./#import <Foundation/Foundation.h>interface Person : NSObject#pragma mark - 屬性property (nonatomic,copy) NSString *name;property (nonatomic,assign) int age;endPerson.m/ Person.m/ MemoryManage/ Kenshin Cui on 14-2-15./ Copyright (c) 2014年 Kenshin Cui. All rights reserved./#import "Person.h

7、"implementation Person#pragma mark - 覆蓋方法#pragma mark 重寫(xiě)dealloc方法,在這個(gè)方法中通常進(jìn)行對(duì)象釋放操作-(void)dealloc NSLog("Invoke Person's dealloc method."); super dealloc;/注意最后一定要調(diào)用父類(lèi)的dealloc方法(兩個(gè)目的:一是父類(lèi)可能有其他引用對(duì)象需要釋放;二是:當(dāng)前對(duì)象真正的釋放操作是在super的dealloc中完成的)endmain.m/ main.m/ MemoryManage/ Created by Kens

8、hin Cui on 14-2-15./ Copyright (c) 2014年 Kenshin Cui. All rights reserved./#import <Foundation/Foundation.h>#import "Person.h"void Test1() Person *p=Person allocinit; /調(diào)用alloc,引用計(jì)數(shù)器+1 ="Kenshin" p.age=28; NSLog("retainCount=%lu",p retainCount); /結(jié)果:retainCou

9、nt=1 p release; /結(jié)果:Invoke Person's dealloc method. /上面調(diào)用過(guò)release方法,p指向的對(duì)象就會(huì)被銷(xiāo)毀,但是此時(shí)變量p中還存放著Person對(duì)象的地址, /如果不設(shè)置p=nil,則p就是一個(gè)野指針,它指向的內(nèi)存已經(jīng)不屬于這個(gè)程序,因此是很危險(xiǎn)的 p=nil; /如果不設(shè)置p=nil,此時(shí)如果再調(diào)用對(duì)象release會(huì)報(bào)錯(cuò),但是如果此時(shí)p已經(jīng)是空指針了, /則在ObjC中給空指針發(fā)送消息是不會(huì)報(bào)錯(cuò)的 p release;void Test2() Person *p=Person allocinit; ="Ken

10、shin" p.age=28; NSLog("retainCount=%lu",p retainCount); /結(jié)果:retainCount=1 p retain;/引用計(jì)數(shù)器+1 NSLog("retainCount=%lu",p retainCount); /結(jié)果:retainCount=2 p release;/調(diào)用1次release引用計(jì)數(shù)器-1 NSLog("retainCount=%lu",p retainCount); /結(jié)果:retainCount=1 p release; /結(jié)果:Invoke Perso

11、n's dealloc method. p=nil;int main(int argc, const char * argv) autoreleasepool Test1(); return 0;在上面的代碼中我們可以通過(guò)dealloc方法來(lái)查看是否一個(gè)對(duì)象已經(jīng)被回收,如果沒(méi)有被回收則有可能造成內(nèi)存泄露。如果一個(gè)對(duì)象被釋放之后,那么最后引用它的變量我們手動(dòng)設(shè)置為nil,否則可能造成野指針錯(cuò)誤,而且需要注意在ObjC中給空對(duì)象發(fā)送消息是不會(huì)引起錯(cuò)誤的。野指針錯(cuò)誤形式在Xcode中通常表現(xiàn)為:Thread 1:EXC_BAD_ACCESS(code=EXC_I386_GPFLT)錯(cuò)誤。因?yàn)?/p>

12、你訪問(wèn)了一塊已經(jīng)不屬于你的內(nèi)存。內(nèi)存釋放的原則手動(dòng)管理內(nèi)存有時(shí)候并不容易,因?yàn)閷?duì)象的引用有時(shí)候是錯(cuò)綜復(fù)雜的,對(duì)象之間可能互相交叉引用,此時(shí)需要遵循一個(gè)法則:誰(shuí)創(chuàng)建,誰(shuí)釋放。假設(shè)現(xiàn)在有一個(gè)人員Person類(lèi),每個(gè)Person可能會(huì)購(gòu)買(mǎi)一輛汽車(chē)Car,通常情況下購(gòu)買(mǎi)汽車(chē)這個(gè)活動(dòng)我們可能會(huì)單獨(dú)抽取到一個(gè)方法中,同時(shí)買(mǎi)車(chē)的過(guò)程中我們可能會(huì)多看幾輛來(lái)最終確定理想的車(chē),現(xiàn)在我們的代碼如下:Car.h/ Car.h/ MemoryManage/ Created by Kenshin Cui on 14-2-15./ Copyright (c) 2014年 Kenshin Cui. All rights re

13、served./#import <Foundation/Foundation.h>interface Car : NSObject#pragma mark - 屬性#pragma mark 車(chē)牌號(hào)property (nonatomic,copy) NSString *no;#pragma mark - 公共方法#pragma mark 運(yùn)行方法-(void)run;endCar.m/ Car.m/ MemoryManage/ Created by Kenshin Cui on 14-2-15./ Copyright (c) 2014年 Kenshin Cui. All rights

14、 reserved./#import "Car.h"implementation Car#pragma mark - 公共方法#pragma mark 運(yùn)行方法-(void)run NSLog("Car(%) run.",self.no);#pragma mark - 覆蓋方法#pragma mark 重寫(xiě)dealloc方法-(void)dealloc NSLog("Invoke Car(%) dealloc method.",self.no); super dealloc;endPerson.h/ Person.h/ MemoryM

15、anage/ Created by Kenshin Cui on 14-2-15./ Copyright (c) 2014年 Kenshin Cui. All rights reserved./#import <Foundation/Foundation.h>class Car;interface Person : NSObject Car *_car;#pragma mark - 屬性#pragma mark 姓名property (nonatomic,copy) NSString *name;#pragma mark - 公共方法#pragma mark Car屬性的set方法

16、-(void)setCar:(Car *)car;#pragma mark Car屬性的get方法-(Car *)car;endPerson.m/ Person.m/ MemoryManage/ Created by Kenshin Cui on 14-2-15./ Copyright (c) 2014年 Kenshin Cui. All rights reserved./#import "Person.h"#import "Car.h"implementation Person#pragma mark - 公共方法#pragma mark Car屬性的

17、set方法-(void)setCar:(Car *)car if (_car!=car) /首先判斷要賦值的變量和當(dāng)前成員變量是不是同一個(gè)變量 _car release; /釋放之前的對(duì)象 _car=car retain;/賦值時(shí)重新retain #pragma mark Car屬性的get方法-(Car *)car return _car;#pragma mark - 覆蓋方法#pragma mark 重寫(xiě)dealloc方法-(void)dealloc NSLog("Invoke Person(%) dealloc method.",); _car re

18、lease;/在此釋放對(duì)象,即使沒(méi)有賦值過(guò)由于空指針也不會(huì)出錯(cuò) super dealloc;endmain.m/ main.m/ MemoryManage/ Created by Kenshin Cui on 14-2-15./ Copyright (c) 2014年 Kenshin Cui. All rights reserved./#import <Foundation/Foundation.h>#import "Person.h"#import "Car.h"void getCar(Person *p) Car *car1=Car al

19、locinit; car1.no="888888" p.car=car1; NSLog("retainCount(p)=%lu",p retainCount); Car *car2=Car allocinit; car2.no="666666" car1 release; car1=nil; car2 release; car2=nil;int main(int argc, const char * argv) autoreleasepool Person *p=Person allocinit; ="Kensh

20、in" getCar(p); p.car run; p release; p=nil; return 0;程序運(yùn)行結(jié)果:setMethod從運(yùn)行結(jié)果來(lái)看創(chuàng)建的三個(gè)對(duì)象p、car1、car2都被回收了,而且p.car run也能順利運(yùn)行,已經(jīng)達(dá)到了我們的需求。但是這里需要重點(diǎn)解釋一下setCar方法的實(shí)現(xiàn),setCar方法中為什么沒(méi)有寫(xiě)成如下形式:-(void)setCar:(Car *)car _car=car;前面在我們說(shuō)到屬性的定義時(shí)不是都采用的這種方式嗎?根據(jù)前面說(shuō)到的內(nèi)存釋放原則,getCar方法完全符合,在這個(gè)方法中定義的兩個(gè)對(duì)象car1、car2也都是在這個(gè)方法中釋放的,

21、包括main函數(shù)中的p對(duì)象也是在main函數(shù)中定義和釋放的。但是如果發(fā)現(xiàn)調(diào)用完getCar方法之后緊接著調(diào)用了汽車(chē)的run方法,當(dāng)然這在程序設(shè)計(jì)和開(kāi)發(fā)過(guò)程中應(yīng)該是再普通不過(guò)的設(shè)計(jì)了。如果setCar寫(xiě)成“_car=car”的形式,當(dāng)調(diào)用完getCar方法后,人員的car屬性被釋放了,此時(shí)調(diào)用run方法是會(huì)報(bào)錯(cuò)的(大家自己可以試試)。但是如下的方式卻不會(huì)有問(wèn)題:-(void)setCar:(Car *)car if (_car!=car) /首先判斷要賦值的變量和當(dāng)前成員變量是不是同一個(gè)變量 _car release; /釋放之前的對(duì)象 _car=car retain;/賦值時(shí)重新retain

22、因?yàn)樵谶@個(gè)方法中我們通過(guò)car retain保證每次屬性賦值的時(shí)候?qū)ο笠糜?jì)數(shù)器+1,這樣一來(lái)調(diào)用過(guò)getCar方法可以保證人員的car屬性不會(huì)被釋放,其次為了保證上一次的賦值對(duì)象(car1)能夠正常釋放,我們?cè)谫x新值之前對(duì)原有的值進(jìn)行release操作。最后在Person的dealloc方法中對(duì)_car進(jìn)行一次release操作(因?yàn)閟etCar中做了一次retain操作)保證_car能正常回收。屬性參數(shù)像上面這樣編寫(xiě)setCar方法的情況是比較多的,那么如何使用property進(jìn)行自動(dòng)實(shí)現(xiàn)呢?答案就是使用屬性參數(shù),例如上面car屬性的setter方法,可以通過(guò)property定義如下:pr

23、operty (nonatomic,retain) Car *car;你會(huì)發(fā)現(xiàn)此刻我們不必手動(dòng)實(shí)現(xiàn)car的getter、setter方法程序仍然沒(méi)有內(nèi)存泄露。其實(shí)大家也應(yīng)該都已經(jīng)看到前面Person的name屬性定義的時(shí)候我們同樣加上了(nonatomic,copy)參數(shù),這些參數(shù)到底是什么意思呢?propertyParameterproperty的參數(shù)分為三類(lèi),也就是說(shuō)參數(shù)最多可以有三個(gè),中間用逗號(hào)分隔,每類(lèi)參數(shù)可以從上表三類(lèi)參數(shù)中人選一個(gè)。如果不進(jìn)行設(shè)置或者只設(shè)置其中一類(lèi)參數(shù),程序會(huì)使用三類(lèi)中的各個(gè)默認(rèn)參數(shù),默認(rèn)參數(shù):(atomic,readwrite,assign)一般情況下如果在多線程

24、開(kāi)發(fā)中一個(gè)屬性可能會(huì)被兩個(gè)及兩個(gè)以上的線程同時(shí)訪問(wèn),此時(shí)可以考慮atomic屬性,否則建議使用nonatomic,不加鎖,效率較高;readwirte方法會(huì)生成getter、setter兩個(gè)方法,如果使用readonly則只生成getter方法;關(guān)于set方法處理需要特別說(shuō)明,假設(shè)我們定義一個(gè)屬性a,這里列出三種方式的生成代碼:assign,用于基本數(shù)據(jù)類(lèi)型-(void)setA:(int)a _a=a;retain,通常用于非字符串對(duì)象-(void)setA:(Car *)a if(_a!=a) _a release; _a=a retain; copy,通常用于字符串對(duì)象、block、NS

25、Array、NSDictionary-(void)setA:(NSString *)a if(_a!=a) _a release; _a=a copy; 備注:本文基于MRC進(jìn)行介紹,ARC下的情況不同,請(qǐng)參閱其他文章,例如ARC下基本數(shù)據(jù)類(lèi)型默認(rèn)的屬性參數(shù)為(atomic,readwrite,assign),對(duì)象類(lèi)型默認(rèn)的屬性參數(shù)為(atomic,readwrite,strong)自動(dòng)釋放池在ObjC中也有一種內(nèi)存自動(dòng)釋放的機(jī)制叫做“自動(dòng)引用計(jì)數(shù)”(或“自動(dòng)釋放池”),與C#、Java不同的是,這只是一種半自動(dòng)的機(jī)制,有些操作還是需要我們手動(dòng)設(shè)置的。自動(dòng)內(nèi)存釋放使用autoreleasepo

26、ol關(guān)鍵字聲明一個(gè)代碼塊,如果一個(gè)對(duì)象在初始化時(shí)調(diào)用了autorelase方法,那么當(dāng)代碼塊執(zhí)行完之后,在塊中調(diào)用過(guò)autorelease方法的對(duì)象都會(huì)自動(dòng)調(diào)用一次release方法。這樣一來(lái)就起到了自動(dòng)釋放的作用,同時(shí)對(duì)象的銷(xiāo)毀過(guò)程也得到了延遲(統(tǒng)一調(diào)用release方法)??聪旅娴拇a:Person.h/ Person.h/ MemoryManage/ Created by Kenshin Cui on 14-2-15./ Copyright (c) 2014年 Kenshin Cui. All rights reserved./#import <Foundation/Foundat

27、ion.h>interface Person : NSObject#pragma mark - 屬性#pragma mark 姓名property (nonatomic,copy) NSString *name;#pragma mark - 公共方法#pragma mark 帶參數(shù)的構(gòu)造函數(shù)-(Person *)initWithName:(NSString *)name;#pragma mark 取得一個(gè)對(duì)象(靜態(tài)方法)+(Person *)personWithName:(NSString *)name;endPerson.m/ Person.m/ MemoryManage/ Creat

28、ed by Kenshin Cui on 14-2-15./ Copyright (c) 2014年 Kenshin Cui. All rights reserved./#import "Person.h"implementation Person#pragma mark - 公共方法#pragma mark 帶參數(shù)的構(gòu)造函數(shù)-(Person *)initWithName:(NSString *)name if(self=super init) =name; return self;#pragma mark 取得一個(gè)對(duì)象(靜態(tài)方法)+(Person *)p

29、ersonWithName:(NSString *)name Person *p=Person allocinitWithName:name autorelease;/注意這里調(diào)用了autorelease return p;#pragma mark - 覆蓋方法#pragma mark 重寫(xiě)dealloc方法-(void)dealloc NSLog("Invoke Person(%) dealloc method.",); super dealloc;endmain.m/ main.m/ MemoryManage/ Created by Kenshin C

30、ui on 14-2-15./ Copyright (c) 2014年 Kenshin Cui. All rights reserved./#import <Foundation/Foundation.h>#import "Person.h"int main(int argc, const char * argv) autoreleasepool Person *person1=Person allocinit; person1 autorelease;/調(diào)用了autorelease方法后面就不需要手動(dòng)調(diào)用release方法了 ="Kenshin"/由于autorelease是延遲釋放,所以這里仍然可以使用person1 Person *person2=Person allocini

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論