項目中關於內存管理的問題

OC中內存管理機制。與retain配對使用的方法是dealloc還是release,爲什麼?需要與alloc配對使用的方法是dealloc還是release,爲什麼?readwritereadonly,assign,retain,copy,nonatomic,atomic,strong,weak屬性的作用?
OC中內存管理機制應該就是引用計數機制,retainCount0時釋放該內存。
retain對應的是release,內存的釋放用release
alloc對應的是dealloc,內存的銷燬用dealloc
readwrite此標記說明屬性會被當成讀寫的,這也是默認屬性。
readonly此標記說明屬性只可以讀,也就是不能設置,可以獲取。
assign不會使引用計數加1,也就是直接賦值。
retain會使引用計數加1
copy建立一個索引計數爲1的對象,在賦值時使用傳入值的一份拷貝。
nonatomic:非原子性訪問,多線程併發訪問會提高性能。
atomic:原子性訪問。
strong:打開ARC時纔會使用,相當於retain
weak:打開ARC時纔會使用,相當於assign,可以把對應的指針變量置爲nil
 
什麼叫做循環引用
   對象A創建並引用到了對象B,對象B創建並引用到了對象C,對象C創建並引用到了對象B,這個時候B的引用計數是2,而C的引用計數是1,當A不用B的時候,就釋放了B的所有權,這個時候C還引用對象B,所有B不會釋放,引用計數爲1;因爲B也引用着對象C,B不釋放,那麼C就不會被釋放,所以它們的引用計數都爲1,並且永遠不會被釋放,所以形成了循環引用。
 
1.分別寫出MRC中在assign、retain、copy下屬性name對應的setter方法的內部實現。
assign下
- (void) setName:(NSString*)name
{
      _name = name;
}
retain下
- (void) setName:(NSString*)name
{
      if(_name != name){
       [_name release];
       _name = [name retain];
       }
}
 
copy下
- (void) setName:(NSString*)name
{
      if(_name != name){
       [_name release];
       _name = [name copy];
       }
}
 
2.ARCdealloc方法存在的意義在於什麼地方?舉例說明一下具體的使用場景。
答:其實在MRCdealloc方法存在的主要意義是爲了釋放自身的實例變量,移除觀察者,停止timer,移除通知,代理置空等。ARC下,系統會幫助我們釋放該對象所包含的實例變量,但是有些對象還是需要們自己去釋放的(比如Core Foundation框架下的一些對象),另外通知中觀察者的移除,代理置空,停止timer
示例如下所示:
 
- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];//移除通知觀察者
    [[XMPPManager sharedManager] removeFromDelegateQueue:self];//移除委託引用
    [[MyClass shareInstance]  doSomething ]//其他操作
    scrollView.delegate = nil;
    [timer invalidate]; 
}
 
3.@synthesize@dynamic有什麼區別?
(1)@property有兩個對應的詞,一個是@synthesize,一個是@dynamic。如果@synthesize@dynamic都沒寫,那麼默認的就是@syntheszie var = _var;
(2)@synthesize的語義是如果你沒有手動實現setter方法和getter方法,那麼編譯器會自動爲你加上這兩個方法。
(3)@dynamic告訴編譯器:屬性的settergetter方法由用戶自己實現,不自動生成。(當然對於readonly的屬性只需提供getter即可)。假如一個屬性被聲明爲@dynamic var,然後你沒有提供@setter方法和@getter方法,編譯的時候沒問題,但是當程序運行到instance.var = someVar,由於缺setter方法會導致程序崩潰;或者當運行到 someVar = instance.var時,由於缺getter方法同樣會導致崩潰。編譯時沒問題,運行時才執行相應的方法,這就是所謂的動態綁定。@dynamic可用於在分類中添加屬性(需要用到objc_getAssociatedObjectobjc_setAssociatedObject函數)。
 
4.你如何看待iOS中的拷貝?
答:在我看來,日常生活中,當我們用到拷貝這個詞語的時候,不管怎樣都會產生兩份。一份是原來的,一份是新拷貝出來的。但是到目前爲止,在iOS中我看到了三種拷貝方式:
(1)僞拷貝:僞拷貝,顧名思義,就是假拷貝,沒有拷貝出新的對象。這一點對於NSString這種類簇來說比較常見,NSString本身是不可變字符串,內容不可能被修改,因此我們也沒有拷貝的必要,因爲拷貝的目的是防止原件被修改,所以才拷貝出來一份,僞拷貝實際上是對象引用計數加了1(相當於retain或者strong的功效)。
(2)淺拷貝:淺拷貝就是確實拷貝出來一份和原來內容一樣的新對象。但是對於對象自帶的屬性是僞拷貝,兩個對象的屬性指向同一個內存。
(3)深拷貝:深拷貝就是不僅僅拷貝出一份新對象,而且對象的屬性也拷貝了一份。
總的來說,如果在開發的過程中需要實現拷貝,那麼需要接受NSCopying協議。實現copyWithZone:方法。淺拷貝、深拷貝的區別在於copyWithZone:方法的實現不同。
 
16.定義屬性時,什麼情況使用copyassign,和retain
答:assign用於簡單數據類型,如NSInteger,double,boolretain copy用於對象類型,二者又有區別,copy是用於一個對象有可能被修改,但不想修改原件,所以拷貝一份出來(新拷貝的對象引用計數爲1),這樣新拷貝的修改了,原件不會變,原件修改了,新拷貝的不會變。而retain呢,對象引用計數+1,對象只有一個,但引用計數爲2,通過任何一個引用修改對象內容,另外一個引用使用的時候,用的就是修改之後的內容。
1、以下代碼有什麼問題嗎?如果沒有問題的話,objobj2的引用計數分別是多少?如果有問題的話存在什麼問題?
 Class *obj = [[Class alloc]init];
 Class *obj2 = obj;
          [obj hello];
          [obj release];  
          [obj2 hello]; 
          [obj2 release];
答:上面的代碼是存在問題的。當執行完第一行代碼時,因爲執行了allocobj的引用計數爲1。第二行代碼執行完之後,obj2只是和obj指向了同一塊內存。第三行代碼是執行了hello方法。第四行代碼執行release消息之後,obj的引用計數減一,這時retainCount變爲0.系統自動調用dealloc方法,對象被銷燬。第五行代碼執行時,obj2執行的內存已經被系統回收了,但還是調用了hello方法,出現了問題(野指針)。第六行執行時,obj2所指向的內存已經不存在,再次調用release消息,出現過度釋放的問題,而且obj2已經變成野指針了。
Class *obj = [[Class alloc]init];//obj引用計數加1
Class *obj2 = obj;//objobj2指向同一塊內存(對象)
         [obj hello];
         [obj release];//obj指向的內存(對象)被銷燬
         [obj2 hello];//錯誤,obj2指向的內存(對象)已經被銷燬了
          [obj2 release];//[obj release]之後,obj2是個野指針,不應該再去調用方法.
2、在實際開發的過程中,什麼情況下需要創建自動釋放池?下面代碼中有沒有什麼問題?
 
  Person *p1=[[Person alloc]init];
  @autoreleasepool {
    [p1 autorelease];
     @autoreleasepool {
         [p1 autorelease];
    }
 
 
答:其實自動釋放池存在的意義是爲了延遲釋放一些對象,延遲向對象發送release消息。在實際的開發中呢,有兩種情況是需要手動創建自動釋放池的。第一個就是在多線程中,因爲子線程中可能會使用便利構造器等方法來創建對象,那麼這些對象的釋放只能放在自動釋放池中,主線程其實已經添加過自動釋放池,在main函數裏面。第二個就是如果一段代碼裏面(比如for循環)大量使用便利構造器創建對象,也需要手動添加自動釋放池。
上述代碼其實是存在問題的。當執行完第一行代碼時,p1的引用計數是1.第二行是創建了一個autoreleasepool。第三行代碼向p1發送了autorelease消息,延遲release,即在出池的時候,把p1釋放掉。第四行代碼又創建了一個autoreleasepool。第五行代碼再次向p1發送了autorelease消息。當代碼執行到第六行的“}”時,第二個自動釋放池結束,這時p1引用技術減1p1所指向的內存(對象)的retainCount1變爲0,內存被系統回收。代碼執行到第七行時,最外層的自動釋放池結束,再次向p1發送release消息,出現過度釋放。
 @autoreleasepool {
    [p1 autorelease];//此時p1被加入自動釋放池1
    @autoreleasepool {
      [p1 autorelease];//此時p1被加入自動釋放池2
    }//此處,自動釋放池2結束,p1引用計數-1
  }//此處,自動釋放池1結束,但是向已經被釋放的對象p1發送了消息
 
4.父類實現深拷貝時,子類如何實現深拷貝 ?父類沒有實現深拷貝時,子類如何實現深度拷貝?
答:父類實現深拷貝之後,子類在重寫的copyWithZone方法,先調用父類的copyWithZone方法,之後實現自己屬性的拷貝。
如果父類沒有實現深拷貝,子類除了需要對自己的屬性進行拷貝,還要對父類的屬性進行拷貝。
 
一、內存管理
ARC下的內存管理
ARC,全稱叫AutomaticReference Counting,該機制從ios5開始開始導入。簡單地說,就是代碼中自動加入了retain/release。所以,其底層機制還是引用計數,
在你打開ARC時,你是不能使用retainrelease autorelease 操作的,原先需要手動添加的用來處理內存管理的引用計數的代碼可以自動地由編譯器完成了,但是你需要在對象屬性上使用weak 和strong, 其中strong就相當於retain屬性,而weak相當於assign,基礎類型還是使用assign。
strong關鍵字:
strong 用來修飾強引用的屬性;對應原來的retain。
該屬性值對應 __strong 關鍵字,即該屬性所聲明的變量將成爲對象的持有者。
weak關鍵字:
weak 用來修飾弱引用的屬性;對應原來的assign。
但是不同的是當對象被釋放以後,對象自動賦值爲nil;並且,delegate 和 Outlet 蘋果推薦用 weak 屬性來聲明。同時,如上一回介紹的 iOS 5 之前的版本是沒有 __weak 關鍵字的,所以 weak 屬性是不能使用的。這種情況我們使用 unsafe_unretained。
 
IOS的ARC會導致的內存泄露問題和解決方案
iOS提供了ARC功能,很大程度上簡化了內存管理的代碼。
但使用ARC並不代表了不會發生內存泄露,使用不當照樣會發生內存泄露。
下面列舉兩種ARC導致內存泄露的情況。
1,循環參照
A有個屬性參照B,B有個屬性參照A,如果都是strong參照的話,兩個對象都無法釋放。
這種問題常發生於把delegate聲明爲strong屬性了。
2,死循環
如果某個ViewController中有無限循環,也會導致即使ViewController對應的view關掉了,ViewController也不能被釋放。
這種問題常發生於animation處理。
 
5、內存中的棧和堆的區別是什麼?哪些數據在棧上哪些數據在堆上?
(1)管理方式:對於棧來講,是由編譯器自動管理,無需我們手工控制;對於堆來說,釋放工作由程序員控制,容易產生 memory leak。
 (2)申請大小:能從棧獲得的空間較小,堆是向高地址擴展的數據結構,是不連續的內存區域。堆的大小受限於計算機系統中 有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。
 (3)碎片問題:對於堆來講,頻繁的new/delete勢必會造成內存空間的不連續,從而造成大量的碎片,使程序效率降低。 對於棧來講,則不會存在這個問題,因爲棧是先進後出的隊列,他們是如此的一一對應,以至於永遠都不可能有一個內存塊 從棧中間彈出
 (4)分配方式:堆都是動態分配的,沒有靜態分配的堆。棧有2種分配方式:靜態分配和動態分配。靜態分配是編譯器完成 的,比如局部變量的分配。動態分配由 alloca函數進行分配,但是棧的動態分配和堆是不同的,他的動態分配是由編譯器 進行釋放,無需我們手工實現。
 (5)分配效率:棧是機器系統提供的數據結構,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧 都有專門的指令執行,這就決定了棧的效率比較高。堆則是C/C++函數庫提供的,它的機制是很複雜的。
在函數體中定義的變量通常是在棧上,用malloc, calloc, realloc等分配內存的函數分配得到的就是在堆上。
 
12.Objective-C如何對內存管理的,說說你的看法和解決方法?
  Objective-C的內存管理主要有三種方式ARC(自動內存計數)、手動內存計數、內存池。
  1. (Garbage Collection)自動內存計數:這種方式和java類似,在你的程序的執行過程中。始終有一個高人在背後準確地幫你收拾垃圾,你不用考慮它什麼時候開始工作,怎樣工作。你只需要明白,我申請了一段內存空間,當我不再使用從而這段內存成爲垃圾的時候,我就徹底的把它忘記掉,反正那個高人會幫我收拾垃圾。遺憾的是,那個高人需要消耗一定的資源,在攜帶設備裏面,資源是緊俏商品所以iPhone不支持這個功能。所以“Garbage Collection”不是本入門指南的範圍,對“Garbage Collection”內部機制感興趣的同學可以參考一些其他的資料,不過說老實話“Garbage Collection”不大適合適初學者研究。
  解決: 通過alloc – initial方式創建的, 創建後引用計數+1, 此後每retain一次引用計數+1, 那麼在程序中做相應次數的release就好了.
  2. (Reference Counted)手動內存計數:就是說,從一段內存被申請之後,就存在一個變量用於保存這段內存被使用的次數,我們暫時把它稱爲計數器,當計數器變爲0的時候,那麼就是釋放這段內存的時候。比如說,當在程序A裏面一段內存被成功申請完成之後,那麼這個計數器就從0變成1(我們把這個過程叫做alloc),然後程序B也需要使用這個內存,那麼計數器就從1變成了2(我們把這個過程叫做retain)。緊接着程序A不再需要這段內存了,那麼程序A就把這個計數器減1(我們把這個過程叫做release);程序B也不再需要這段內存的時候,那麼也把計數器減1(這個過程還是release)。當系統(也就是Foundation)發現這個計數器變成了0,那麼就會調用內存回收程序把這段內存回收(我們把這個過程叫做dealloc)。順便提一句,如果沒有Foundation,那麼維護計數器,釋放內存等等工作需要你手工來完成。
  解決:一般是由類的靜態方法創建的, 函數名中不會出現alloc或init字樣, 如[NSString string]和[NSArray arrayWithObject:], 創建後引用計數+0, 在函數出棧後釋放, 即相當於一個棧上的局部變量. 當然也可以通過retain延長對象的生存期.
  3. (NSAutoRealeasePool)內存池:可以通過創建和釋放內存池控制內存申請和回收的時機.
  解決:是由autorelease加入系統內存池, 內存池是可以嵌套的, 每個內存池都需要有一個創建釋放對, 就像main函數中寫的一樣. 使用也很簡單, 比如[[[NSString alloc]initialWithFormat:@”Hey you!”] autorelease], 即將一個NSString對象加入到最內層的系統內存池, 當我們釋放這個內存池時, 其中的對象都會被釋放.
 
 
 8)淺複製和深複製的區別
淺層複製:只複製指向對象的指針,而不復制引用對象本身。
深層複製:複製引用對象本身。
意思就是說我有個A對象,複製一份後得到A_copy對象後,對於淺複製來說,A和A_copy指向的是同一 個內存資源,複製的只不過是是一個指針,對象本身資源 還是隻有一份,那如果我們對A_copy執行了修改操作,那麼發現A引用的對象同樣被修改,這其實違背 了我們複製拷貝的一個思想。深複製就好理解了,內存中存在了
 
兩份獨立對象本身。
用網上一哥們通俗的話將就是:
淺複製好比你和你的影子,你完蛋,你的影子也完蛋
深複製好比你和你的克隆人,你完蛋,你的克隆人還活着。
 
深淺拷貝前提是:是實現NSCopying或者NSMutableCopying協議。
淺拷貝只是複製對象本身,對象的屬性和包含的對象不做複製。
深拷貝則對對象本身複製,同時對對象的屬性也進行復制。
深淺拷貝的本質區別是對象或者對象屬性的內存地址是否一樣,一樣則爲淺拷貝,不一樣則爲深拷貝。
Foundation框架支持複製的類,默認是淺拷貝。其中對Foundation中不可變的對象進行copy時作用相當於retain。
而如果是mutablecopy時,無論對象是否可變,副本是可變的,並且實現了真正意義上的copy。如果對可變對象進行copy,
副本對象是不可變的,同樣是真正意義上的copy。
 
3.Objective-C堆和棧的區別?
 
(1)管理方式:對於棧來講,是由編譯器自動管理,無需我們手工控制;對於堆來說,釋放工作由程序員控制,容易產生 memory leak。
 (2)申請大小:能從棧獲得的空間較小,堆是向高地址擴展的數據結構,是不連續的內存區域。堆的大小受限於計算機系統中 有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。
 (3)碎片問題:對於堆來講,頻繁的new/delete勢必會造成內存空間的不連續,從而造成大量的碎片,使程序效率降低。 對於棧來講,則不會存在這個問題,因爲棧是先進後出的隊列,他們是如此的一一對應,以至於永遠都不可能有一個內存塊 從棧中間彈出
 (4)分配方式:堆都是動態分配的,沒有靜態分配的堆。棧有2種分配方式:靜態分配和動態分配。靜態分配是編譯器完成 的,比如局部變量的分配。動態分配由 alloca函數進行分配,但是棧的動態分配和堆是不同的,他的動態分配是由編譯器 進行釋放,無需我們手工實現。
 (5)分配效率:棧是機器系統提供的數據結構,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧 都有專門的指令執行,這就決定了棧的效率比較高。堆則是C/C++函數庫提供的,它的機制是很複雜的。
 
 
6.debug 和內存分析工具
 
衆所周知,我們進行iOS開發,在Xcode調試程序時,分爲兩種方式,Debug和Release,在Target的Setting中相信大家應該看到很多選項都分爲Debug和Release,方便我們分別設置,滿足調試和發佈的不同需求。
Release是發行版本,比Debug版本有一些優化,文件比Debug文件小 Debug是調試版本,Debug和Release調用兩個不同的底層庫。通俗點講,我們開發者自己內部真機或模擬器調試時,使用Debug模式就好,等到想要發佈時,也就是說需要大衆客戶使用時,需要build Release版本,具體區別如下:
一、Debug是調試版本,包括的程序信息更多
二、只有Debug版的程序才能設置斷點、單步執行、使用TRACE/ASSERT等調試輸出語句
三、Release不包含任何調試信息,所以體積小、運行速度快
 
內存分析工具:
1、Analyze  用XCode的Analyze就能分析到哪裏有內存泄露
2、分析內存泄露不能把所有的內存泄露查出來,有的內存泄露是在運行時,用戶操作時才產生的。那就需要用到Instruments了。
 Leak工具可以很容易的統計所有內存泄漏的點,而且還可以顯示在哪個文件,哪行代碼有內存泄漏,這樣定位問題比較容易,也比較方便;但是Leak在統計內存泄漏的時候會把autorelease方式的內存也統計進來; 所以我們在查找內存泄漏情況的時候,可以autorelease的情況忽略掉;
 
 
 使用Xcode和Instruments調試解決iOS內存泄露
 
3.屬性readwrite,readonly,assign,retain,copy,nonatomic 各是什麼作用,在那種情況下用?
①. readwrite 是可讀可寫特性;需要生成getter方法和setter方法時
②. readonly 是隻讀特性 只會生成getter方法 不會生成setter方法 ;不希望屬性在類外改變
③. assign 是賦值特性,setter方法將傳入參數賦值給實例變量;僅設置變量時;
④. retain 表示持有特性,setter方法將傳入參數先保留,再賦值,傳入參數的retaincount會+1;
⑤. copy 表示賦值特性,setter方法將傳入對象複製一份;需要完全一份新的變量時。
⑥. nonatomic 非原子操作,決定編譯器生成的setter getter是否是原子操作,atomic表示多線程安全,一般使用nonatomic
 
4.寫一個setter方法用於完成@property (nonatomic,retain)NSString *name,寫一個setter方法用於完成@property(nonatomic,copy)NSString *name
- (void) setName:(NSString*) str
{
  [str retain];
  [name release];
  name = str;
}
- (void)setName:(NSString *)str
{
  id t = [str copy];
  [name release];
  name = t;
}
 
8.Objective-C如何對內存管理的,說說你的看法和解決方法?
Objective-C的內存管理主要有三種方式ARC(自動內存計數)、手動內存計數、內存池。
①.自動內存計數:這種方式和java類似,在你的程序的執行過程中。始終有一個高人在背後準確地幫你收拾垃圾,你不用考慮它什麼時候開始工作,怎樣工作。你只需要明白,我申請了一段內存空間,當我不再使用從而這段內存成爲垃圾的時候,我就徹底的把它忘記掉,反正那個高人會幫我收拾垃圾。遺憾的是,那個高人需要消耗一定的資源,在攜帶的移動設備裏面,資源是緊俏商品所以iPhone不支持這個功能。
解決: 通過alloc – init方式創建的, 創建後引用計數+1, 此後每retain一次引用計數+1, 那麼在程序中做相應次數的release就好了.
②. (Reference Counted)手動內存計數:就是說,從一段內存被申請之後,就存在一個變量用於保存這段內存被使用的次數,我們暫時把它稱爲計數器,當計數器變爲0的時候,那麼就是釋放這段內存的時候。比如說,當在程序A裏面一段內存被成功申請完成之後,那麼這個計數器就從0變成1(我們把這個過程叫做alloc),然後程序B也需要使用這個內存,那麼計數器就從1變成了2(我們把這個過程叫做retain)。緊接着程序A不再需要這段內存了,那麼程序A就把這個計數器減1(我們把這個過程叫做release);程序B也不再需要這段內存的時候,那麼也把計數器減1(這個過程還是release)。當系統(也就是Foundation)發現這個計數器變成了0,那麼就會調用內存回收程序把這段內存回收(我們把這個過程叫做dealloc)。順便提一句,如果沒有Foundation,那麼維護計數器,釋放內存等等工作需要你手工來完成。
解決:一般是由類的靜態方法創建的, 函數名中不會出現alloc或init字樣, 如[NSString string]和[NSArray arrayWithObject:], 創建後引用計數+0, 在函數出棧後釋放, 即相當於一個棧上的局部變量. 當然也可以通過retain延長對象的生存期.
③. (NSAutoRealeasePool)內存池:可以通過創建和釋放內存池控制內存申請和回收的時機.
解決:是由autorelease加入系統內存池, 內存池是可以嵌套的, 每個內存池都需要有一個創建釋放對, 就像main函數中寫的一樣. 使用也很簡單, 比如[[[NSString alloc]initialWithFormat:@”Hey you!”] autorelease], 即將一個NSString對象加入到最內層的系統內存池, 當我們釋放這個內存池時, 其中的對象都會被釋放.
 
9. 原子(atomic)跟非原子(non-atomic)屬性有什麼區別?
①. atomic提供多線程安全。是防止在寫未完成的時候被另外一個線程讀取,造成數據錯誤
②. non-atomic:在自己管理內存的環境中,解析的訪問器保留並自動釋放返回的值,如果指定了 nonatomic ,那麼訪問器只是簡單地返回這個值。
 
10.Object C中創建線程的方法是什麼?如果在主線程中執行代碼,方法是什麼?如果想延時執行代碼、方法又是什麼?
線程創建有三種方法:使用NSThread創建、使用GCD的dispatch、使用子類化的NSOperation,然後將其加入NSOperationQueue;在主線程執行代碼,方法是performSelectorOnMainThread,如果想延時執行代碼可以用performSelector:onThread:withObject:waitUntilDone:
 
11. 淺複製和深複製的區別?
答案:淺層複製:只複製指向對象的指針,而不復制引用對象本身。
深層複製:複製引用對象本身。
意思就是說我有個A對象,複製一份後得到A_copy對象後,對於淺複製來說,A和A_copy指向的是同一個內存資源,複製的只不過是是一個指針,對象本身資源
還是隻有一份,那如果我們對A_copy執行了修改操作,那麼發現A引用的對象同樣被修改,這其實違背了我們複製拷貝的一個思想。深複製就好理解了,內存中存在了
兩份獨立對象本身。
用網上一哥們通俗的話將就是:
淺複製好比你和你的影子,你完蛋,你的影子也完蛋
深複製好比你和你的克隆人,你完蛋,你的克隆人還活着。
 
 
20.簡述OC中內存管理機制。與retain配對使用的方法是dealloc還是release,爲什麼?需要與alloc配對使用的方法是dealloc還是release,爲什麼?readwrite,readonly,assign,retain,copy,nonatomic 、atomic、strong、weak屬性的作用? (OC第九講 內存管理初級)
OC使用了一種叫做引用計數的機制來管理對象,如果對一個對象使用了alloc、[Mutable]copy,retain,那麼你必須使用相應的realease或者autorelease。也可以理解爲自己生成的對象,自己持有。非自己生成的對象,自己也能持有。不在需要自己持有的對象時釋放。非自己持有的對象無法釋放。生成並持有對象<alloc,new,copy,mutableCopy等>,持有對象<retain>,釋放對象<release>,廢棄對象<dealloc>。readwrite(默認):可讀可寫,表示既有getter方法,也有setter方法。readonly:表示只有getter方法,沒有setter方法。nonatomic:不考慮線程安全。atomic(默認):線程操作安全。strong(默認):ARC下和MRC下retain一樣,weak(ARC下):和(MRC下)assign類似,區別是當weak指向的內存釋放掉後自動置爲nil,防止野指針。
unsafe_unretained聲明一個若引用,但不會自動置爲nil,可能會出現野指針。
線程安全下的setter和getter方法:
-(NSString *)value{
       @synchronized(self){
       return [[_value retain] autorelease];
       }
}
- (void)setValue:(NSString *)aValue{
       @synchronized(self){
              [aValue retain];
              [_value release];
              _value = aValue;
       }
}
64.同一個工程中ARC和非ARC 的如何混合使用?
點擊項目->TARGETS->Build Phases->Compile  Sources中選擇要改的.m,雙擊,在標籤中添加:
1.如果是ARC項目,要加入非ARC的代碼文件:-fobjc-arc
2.如果是非ARC,要加入ARC的代碼:-fno-objc-arc
回車
 
43.內存中的棧和堆的區別是什麼?
管理方式:對於棧來講,是由編譯器自動管理的,無需我們手動控制,對於堆來講,釋放工作有程序猿控制,這樣就容易產生memory Leak
 
申請大小: 棧是向低地址擴展的數據結構,是一塊連續的內存區域。這句話的意思是棧頂上的地址和棧的最大容量是系統預先規定好的,在Windows下,棧的大小是2M(也有的說1M,總之是編譯器確定的一個常數),如果申請的空間超過了棧的剩餘空間時候,就overflow。因此,能獲得棧的空間較小。
堆:堆是向高地址擴展的數據結構,是不連續的內存區域。這是由於系統是用鏈表來存儲的空閒內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大笑受限於計算機系統中有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大
 
碎片問題:
對於堆來講,頻繁的new/delete勢必會造成內存空間的不連續,從而造成大量的碎片,使程序效率降低。對於棧來講,則不會存在這個問題,因爲棧是先進後出的隊列,他們是如此的一一對應,以至於永遠都不可能有一個內存快從棧中彈出
 
分配方式:
堆都是動態分配的,沒有靜態分配的堆。棧有兩種分配方式:靜態分配和動態分配。靜態分配是編譯器完成的,比如局部變量的分配。動態分配是有alloc函數進行分配的,但是棧的動態分配和堆是不同的,他的動態分配由編譯器進行釋放,無需我們手工實現。
 分配效率:
棧是機器系統提供的數據結構,計算機會在底層堆棧提供支持,分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執行,這就決定了棧的效率比較高。堆則是C/C++函數庫提供的,他的機制是很複雜的。
 
3、iOS有沒有垃圾回收?
iOS沒有垃圾回收機制。只有系統在應用程序退出的時候會在適當的時候回收所有的東西。ios5以後有arc,可以自動添加管理內存的代碼,但與垃圾回收不是一個東西。一個需要自己管理,一個不需要。
其實也可以垃圾回收,但是這樣的話對性能有影響,而且手機不像電腦,對性能要求嚴格,所以蘋果果斷沒有用垃圾回收。把內存的煩惱留程序員,但爲了照顧程序員,又發明了arc。
 
iOS開發系列—Objective-C之內存管理
11. 內存池的概念詳細解釋一下
 
         內存池:內存池就是用來存放當前創建的多有對象的空間位置,當我們在創建一個實例對象以後系統會自動的把內容放到內存池中,當我們在使用這些對象的時候就會在內存中自動的去執行retain,copy,release操作,當實例對象的引用計數爲0時就會自動的執行dealloc方法來銷燬實例對象.
在內存池中也會有runloop作用是來監察每個實例對象的引用計數.
 
31.如何優化內存?
 
       (1). 用ARC管理內存
       ARC(Automatic ReferenceCounting, 自動引用計數)和iOS5一起發佈,它避免了最常見的也就是經常是由於我們忘記釋放內存所造成的內存泄露。它自動爲你管理retain和release的過程,除了幫你避免內存泄露,ARC還可以幫你提高性能,它能保證釋放掉不再需要的對象的內存。
       (2). 在正確的地方使用reuseIdentifier
       一個開發中常見的錯誤就是沒有給UITableViewCells, UICollectionViewCells,甚至是UITableViewHeaderFooterViews設置正確的reuseIdentifier。
 
       (3).儘量把views設置爲透明
       如果你有透明的Views你應該設置它們的opaque屬性爲YES。
       原因是這會使系統用一個最優的方式渲染這些views。如果設爲YES,渲染系統就認爲這個view是完全不透明的,這使得渲染系統優化一些渲染過程和提高性能。如果設置爲NO,渲染系統正常地和其它內容組成這個View。默認值是YES。
 
       (4).避免過於龐大的XIB
       當你加載一個XIB的時候所有內容都被放在了內存裏,包括任何圖片。如果有一個不會即刻用到的view,你這就是在浪費寶貴的內存資源了。
       (5).不要阻塞主線程
       永遠不要使主線程承擔過多。因爲UIKit在主線程上做所有工作,渲染,管理觸摸反應,迴應輸入等都需要在它上面完成。
       一直使用主線程的風險就是如果你的代碼真的block了主線程,你的app會失去反應。
       大部分阻礙主進程的情形是你的app在做一些牽涉到讀寫外部資源的I/O操作,比如存儲或者網絡。
       (6). 在Image Views中調整圖片大小
       如果要在`UIImageView`中顯示一個來自bundle的圖片,你應保證圖片的大小和UIImageView的大小相同。在運行中縮放圖片是很耗費資源的,特別是`UIImageView`嵌套在`UIScrollView`中的情況下。
       如果圖片是從遠端服務加載的你不能控制圖片大小,比如在下載前調整到合適大小的話,你可以在下載完成後,最好是用background thread,縮放一次,然後在UIImageView中使用縮放後的圖片。
       (7). 選擇正確的Collection
       學會選擇對業務場景最合適的類或者對象是寫出能效高的代碼的基礎。當處理collections時這句話尤其正確。
       一些常見collection的總結:
       · Arrays: 有序的一組值。使用index來lookup很快,使用value lookup很慢,插入/刪除很慢。
       ·Dictionaries: 存儲鍵值對。用鍵來查找比較快。
       · Sets: 無序的一組值。用值來查找很快,插入/刪除很快。
 
       (8). 打開gzip壓縮
       大量app依賴於遠端資源和第三方API,你可能會開發一個需要從遠端下載XML, JSON, HTML或者其它格式的app。
       問題是我們的目標是移動設備,因此你就不能指望網絡狀況有多好。一個用戶現在還在edge網絡,下一分鐘可能就切換到了3G。不論什麼場景,你肯定不想讓你的用戶等太長時間。
減小文檔的一個方式就是在服務端和你的app中打開gzip。這對於文字這種能有更高壓縮率的數據來說會有更顯著的效用。
       好消息是,iOS已經在NSURLConnection中默認支持了gzip壓縮,當然AFNetworking這些基於它的框架亦然。像Google App Engine這些雲服務提供者也已經支持了壓縮輸出。
 
       (9). 重用和延遲加載(lazy load) Views
       更多的view意味着更多的渲染,也就是更多的CPU和內存消耗,對於那種嵌套了很多view在   UIScrollView裏邊的app更是如此。
       這裏我們用到的技巧就是模仿`UITableView`和`UICollectionView`的操作:不要一次創建所有的subview,而是當需要時才創建,當它們完成了使命,把他們放進一個可重用的隊列中。
       這樣的話你就只需要在滾動發生時創建你的views,避免了不划算的內存分配。
創建views的能效問題也適用於你app的其它方面。想象一下一個用戶點擊一個按鈕的時候需要呈現      一個view的場景。有兩種實現方法:
       1. 創建並隱藏這個view當這個screen加載的時候,當需要時顯示它;
       2. 當需要時才創建並展示。
       每個方案都有其優缺點。用第一種方案的話因爲你需要一開始就創建一個view並保持它直到不再使用,這就會更加消耗內存。然而這也會使你的app操作更敏感因爲當用戶點擊按鈕的時候它只需要改變一下這個view的可見性。
       第二種方案則相反-消耗更少內存,但是會在點擊按鈕的時候比第一種稍顯卡頓。
       (10). Cache, Cache, 還是Cache!
       一個極好的原則就是,緩存所需要的,也就是那些不大可能改變但是需要經常讀取的東西。
我們能緩存些什麼呢?一些選項是,遠端服務器的響應,圖片,甚至計算結果,比如UITableView的行高。
       NSURLConnection默認會緩存資源在內存或者存儲中根據它所加載的HTTP Headers。你甚至可以手動創建一個NSURLRequest然後使它只加載緩存的值。
 
       (11).權衡渲染方法
       在iOS中可以有很多方法做出漂亮的按鈕。你可以用整幅的圖片,可調大小的圖片,uozhe可以用CALayer, CoreGraphics甚至OpenGL來畫它們。
       當然每個不同的解決方法都有不同的複雜程度和相應的性能。
       簡單來說,就是用事先渲染好的圖片更快一些,因爲如此一來iOS就免去了創建一個圖片再畫東西上去然後顯示在屏幕上的程序。問題是你需要把所有你需要用到的圖片放到app的bundle裏面,這樣就增加了體積–這就是使用可變大小的圖片更好的地方了:你可以省去一些不必要的空間,也不需要再爲不同的元素(比如按鈕)來做不同的圖。
       然而,使用圖片也意味着你失去了使用代碼調整圖片的機動性,你需要一遍又一遍不斷地重做他們,這樣就很浪費時間了,而且你如果要做一個動畫效果,雖然每幅圖只是一些細節的變化你就需要很多的圖片造成bundle大小的不斷增大。
       總得來說,你需要權衡一下利弊,到底是要性能能還是要bundle保持合適的大小。
 
       (12).處理內存警告
       一旦系統內存過低,iOS會通知所有運行中app。如果你的app收到了內存警告,它就需要儘可能釋       放更多的內存。最佳方式是移除對緩存,圖片object和其他一些可以重創建的objects的strong references.
       幸運的是,UIKit提供了幾種收集低內存警告的方法:
       · 在app delegate中使用`applicationDidReceiveMemoryWarning:`的方法
       · 在你的自定義UIViewController的子類(subclass)中覆蓋`didReceiveMemoryWarning`
       · 註冊並接收UIApplicationDidReceiveMemoryWarningNotification的通知
       一旦收到這類通知,你就需要釋放任何不必要的內存使用。
       例如,UIViewController的默認行爲是移除一些不可見的view,它的一些子類則可以補充這個方法,刪掉一些額外的數據結構。一個有圖片緩存的app可以移除不在屏幕上顯示的圖片。
 
       (13).重用大開銷對象
       一些objects的初始化很慢,比如NSDateFormatter和NSCalendar。然而,你又不可避免地需要使用它們,比如從JSON或者XML中解析數據。
       想要避免使用這個對象的瓶頸你就需要重用他們,可以通過添加屬性到你的class裏或者創建靜態變量來實現。
       注意如果你要選擇第二種方法,對象會在你的app運行時一直存在於內存中,和單例(singleton)很相似。
       還需要注意的是,其實設置一個NSDateFormatter的速度差不多是和創建新的一樣慢的!所以如果你的app需要經常進行日期格式處理的話,你會從這個方法中得到不小的性能提升。
 
       (14). 減少使用Web特性
       UIWebView很有用,用它來展示網頁內容或者創建UIKit很難做到的動畫效果是很簡單的一件事。
       但是你可能有注意到UIWebView並不像驅動Safari的那麼快。這是由於以JIT compilation爲特色的Webkit的Nitro Engine的限制。
       所以想要更高的性能你就要調整下你的HTML了。第一件要做的事就是儘可能移除不必要的 javascript,避免使用過大的框架。能只用原生js就更好了。
       另外,儘可能異步加載例如用戶行爲統計script這種不影響頁面表達的javascript。
       最後,永遠要注意你使用的圖片,保證圖片的符合你使用的大小。使用Sprite sheet提高加載速度和節約內存。
 
       (15). 優化Table View
       Table view需要有很好的滾動性能,不然用戶會在滾動過程中發現動畫的瑕疵。
       爲了保證table view平滑滾動,確保你採取了以下的措施:
       · 正確使用`reuseIdentifier`來重用cells
       · 儘量使所有的view opaque,包括cell自身
       · 避免漸變,圖片縮放,後臺選人
       · 緩存行高
       · 如果cell內現實的內容來自web,使用異步加載,緩存請求結果
       · 使用`shadowPath`來畫陰影
       · 減少subviews的數量
       · 儘量不適用`cellForRowAtIndexPath:`,如果你需要用到它,只用一次然後緩存結果
       · 使用正確的數據結構來存儲數據
       · 使用`rowHeight`, `sectionFooterHeight`和 `sectionHeaderHeight`來設定固定的高,不要請求delegate
       (16). 使用Autorelease Pool
       `NSAutoreleasePool`負責釋放block中的autoreleased objects。一般情況下它會自動被UIKit調用。但是有些狀況下你也需要手動去創建它。
       假如你創建很多臨時對象,你會發現內存一直在減少直到這些對象被release的時候。這是因爲只有當UIKit用光了autorelease pool的時候memory纔會被釋放。好消息是你可以在你自己的    @autoreleasepool裏創建臨時的對象來避免這個行爲:
       NSArray *urls = <# An array of file URLs #>;
       for(NSURL *url in urls) {
       @autoreleasepool {
       NSError *error;
       NSString *fileContents = [NSString stringWithContentsOfURL:url              encoding:NSUTF8StringEncoding error:&error];
 
       /* Process the string, creating and autoreleasing more objects. */
 
       }
 
}
       這段代碼在每次遍歷後釋放所有autorelease對象
 
       (17). 選擇是否緩存圖片
       常見的從bundle中加載圖片的方式有兩種,一個是用`imageNamed`,二是用`imageWithContentsOfFile`,第一種比較常見一點。
       既然有兩種類似的方法來實現相同的目的,那麼他們之間的差別是什麼呢?
       `imageNamed`的優點是當加載時會緩存圖片。`imageNamed`的文檔中這麼說:這個方法用一個指定的名字在系統緩存中查找並返回一個圖片對象如果它存在的話。如果緩存中沒有找到相應       的圖片,這個方法從指定的文檔中加載然後緩存並返回這個對象。
       相反的,`imageWithContentsOfFile`僅加載圖片。
       如果你要加載一個大圖片而且是一次性使用,那麼就沒必要緩存這個圖片,用    `imageWithContentsOfFile`足矣,這樣不會浪費內存來緩存它。
         然而,在圖片反覆重用的情況下`imageNamed`是一個好得多的選擇
 
6.OCalloc,retain,copy,release,autorelease的含義是什麼?,請寫出下面函數中帶有標號的執行後tracker的引用計數。
 
int main(int argc, const char *argv[])
{
    NSAutoreleasePool *pool;
    pool = [[NSAutoreleasePool alloc] init];
    RetainTracker *tracker;
    tracker = [RetainTracker new];  (1)
    RetainTracker *otherTrecker = [tracker copy];      (2)
    [tracker retain];        (3)
    [tracker autorelease];       (4)
    [tracker release];     (5)
    [otherTrecker release];     (6)
    [pool release];
    return 0;
}
 
 alloc 初始化開闢內存空間,自己的retainCount爲一;
 retain 是把該對象的retainCount 加一;
 copy 是拷貝一個新的對象, alloc, 使新對象的retainCount值爲一.
 
releaseautorelease是使retainCount減一的操作, 你自己加一的操作用完了一定要減一就行了,
releaseautorelease在這一點上是一樣的, 不過release是馬上就減一,autorelease是在當前的循環結束後才統一減1.
 
 
執行過程中tracker的引用計數分別爲:
11
21
32
42
51
61
 
arc的程序出現內存泄露怎辦
實例一:
用arc和非arc混編,非arc的類在arc裏實例化並且使用,在arc里居然出現內存泄露,而且應爲是arc,所以無法使用release,autorelease和dealloc去管理內存。正常情況下應該是不會出現這種情況的,某一個類若是ARC,則在這個類裏面都應該遵循ARC的用法,而無需關心用到的類是否是ARC的,同樣,在非ARC類裏面,就需要遵循內存管理原則。
 
用ARC,只是編譯器幫你管理了何時去release,retain,不用ARC就需要你自己去管理,說到底只是誰去管理的問題,所以你再好好看看,可能問題與ARC無關。
如果實在找不到問題,建議你找到泄露的那個對象,將其賦值爲nil,因爲ARC裏面,一旦對象沒有指針指向,就會馬上被釋放。
 
Objective-c中提供了兩種內存管理機制MRC(MannulReference Counting)和 ARC(Automatic Reference Counting),分別提供對內存的⼿手動和⾃自動管理,來滿 ⾜足不同的需求。注意的是Xcode 4.1及其以前版本沒有ARC。需要理解MRC,但 實際使⽤用時強推ARC。
.ARC機制的使⽤用規則
(1)不能調⽤用dealloc,不能重寫和調⽤用retain,release,retainCount 和autorelease, 同理,@selector(retain),@selector(release)這些曲線救國的⽅方法也不能調⽤用。 dealloc雖然能夠重寫,但是不能調⽤用[super dealloc]之類的⽅方法,CoreFoundation 框架由於⾮非從屬cocoa框架,所以CFRetain和CFRelease仍然正常使⽤用。
(2)不能使⽤用NSAllocateObjec或NSDeallocateObject函數來創建對象
(3)不能在C語⾔言的結構體中使⽤用對象指針,同時建議⽤用object-c的類來管理數 據⽽而不是結構體
(4)不得使⽤用NSAutoreleasePool對象。ARC中,全部使⽤用@autorelease關鍵字代 替,且⽐比NSAutoreleasePool更⾼高效
(5)不得使⽤用內存Zone,那些牽涉NSZone的⽅方法都不得使⽤用。 (6)不得對⼀一個屬性變量的取值⽅方法命名以new開頭(7)outlet(xib、storyBoard拖得屬性)均⽤用weak關鍵字修飾,除⾮非他是xib中最頂
部的界⾯面元素,則需要strong。
(8)Core Foundation不適合ARC,該創建的仍創建,該釋放的仍釋放。
 
IOS的ARC會導致的內存泄露問題和解決方案
iOS提供了ARC功能,很大程度上簡化了內存管理的代碼。
但使用ARC並不代表了不會發生內存泄露,使用不當照樣會發生內存泄露。
下面列舉兩種ARC導致內存泄露的情況。
1,循環參照
A有個屬性參照B,B有個屬性參照A,如果都是strong參照的話,兩個對象都無法釋放。
這種問題常發生於把delegate聲明爲strong屬性了。
2,死循環
如果某個ViewController中有無限循環,也會導致即使ViewController對應的view關掉了,ViewController也不能被釋放。
這種問題常發生於animation處理。
 
 
衆所周知,我們進行iOS開發,在Xcode調試程序時,分爲兩種方式,DebugRelease,在TargetSetting中相信大家應該看到很多選項都分爲DebugRelease,方便我們分別設置,滿足調試和發佈的不同需求。
Release是發行版本,Debug版本有一些優化,文件比Debug文件小 Debug是調試版本,DebugRelease調用兩個不同的底層庫。通俗點講,我們開發者自己內部真機或模擬器調試時,使用Debug模式就好,等到想要發佈時,也就是說需要大衆客戶使用時,需要build Release版本,具體區別如下:
一、Debug是調試版本,包括的程序信息更多
二、只有Debug版的程序才能設置斷點、單步執行、使用TRACE/ASSERT等調試輸出語句
三、Release不包含任何調試信息,所以體積小、運行速度快
 
內存分析工具:
1Analyze  XCodeAnalyze就能分析到哪裏有內存泄露
2、分析內存泄露不能把所有的內存泄露查出來,有的內存泄露是在運行時,用戶操作時才產生的。那就需要用到Instruments了。
 Leak工具可以很容易的統計所有內存泄漏的點,而且還可以顯示在哪個文件,哪行代碼有內存泄漏,這樣定位問題比較容易,也比較方便;但是Leak在統計內存泄漏的時候會把autorelease方式的內存也統計進來; 所以我們在查找內存泄漏情況的時候,可以autorelease的情況忽略掉;
 
 
5.調用一個類的靜態方法需不需要release
 
調用一個類的靜態方法不需要release,其實靜態成員方法也是有對象的,叫做類對象,但這個是在第一次訪問類成員時觸發系統將其加載到內存的,而且該類對象只在程序立閉時,纔會釋放,並不由程序本身控制。


在2016年3月29日 18:41:39出現衝突的修改:
簡述OC中內存管理機制。與retain配對使用的方法是dealloc還是release,爲什麼?需要與alloc配對使用的方法是dealloc還是release,爲什麼?readwritereadonly,assign,retain,copy,nonatomic,atomic,strong,weak屬性的作用?
OC中內存管理機制應該就是引用計數機制,retainCount0時釋放該內存。
retain對應的是release,內存的釋放用release
alloc對應的是dealloc,內存的銷燬用dealloc
readwrite此標記說明屬性會被當成讀寫的,這也是默認屬性。
readonly此標記說明屬性只可以讀,也就是不能設置,可以獲取。
assign不會使引用計數加1,也就是直接賦值。
retain會使引用計數加1
copy建立一個索引計數爲1的對象,在賦值時使用傳入值的一份拷貝。
nonatomic:非原子性訪問,多線程併發訪問會提高性能。
atomic:原子性訪問。
strong:打開ARC時纔會使用,相當於retain
weak:打開ARC時纔會使用,相當於assign,可以把對應的指針變量置爲nil
 
什麼叫做循環引用
   對象A創建並引用到了對象B,對象B創建並引用到了對象C,對象C創建並引用到了對象B,這個時候B的引用計數是2,而C的引用計數是1,當A不用B的時候,就釋放了B的所有權,這個時候C還引用對象B,所有B不會釋放,引用計數爲1;因爲B也引用着對象C,B不釋放,那麼C就不會被釋放,所以它們的引用計數都爲1,並且永遠不會被釋放,所以形成了循環引用。
 
1.分別寫出MRC中在assign、retain、copy下屬性name對應的setter方法的內部實現。
assign下
- (void) setName:(NSString*)name
{

      _name = name;
}
retain下
- (void) setName:(NSString*)name
{
      if(_name != name){
       [_name release];
       _name = [name retain];
       }
}
 
copy下
- (void) setName:(NSString*)name
{
      if(_name != name){
       [_name release];
       _name = [name copy];
       }
}
 
2.ARCdealloc方法存在的意義在於什麼地方?舉例說明一下具體的使用場景。
答:其實在MRCdealloc方法存在的主要意義是爲了釋放自身的實例變量,移除觀察者,停止timer,移除通知,代理置空等。ARC下,系統會幫助我們釋放該對象所包含的實例變量,但是有些對象還是需要們自己去釋放的(比如Core Foundation框架下的一些對象),另外通知中觀察者的移除,代理置空,停止timer
示例如下所示:
 
- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];//移除通知觀察者
    [[XMPPManager sharedManager] removeFromDelegateQueue:self];//移除委託引用
    [[MyClass shareInstance]  doSomething ]//其他操作
    scrollView.delegate = nil;
    [timer invalidate]; 
}
 
3.@synthesize@dynamic有什麼區別?
(1)@property有兩個對應的詞,一個是@synthesize,一個是@dynamic。如果@synthesize@dynamic都沒寫,那麼默認的就是@syntheszie var = _var;
(2)@synthesize的語義是如果你沒有手動實現setter方法和getter方法,那麼編譯器會自動爲你加上這兩個方法。
(3)@dynamic告訴編譯器:屬性的settergetter方法由用戶自己實現,不自動生成。(當然對於readonly的屬性只需提供getter即可)。假如一個屬性被聲明爲@dynamic var,然後你沒有提供@setter方法和@getter方法,編譯的時候沒問題,但是當程序運行到instance.var = someVar,由於缺setter方法會導致程序崩潰;或者當運行到 someVar = instance.var時,由於缺getter方法同樣會導致崩潰。編譯時沒問題,運行時才執行相應的方法,這就是所謂的動態綁定。@dynamic可用於在分類中添加屬性(需要用到objc_getAssociatedObjectobjc_setAssociatedObject函數)。
 
4.你如何看待iOS中的拷貝?
答:在我看來,日常生活中,當我們用到拷貝這個詞語的時候,不管怎樣都會產生兩份。一份是原來的,一份是新拷貝出來的。但是到目前爲止,在iOS中我看到了三種拷貝方式:
(1)僞拷貝:僞拷貝,顧名思義,就是假拷貝,沒有拷貝出新的對象。這一點對於NSString這種類簇來說比較常見,NSString本身是不可變字符串,內容不可能被修改,因此我們也沒有拷貝的必要,因爲拷貝的目的是防止原件被修改,所以才拷貝出來一份,僞拷貝實際上是對象引用計數加了1(相當於retain或者strong的功效)。
(2)淺拷貝:淺拷貝就是確實拷貝出來一份和原來內容一樣的新對象。但是對於對象自帶的屬性是僞拷貝,兩個對象的屬性指向同一個內存。
(3)深拷貝:深拷貝就是不僅僅拷貝出一份新對象,而且對象的屬性也拷貝了一份。
總的來說,如果在開發的過程中需要實現拷貝,那麼需要接受NSCopying協議。實現copyWithZone:方法。淺拷貝、深拷貝的區別在於copyWithZone:方法的實現不同。
 
16.定義屬性時,什麼情況使用copyassign,和retain
答:assign用於簡單數據類型,如NSInteger,double,boolretain copy用於對象類型,二者又有區別,copy是用於一個對象有可能被修改,但不想修改原件,所以拷貝一份出來(新拷貝的對象引用計數爲1),這樣新拷貝的修改了,原件不會變,原件修改了,新拷貝的不會變。而retain呢,對象引用計數+1,對象只有一個,但引用計數爲2,通過任何一個引用修改對象內容,另外一個引用使用的時候,用的就是修改之後的內容。
1、以下代碼有什麼問題嗎?如果沒有問題的話,objobj2的引用計數分別是多少?如果有問題的話存在什麼問題?
 Class *obj = [[Class alloc]init];
 Class *obj2 = obj;
          [obj hello];
          [obj release];  
          [obj2 hello]; 
          [obj2 release];
答:上面的代碼是存在問題的。當執行完第一行代碼時,因爲執行了allocobj的引用計數爲1。第二行代碼執行完之後,obj2只是和obj指向了同一塊內存。第三行代碼是執行了hello方法。第四行代碼執行release消息之後,obj的引用計數減一,這時retainCount變爲0.系統自動調用dealloc方法,對象被銷燬。第五行代碼執行時,obj2執行的內存已經被系統回收了,但還是調用了hello方法,出現了問題(野指針)。第六行執行時,obj2所指向的內存已經不存在,再次調用release消息,出現過度釋放的問題,而且obj2已經變成野指針了。
Class *obj = [[Class alloc]init];//obj引用計數加1
Class *obj2 = obj;//objobj2指向同一塊內存(對象)


         [obj hello];
         [obj release];//obj指向的內存(對象)被銷燬
         [obj2 hello];//錯誤,obj2指向的內存(對象)已經被銷燬了
          [obj2 release];//[obj release]之後,obj2是個野指針,不應該再去調用方法.
2、在實際開發的過程中,什麼情況下需要創建自動釋放池?下面代碼中有沒有什麼問題?
 
  Person *p1=[[Person alloc]init];
  @autoreleasepool {
    [p1 autorelease];
     @autoreleasepool {
         [p1 autorelease];
    }
 
 
答:其實自動釋放池存在的意義是爲了延遲釋放一些對象,延遲向對象發送release消息。在實際的開發中呢,有兩種情況是需要手動創建自動釋放池的。第一個就是在多線程中,因爲子線程中可能會使用便利構造器等方法來創建對象,那麼這些對象的釋放只能放在自動釋放池中,主線程其實已經添加過自動釋放池,在main函數裏面。第二個就是如果一段代碼裏面(比如for循環)大量使用便利構造器創建對象,也需要手動添加自動釋放池。
上述代碼其實是存在問題的。當執行完第一行代碼時,p1的引用計數是1.第二行是創建了一個autoreleasepool。第三行代碼向p1發送了autorelease消息,延遲release,即在出池的時候,把p1釋放掉。第四行代碼又創建了一個autoreleasepool。第五行代碼再次向p1發送了autorelease消息。當代碼執行到第六行的“}”時,第二個自動釋放池結束,這時p1引用技術減1p1所指向的內存(對象)的retainCount1變爲0,內存被系統回收。代碼執行到第七行時,最外層的自動釋放池結束,再次向p1發送release消息,出現過度釋放。
 @autoreleasepool {
    [p1 autorelease];//此時p1被加入自動釋放池1
    @autoreleasepool {
      [p1 autorelease];//此時p1被加入自動釋放池2
    }//此處,自動釋放池2結束,p1引用計數-1
  }//此處,自動釋放池1結束,但是向已經被釋放的對象p1發送了消息
 
4.父類實現深拷貝時,子類如何實現深拷貝 ?父類沒有實現深拷貝時,子類如何實現深度拷貝?
答:父類實現深拷貝之後,子類在重寫的copyWithZone方法,先調用父類的copyWithZone方法,之後實現自己屬性的拷貝。
如果父類沒有實現深拷貝,子類除了需要對自己的屬性進行拷貝,還要對父類的屬性進行拷貝。
 
一、內存管理
ARC下的內存管理
ARC,全稱叫AutomaticReference Counting,該機制從ios5開始開始導入。簡單地說,就是代碼中自動加入了retain/release。所以,其底層機制還是引用計數,
在你打開ARC時,你是不能使用retainrelease autorelease 操作的,原先需要手動添加的用來處理內存管理的引用計數的代碼可以自動地由編譯器完成了,但是你需要在對象屬性上使用weak 和strong, 其中strong就相當於retain屬性,而weak相當於assign,基礎類型還是使用assign。
strong關鍵字:
strong 用來修飾強引用的屬性;對應原來的retain。
該屬性值對應 __strong 關鍵字,即該屬性所聲明的變量將成爲對象的持有者。
lweak關鍵字:
weak 用來修飾弱引用的屬性;對應原來的assign。
但是不同的是當對象被釋放以後,對象自動賦值爲nil;並且,delegate 和 Outlet 蘋果推薦用 weak 屬性來聲明。同時,如上一回介紹的 iOS 5 之前的版本是沒有 __weak 關鍵字的,所以 weak 屬性是不能使用的。這種情況我們使用 unsafe_unretained。
 
IOS的ARC會導致的內存泄露問題和解決方案
iOS提供了ARC功能,很大程度上簡化了內存管理的代碼。
但使用ARC並不代表了不會發生內存泄露,使用不當照樣會發生內存泄露。
下面列舉兩種ARC導致內存泄露的情況。
1,循環參照
A有個屬性參照B,B有個屬性參照A,如果都是strong參照的話,兩個對象都無法釋放。
這種問題常發生於把delegate聲明爲strong屬性了。
2,死循環
如果某個ViewController中有無限循環,也會導致即使ViewController對應的view關掉了,ViewController也不能被釋放。
這種問題常發生於animation處理。
 
5、內存中的棧和堆的區別是什麼?哪些數據在棧上哪些數據在堆上?
(1)管理方式:對於棧來講,是由編譯器自動管理,無需我們手工控制;對於堆來說,釋放工作由程序員控制,容易產生 memory leak。
 (2)申請大小:能從棧獲得的空間較小,堆是向高地址擴展的數據結構,是不連續的內存區域。堆的大小受限於計算機系統中 有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。
 (3)碎片問題:對於堆來講,頻繁的new/delete勢必會造成內存空間的不連續,從而造成大量的碎片,使程序效率降低。 對於棧來講,則不會存在這個問題,因爲棧是先進後出的隊列,他們是如此的一一對應,以至於永遠都不可能有一個內存塊 從棧中間彈出
 (4)分配方式:堆都是動態分配的,沒有靜態分配的堆。棧有2種分配方式:靜態分配和動態分配。靜態分配是編譯器完成 的,比如局部變量的分配。動態分配由 alloca函數進行分配,但是棧的動態分配和堆是不同的,他的動態分配是由編譯器 進行釋放,無需我們手工實現。
 (5)分配效率:棧是機器系統提供的數據結構,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧 都有專門的指令執行,這就決定了棧的效率比較高。堆則是C/C++函數庫提供的,它的機制是很複雜的。
在函數體中定義的變量通常是在棧上,用malloc, calloc, realloc等分配內存的函數分配得到的就是在堆上。
 
12.Objective-C如何對內存管理的,說說你的看法和解決方法?
  Objective-C的內存管理主要有三種方式ARC(自動內存計數)、手動內存計數、內存池。
  1. (Garbage Collection)自動內存計數:這種方式和java類似,在你的程序的執行過程中。始終有一個高人在背後準確地幫你收拾垃圾,你不用考慮它什麼時候開始工作,怎樣工作。你只需要明白,我申請了一段內存空間,當我不再使用從而這段內存成爲垃圾的時候,我就徹底的把它忘記掉,反正那個高人會幫我收拾垃圾。遺憾的是,那個高人需要消耗一定的資源,在攜帶設備裏面,資源是緊俏商品所以iPhone不支持這個功能。所以“Garbage Collection”不是本入門指南的範圍,對“Garbage Collection”內部機制感興趣的同學可以參考一些其他的資料,不過說老實話“Garbage Collection”不大適合適初學者研究。
  解決: 通過alloc – initial方式創建的, 創建後引用計數+1, 此後每retain一次引用計數+1, 那麼在程序中做相應次數的release就好了.
  2. (Reference Counted)手動內存計數:就是說,從一段內存被申請之後,就存在一個變量用於保存這段內存被使用的次數,我們暫時把它稱爲計數器,當計數器變爲0的時候,那麼就是釋放這段內存的時候。比如說,當在程序A裏面一段內存被成功申請完成之後,那麼這個計數器就從0變成1(我們把這個過程叫做alloc),然後程序B也需要使用這個內存,那麼計數器就從1變成了2(我們把這個過程叫做retain)。緊接着程序A不再需要這段內存了,那麼程序A就把這個計數器減1(我們把這個過程叫做release);程序B也不再需要這段內存的時候,那麼也把計數器減1(這個過程還是release)。當系統(也就是Foundation)發現這個計數器變成了0,那麼就會調用內存回收程序把這段內存回收(我們把這個過程叫做dealloc)。順便提一句,如果沒有Foundation,那麼維護計數器,釋放內存等等工作需要你手工來完成。
  解決:一般是由類的靜態方法創建的, 函數名中不會出現alloc或init字樣, 如[NSString string]和[NSArray arrayWithObject:], 創建後引用計數+0, 在函數出棧後釋放, 即相當於一個棧上的局部變量. 當然也可以通過retain延長對象的生存期.
  3. (NSAutoRealeasePool)內存池:可以通過創建和釋放內存池控制內存申請和回收的時機.
  解決:是由autorelease加入系統內存池, 內存池是可以嵌套的, 每個內存池都需要有一個創建釋放對, 就像main函數中寫的一樣. 使用也很簡單, 比如[[[NSString alloc]initialWithFormat:@”Hey you!”] autorelease], 即將一個NSString對象加入到最內層的系統內存池, 當我們釋放這個內存池時, 其中的對象都會被釋放.
 
 
 8)淺複製和深複製的區別
淺層複製:只複製指向對象的指針,而不復制引用對象本身。
深層複製:複製引用對象本身。
意思就是說我有個A對象,複製一份後得到A_copy對象後,對於淺複製來說,A和A_copy指向的是同一 個內存資源,複製的只不過是是一個指針,對象本身資源 還是隻有一份,那如果我們對A_copy執行了修改操作,那麼發現A引用的對象同樣被修改,這其實違背 了我們複製拷貝的一個思想。深複製就好理解了,內存中存在了
 
兩份獨立對象本身。
用網上一哥們通俗的話將就是:
淺複製好比你和你的影子,你完蛋,你的影子也完蛋
深複製好比你和你的克隆人,你完蛋,你的克隆人還活着。
 
深淺拷貝前提是:是實現NSCopying或者NSMutableCopying協議。
淺拷貝只是複製對象本身,對象的屬性和包含的對象不做複製。
深拷貝則對對象本身複製,同時對對象的屬性也進行復制。
深淺拷貝的本質區別是對象或者對象屬性的內存地址是否一樣,一樣則爲淺拷貝,不一樣則爲深拷貝。
Foundation框架支持複製的類,默認是淺拷貝。其中對Foundation中不可變的對象進行copy時作用相當於retain。
而如果是mutablecopy時,無論對象是否可變,副本是可變的,並且實現了真正意義上的copy。如果對可變對象進行copy,
副本對象是不可變的,同樣是真正意義上的copy。
 
3.Objective-C堆和棧的區別?
 
(1)管理方式:對於棧來講,是由編譯器自動管理,無需我們手工控制;對於堆來說,釋放工作由程序員控制,容易產生 memory leak。
 (2)申請大小:能從棧獲得的空間較小,堆是向高地址擴展的數據結構,是不連續的內存區域。堆的大小受限於計算機系統中 有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。
 (3)碎片問題:對於堆來講,頻繁的new/delete勢必會造成內存空間的不連續,從而造成大量的碎片,使程序效率降低。 對於棧來講,則不會存在這個問題,因爲棧是先進後出的隊列,他們是如此的一一對應,以至於永遠都不可能有一個內存塊 從棧中間彈出
 (4)分配方式:堆都是動態分配的,沒有靜態分配的堆。棧有2種分配方式:靜態分配和動態分配。靜態分配是編譯器完成 的,比如局部變量的分配。動態分配由 alloca函數進行分配,但是棧的動態分配和堆是不同的,他的動態分配是由編譯器 進行釋放,無需我們手工實現。
 (5)分配效率:棧是機器系統提供的數據結構,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧 都有專門的指令執行,這就決定了棧的效率比較高。堆則是C/C++函數庫提供的,它的機制是很複雜的。
 
 
6.debug 和內存分析工具
 
衆所周知,我們進行iOS開發,在Xcode調試程序時,分爲兩種方式,Debug和Release,在Target的Setting中相信大家應該看到很多選項都分爲Debug和Release,方便我們分別設置,滿足調試和發佈的不同需求。
Release是發行版本,比Debug版本有一些優化,文件比Debug文件小 Debug是調試版本,Debug和Release調用兩個不同的底層庫。通俗點講,我們開發者自己內部真機或模擬器調試時,使用Debug模式就好,等到想要發佈時,也就是說需要大衆客戶使用時,需要build Release版本,具體區別如下:
一、Debug是調試版本,包括的程序信息更多
二、只有Debug版的程序才能設置斷點、單步執行、使用TRACE/ASSERT等調試輸出語句
三、Release不包含任何調試信息,所以體積小、運行速度快
 
內存分析工具:
1、Analyze  用XCode的Analyze就能分析到哪裏有內存泄露
2、分析內存泄露不能把所有的內存泄露查出來,有的內存泄露是在運行時,用戶操作時才產生的。那就需要用到Instruments了。
 Leak工具可以很容易的統計所有內存泄漏的點,而且還可以顯示在哪個文件,哪行代碼有內存泄漏,這樣定位問題比較容易,也比較方便;但是Leak在統計內存泄漏的時候會把autorelease方式的內存也統計進來; 所以我們在查找內存泄漏情況的時候,可以autorelease的情況忽略掉;

 
3.屬性readwrite,readonly,assign,retain,copy,nonatomic 各是什麼作用,在那種情況下用?
①. readwrite 是可讀可寫特性;需要生成getter方法和setter方法時
②. readonly 是隻讀特性 只會生成getter方法 不會生成setter方法 ;不希望屬性在類外改變
③. assign 是賦值特性,setter方法將傳入參數賦值給實例變量;僅設置變量時;
④. retain 表示持有特性,setter方法將傳入參數先保留,再賦值,傳入參數的retaincount會+1;
⑤. copy 表示賦值特性,setter方法將傳入對象複製一份;需要完全一份新的變量時。
⑥. nonatomic 非原子操作,決定編譯器生成的setter getter是否是原子操作,atomic表示多線程安全,一般使用nonatomic
 
4.寫一個setter方法用於完成@property (nonatomic,retain)NSString *name,寫一個setter方法用於完成@property(nonatomic,copy)NSString *name
- (void) setName:(NSString*) str
{
  [str retain];
  [name release];
  name = str;
}
- (void)setName:(NSString *)str
{
  id t = [str copy];
  [name release];
  name = t;
}
 
8.Objective-C如何對內存管理的,說說你的看法和解決方法?
Objective-C的內存管理主要有三種方式ARC(自動內存計數)、手動內存計數、內存池。
①.自動內存計數:這種方式和java類似,在你的程序的執行過程中。始終有一個高人在背後準確地幫你收拾垃圾,你不用考慮它什麼時候開始工作,怎樣工作。你只需要明白,我申請了一段內存空間,當我不再使用從而這段內存成爲垃圾的時候,我就徹底的把它忘記掉,反正那個高人會幫我收拾垃圾。遺憾的是,那個高人需要消耗一定的資源,在攜帶的移動設備裏面,資源是緊俏商品所以iPhone不支持這個功能。
解決: 通過alloc – init方式創建的, 創建後引用計數+1, 此後每retain一次引用計數+1, 那麼在程序中做相應次數的release就好了.
②. (Reference Counted)手動內存計數:就是說,從一段內存被申請之後,就存在一個變量用於保存這段內存被使用的次數,我們暫時把它稱爲計數器,當計數器變爲0的時候,那麼就是釋放這段內存的時候。比如說,當在程序A裏面一段內存被成功申請完成之後,那麼這個計數器就從0變成1(我們把這個過程叫做alloc),然後程序B也需要使用這個內存,那麼計數器就從1變成了2(我們把這個過程叫做retain)。緊接着程序A不再需要這段內存了,那麼程序A就把這個計數器減1(我們把這個過程叫做release);程序B也不再需要這段內存的時候,那麼也把計數器減1(這個過程還是release)。當系統(也就是Foundation)發現這個計數器變成了0,那麼就會調用內存回收程序把這段內存回收(我們把這個過程叫做dealloc)。順便提一句,如果沒有Foundation,那麼維護計數器,釋放內存等等工作需要你手工來完成。
解決:一般是由類的靜態方法創建的, 函數名中不會出現alloc或init字樣, 如[NSString string]和[NSArray arrayWithObject:], 創建後引用計數+0, 在函數出棧後釋放, 即相當於一個棧上的局部變量. 當然也可以通過retain延長對象的生存期.
③. (NSAutoRealeasePool)內存池:可以通過創建和釋放內存池控制內存申請和回收的時機.
解決:是由autorelease加入系統內存池, 內存池是可以嵌套的, 每個內存池都需要有一個創建釋放對, 就像main函數中寫的一樣. 使用也很簡單, 比如[[[NSString alloc]initialWithFormat:@”Hey you!”] autorelease], 即將一個NSString對象加入到最內層的系統內存池, 當我們釋放這個內存池時, 其中的對象都會被釋放.
 
9. 原子(atomic)跟非原子(non-atomic)屬性有什麼區別?
①. atomic提供多線程安全。是防止在寫未完成的時候被另外一個線程讀取,造成數據錯誤
②. non-atomic:在自己管理內存的環境中,解析的訪問器保留並自動釋放返回的值,如果指定了 nonatomic ,那麼訪問器只是簡單地返回這個值。
 
10.Object C中創建線程的方法是什麼?如果在主線程中執行代碼,方法是什麼?如果想延時執行代碼、方法又是什麼?
線程創建有三種方法:使用NSThread創建、使用GCD的dispatch、使用子類化的NSOperation,然後將其加入NSOperationQueue;在主線程執行代碼,方法是performSelectorOnMainThread,如果想延時執行代碼可以用performSelector:onThread:withObject:waitUntilDone:
 
11. 淺複製和深複製的區別?
答案:淺層複製:只複製指向對象的指針,而不復制引用對象本身。
深層複製:複製引用對象本身。
意思就是說我有個A對象,複製一份後得到A_copy對象後,對於淺複製來說,A和A_copy指向的是同一個內存資源,複製的只不過是是一個指針,對象本身資源
還是隻有一份,那如果我們對A_copy執行了修改操作,那麼發現A引用的對象同樣被修改,這其實違背了我們複製拷貝的一個思想。深複製就好理解了,內存中存在了
兩份獨立對象本身。
用網上一哥們通俗的話將就是:
淺複製好比你和你的影子,你完蛋,你的影子也完蛋
深複製好比你和你的克隆人,你完蛋,你的克隆人還活着。
 
 
20.簡述OC中內存管理機制。與retain配對使用的方法是dealloc還是release,爲什麼?需要與alloc配對使用的方法是dealloc還是release,爲什麼?readwrite,readonly,assign,retain,copy,nonatomic 、atomic、strong、weak屬性的作用? (OC第九講 內存管理初級)
OC使用了一種叫做引用計數的機制來管理對象,如果對一個對象使用了alloc、[Mutable]copy,retain,那麼你必須使用相應的realease或者autorelease。也可以理解爲自己生成的對象,自己持有。非自己生成的對象,自己也能持有。不在需要自己持有的對象時釋放。非自己持有的對象無法釋放。生成並持有對象<alloc,new,copy,mutableCopy等>,持有對象<retain>,釋放對象<release>,廢棄對象<dealloc>。readwrite(默認):可讀可寫,表示既有getter方法,也有setter方法。readonly:表示只有getter方法,沒有setter方法。nonatomic:不考慮線程安全。atomic(默認):線程操作安全。strong(默認):ARC下和MRC下retain一樣,weak(ARC下):和(MRC下)assign類似,區別是當weak指向的內存釋放掉後自動置爲nil,防止野指針。
unsafe_unretained聲明一個若引用,但不會自動置爲nil,可能會出現野指針。
線程安全下的setter和getter方法:
-(NSString *)value{
       @synchronized(self){
       return [[_value retain] autorelease];
       }
}
- (void)setValue:(NSString *)aValue{
       @synchronized(self){
              [aValue retain];
              [_value release];
              _value = aValue;
       }
}
64.同一個工程中ARC和非ARC 的如何混合使用?
點擊項目->TARGETS->Build Phases->Compile  Sources中選擇要改的.m,雙擊,在標籤中添加:
1.如果是ARC項目,要加入非ARC的代碼文件:-fobjc-arc
2.如果是非ARC,要加入ARC的代碼:-fno-objc-arc
回車
 
43.內存中的棧和堆的區別是什麼?
管理方式:對於棧來講,是由編譯器自動管理的,無需我們手動控制,對於堆來講,釋放工作有程序猿控制,這樣就容易產生memory Leak
 
申請大小: 棧是向低地址擴展的數據結構,是一塊連續的內存區域。這句話的意思是棧頂上的地址和棧的最大容量是系統預先規定好的,在Windows下,棧的大小是2M(也有的說1M,總之是編譯器確定的一個常數),如果申請的空間超過了棧的剩餘空間時候,就overflow。因此,能獲得棧的空間較小。
堆:堆是向高地址擴展的數據結構,是不連續的內存區域。這是由於系統是用鏈表來存儲的空閒內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大笑受限於計算機系統中有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大
 
碎片問題:
對於堆來講,頻繁的new/delete勢必會造成內存空間的不連續,從而造成大量的碎片,使程序效率降低。對於棧來講,則不會存在這個問題,因爲棧是先進後出的隊列,他們是如此的一一對應,以至於永遠都不可能有一個內存快從棧中彈出
 
分配方式:
堆都是動態分配的,沒有靜態分配的堆。棧有兩種分配方式:靜態分配和動態分配。靜態分配是編譯器完成的,比如局部變量的分配。動態分配是有alloc函數進行分配的,但是棧的動態分配和堆是不同的,他的動態分配由編譯器進行釋放,無需我們手工實現。
 分配效率:
棧是機器系統提供的數據結構,計算機會在底層堆棧提供支持,分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執行,這就決定了棧的效率比較高。堆則是C/C++函數庫提供的,他的機制是很複雜的。
 
3、iOS有沒有垃圾回收?
iOS沒有垃圾回收機制。只有系統在應用程序退出的時候會在適當的時候回收所有的東西。ios5以後有arc,可以自動添加管理內存的代碼,但與垃圾回收不是一個東西。一個需要自己管理,一個不需要。
其實也可以垃圾回收,但是這樣的話對性能有影響,而且手機不像電腦,對性能要求嚴格,所以蘋果果斷沒有用垃圾回收。把內存的煩惱留程序員,但爲了照顧程序員,又發明了arc。

11. 內存池的概念詳細解釋一下
 
         內存池:內存池就是用來存放當前創建的多有對象的空間位置,當我們在創建一個實例對象以後系統會自動的把內容放到內存池中,當我們在使用這些對象的時候就會在內存中自動的去執行retain,copy,release操作,當實例對象的引用計數爲0時就會自動的執行dealloc方法來銷燬實例對象.
在內存池中也會有runloop作用是來監察每個實例對象的引用計數.
 
31.如何優化內存?
 
       (1). 用ARC管理內存
       ARC(Automatic ReferenceCounting, 自動引用計數)和iOS5一起發佈,它避免了最常見的也就是經常是由於我們忘記釋放內存所造成的內存泄露。它自動爲你管理retain和release的過程,除了幫你避免內存泄露,ARC還可以幫你提高性能,它能保證釋放掉不再需要的對象的內存。
       (2). 在正確的地方使用reuseIdentifier
       一個開發中常見的錯誤就是沒有給UITableViewCells, UICollectionViewCells,甚至是UITableViewHeaderFooterViews設置正確的reuseIdentifier。
 
       (3).儘量把views設置爲透明
       如果你有透明的Views你應該設置它們的opaque屬性爲YES。
       原因是這會使系統用一個最優的方式渲染這些views。如果設爲YES,渲染系統就認爲這個view是完全不透明的,這使得渲染系統優化一些渲染過程和提高性能。如果設置爲NO,渲染系統正常地和其它內容組成這個View。默認值是YES。
 
       (4).避免過於龐大的XIB
       當你加載一個XIB的時候所有內容都被放在了內存裏,包括任何圖片。如果有一個不會即刻用到的view,你這就是在浪費寶貴的內存資源了。
       (5).不要阻塞主線程
       永遠不要使主線程承擔過多。因爲UIKit在主線程上做所有工作,渲染,管理觸摸反應,迴應輸入等都需要在它上面完成。
       一直使用主線程的風險就是如果你的代碼真的block了主線程,你的app會失去反應。
       大部分阻礙主進程的情形是你的app在做一些牽涉到讀寫外部資源的I/O操作,比如存儲或者網絡。
       (6). 在Image Views中調整圖片大小
       如果要在`UIImageView`中顯示一個來自bundle的圖片,你應保證圖片的大小和UIImageView的大小相同。在運行中縮放圖片是很耗費資源的,特別是`UIImageView`嵌套在`UIScrollView`中的情況下。
       如果圖片是從遠端服務加載的你不能控制圖片大小,比如在下載前調整到合適大小的話,你可以在下載完成後,最好是用background thread,縮放一次,然後在UIImageView中使用縮放後的圖片。
       (7). 選擇正確的Collection
       學會選擇對業務場景最合適的類或者對象是寫出能效高的代碼的基礎。當處理collections時這句話尤其正確。
       一些常見collection的總結:
       · Arrays: 有序的一組值。使用index來lookup很快,使用value lookup很慢,插入/刪除很慢。
       ·Dictionaries: 存儲鍵值對。用鍵來查找比較快。
       · Sets: 無序的一組值。用值來查找很快,插入/刪除很快。
 
       (8). 打開gzip壓縮
       大量app依賴於遠端資源和第三方API,你可能會開發一個需要從遠端下載XML, JSON, HTML或者其它格式的app。
       問題是我們的目標是移動設備,因此你就不能指望網絡狀況有多好。一個用戶現在還在edge網絡,下一分鐘可能就切換到了3G。不論什麼場景,你肯定不想讓你的用戶等太長時間。
減小文檔的一個方式就是在服務端和你的app中打開gzip。這對於文字這種能有更高壓縮率的數據來說會有更顯著的效用。
       好消息是,iOS已經在NSURLConnection中默認支持了gzip壓縮,當然AFNetworking這些基於它的框架亦然。像Google App Engine這些雲服務提供者也已經支持了壓縮輸出。
 
       (9). 重用和延遲加載(lazy load) Views
       更多的view意味着更多的渲染,也就是更多的CPU和內存消耗,對於那種嵌套了很多view在   UIScrollView裏邊的app更是如此。
       這裏我們用到的技巧就是模仿`UITableView`和`UICollectionView`的操作:不要一次創建所有的subview,而是當需要時才創建,當它們完成了使命,把他們放進一個可重用的隊列中。
       這樣的話你就只需要在滾動發生時創建你的views,避免了不划算的內存分配。
創建views的能效問題也適用於你app的其它方面。想象一下一個用戶點擊一個按鈕的時候需要呈現      一個view的場景。有兩種實現方法:
       1. 創建並隱藏這個view當這個screen加載的時候,當需要時顯示它;
       2. 當需要時才創建並展示。
       每個方案都有其優缺點。用第一種方案的話因爲你需要一開始就創建一個view並保持它直到不再使用,這就會更加消耗內存。然而這也會使你的app操作更敏感因爲當用戶點擊按鈕的時候它只需要改變一下這個view的可見性。
       第二種方案則相反-消耗更少內存,但是會在點擊按鈕的時候比第一種稍顯卡頓。
       (10). Cache, Cache, 還是Cache!
       一個極好的原則就是,緩存所需要的,也就是那些不大可能改變但是需要經常讀取的東西。
我們能緩存些什麼呢?一些選項是,遠端服務器的響應,圖片,甚至計算結果,比如UITableView的行高。
       NSURLConnection默認會緩存資源在內存或者存儲中根據它所加載的HTTP Headers。你甚至可以手動創建一個NSURLRequest然後使它只加載緩存的值。
 
       (11).權衡渲染方法
       在iOS中可以有很多方法做出漂亮的按鈕。你可以用整幅的圖片,可調大小的圖片,uozhe可以用CALayer, CoreGraphics甚至OpenGL來畫它們。
       當然每個不同的解決方法都有不同的複雜程度和相應的性能。
       簡單來說,就是用事先渲染好的圖片更快一些,因爲如此一來iOS就免去了創建一個圖片再畫東西上去然後顯示在屏幕上的程序。問題是你需要把所有你需要用到的圖片放到app的bundle裏面,這樣就增加了體積–這就是使用可變大小的圖片更好的地方了:你可以省去一些不必要的空間,也不需要再爲不同的元素(比如按鈕)來做不同的圖。
       然而,使用圖片也意味着你失去了使用代碼調整圖片的機動性,你需要一遍又一遍不斷地重做他們,這樣就很浪費時間了,而且你如果要做一個動畫效果,雖然每幅圖只是一些細節的變化你就需要很多的圖片造成bundle大小的不斷增大。
       總得來說,你需要權衡一下利弊,到底是要性能能還是要bundle保持合適的大小。
 
       (12).處理內存警告
       一旦系統內存過低,iOS會通知所有運行中app。如果你的app收到了內存警告,它就需要儘可能釋       放更多的內存。最佳方式是移除對緩存,圖片object和其他一些可以重創建的objects的strong references.
       幸運的是,UIKit提供了幾種收集低內存警告的方法:
       · 在app delegate中使用`applicationDidReceiveMemoryWarning:`的方法
       · 在你的自定義UIViewController的子類(subclass)中覆蓋`didReceiveMemoryWarning`
       · 註冊並接收UIApplicationDidReceiveMemoryWarningNotification的通知
       一旦收到這類通知,你就需要釋放任何不必要的內存使用。
       例如,UIViewController的默認行爲是移除一些不可見的view,它的一些子類則可以補充這個方法,刪掉一些額外的數據結構。一個有圖片緩存的app可以移除不在屏幕上顯示的圖片。
 
       (13).重用大開銷對象
       一些objects的初始化很慢,比如NSDateFormatter和NSCalendar。然而,你又不可避免地需要使用它們,比如從JSON或者XML中解析數據。
       想要避免使用這個對象的瓶頸你就需要重用他們,可以通過添加屬性到你的class裏或者創建靜態變量來實現。
       注意如果你要選擇第二種方法,對象會在你的app運行時一直存在於內存中,和單例(singleton)很相似。
       還需要注意的是,其實設置一個NSDateFormatter的速度差不多是和創建新的一樣慢的!所以如果你的app需要經常進行日期格式處理的話,你會從這個方法中得到不小的性能提升。
 
       (14). 減少使用Web特性
       UIWebView很有用,用它來展示網頁內容或者創建UIKit很難做到的動畫效果是很簡單的一件事。
       但是你可能有注意到UIWebView並不像驅動Safari的那麼快。這是由於以JIT compilation爲特色的Webkit的Nitro Engine的限制。
       所以想要更高的性能你就要調整下你的HTML了。第一件要做的事就是儘可能移除不必要的 javascript,避免使用過大的框架。能只用原生js就更好了。
       另外,儘可能異步加載例如用戶行爲統計script這種不影響頁面表達的javascript。
       最後,永遠要注意你使用的圖片,保證圖片的符合你使用的大小。使用Sprite sheet提高加載速度和節約內存。
 
       (15). 優化Table View
       Table view需要有很好的滾動性能,不然用戶會在滾動過程中發現動畫的瑕疵。
       爲了保證table view平滑滾動,確保你採取了以下的措施:
       · 正確使用`reuseIdentifier`來重用cells
       · 儘量使所有的view opaque,包括cell自身
       · 避免漸變,圖片縮放,後臺選人
       · 緩存行高
       · 如果cell內現實的內容來自web,使用異步加載,緩存請求結果
       · 使用`shadowPath`來畫陰影
       · 減少subviews的數量
       · 儘量不適用`cellForRowAtIndexPath:`,如果你需要用到它,只用一次然後緩存結果
       · 使用正確的數據結構來存儲數據
       · 使用`rowHeight`, `sectionFooterHeight`和 `sectionHeaderHeight`來設定固定的高,不要請求delegate
       (16). 使用Autorelease Pool
       `NSAutoreleasePool`負責釋放block中的autoreleased objects。一般情況下它會自動被UIKit調用。但是有些狀況下你也需要手動去創建它。
       假如你創建很多臨時對象,你會發現內存一直在減少直到這些對象被release的時候。這是因爲只有當UIKit用光了autorelease pool的時候memory纔會被釋放。好消息是你可以在你自己的    @autoreleasepool裏創建臨時的對象來避免這個行爲:
       NSArray *urls = <# An array of file URLs #>;
       for(NSURL *url in urls) {
       @autoreleasepool {
       NSError *error;
       NSString *fileContents = [NSString stringWithContentsOfURL:url              encoding:NSUTF8StringEncoding error:&error];
 
       /* Process the string, creating and autoreleasing more objects. */
 
       }
 
}
       這段代碼在每次遍歷後釋放所有autorelease對象
 
       (17). 選擇是否緩存圖片
       常見的從bundle中加載圖片的方式有兩種,一個是用`imageNamed`,二是用`imageWithContentsOfFile`,第一種比較常見一點。
       既然有兩種類似的方法來實現相同的目的,那麼他們之間的差別是什麼呢?
       `imageNamed`的優點是當加載時會緩存圖片。`imageNamed`的文檔中這麼說:這個方法用一個指定的名字在系統緩存中查找並返回一個圖片對象如果它存在的話。如果緩存中沒有找到相應       的圖片,這個方法從指定的文檔中加載然後緩存並返回這個對象。
       相反的,`imageWithContentsOfFile`僅加載圖片。
       如果你要加載一個大圖片而且是一次性使用,那麼就沒必要緩存這個圖片,用    `imageWithContentsOfFile`足矣,這樣不會浪費內存來緩存它。
         然而,在圖片反覆重用的情況下`imageNamed`是一個好得多的選擇
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章