前言:
最近把 iOS 面試中可能會遇到的問題整理了一番, 題目大部分是網上收錄的, 方便自己鞏固複習, 也分享給大家; 希望對大家有所幫助!
- 對於答案,不一定都合適,歡迎大家積極討論;整理不易,如果您覺得還不錯,麻煩在文末 “點個贊” ,或者留下您的評論“Mark” 一下,謝謝您的支持
iOS面試題-內存管理問題(五)
1.什麼是內存泄漏?
- 內存泄漏指動態分配內存的對象在使用完後沒有被系統回收內存,導致對象始終佔有着內存,屬於內存管理出錯, (例如一個對象或者變量使用完成後沒有釋放,這個對象一直佔用着內存),一次內存泄露危害可以忽略,但內存泄露堆積後果很嚴重,無論多少內存,遲早會被佔光。
2. 什麼是殭屍對象?
- 已經被銷燬的對象(不能再使用的對象),內存已經被回收的對象。一個引用計數器爲0對象被釋放後就變爲了殭屍對象;
3. 野指針
-
野指針又叫做'懸掛指針', 野指針出現的原因是因爲指針沒有賦值,或者指針指向的對象已經釋放了, 比如指向殭屍對象;野指針可能會指向一塊垃圾內存,給野指針發送消息會導致程序崩潰
比如:NSObject *obj = [NSObject new]; [obj release]; // obj 指向的內存地址已經釋放了, obj 如果再去訪問的話就是野指針錯誤了. 野指針錯誤形式在Xcode中通常表現爲:Thread 1:EXC_BAD_ACCESS,因爲你訪問了一塊已經不屬於你的內存。
4. 什麼是空指針?
- 空指針不同於野指針,他是一個沒有指向任何內存的指針,空指針是有效指針,值爲
nil,NULL,Nil,0
等,給空指針發送消息不會報錯,不會響應消息;
5. OC對象的內存管理機制?
在iOS中,使用引用計數來管理OC對象的內存
- 一個新創建的OC對象引用計數默認是1,當引用計數減爲0,OC對象就會銷燬,釋放其佔用的內存空間
- 調用
retain
會讓OC對象的引用計數+1,調用release
會讓OC對象的引用計數-1
內存管理的經驗總結
- 當調用alloc、new、copy、mutableCopy方法返回了一個對象,在不需要這個對象時,要調用release或者autorelease來釋放它
- 想擁有某個對象,就讓它的引用計數+1;不想再擁有某個對象,就讓它的引用計數-1
可以通過以下私有函數來查看自動釋放池的情況
extern void _objc_autoreleasePoolPrint(void);
精選全網 · iOS面試題答案PDF文集
- 獲取加小編的iOS技術交流圈:937 194 184,直接獲取
6. OC中有GC垃圾回收機制嗎?,iPhone上GC嗎?
- 垃圾回收(GC),就是程序中用於處理廢棄不用的內存對象的機制,防止內存泄露
- OC本身是支持垃圾回頭得,不過只支持MAC OSX平臺, iOS 平臺不支持
7.在OC中與 Alloc 語義相反的是 release 還是 dealloc?
- alloc 與 dealloc 語義相反,alloc 是創建變量,dealloc是釋放變量
- retain 與 release 語義相反, retain 保留一個對象,引用計數器+1, release 使引用計數器 -1;
8.什麼是內存溢出?
- 當程序在申請內存時,沒有足夠的內存空間供其使用,出現out of memory;比如申請了一個int,但給它存了long才能存下的數,那就是內存溢出。
9.內存區域分佈
在iOS開發過程中,爲了合理的分配有限的內存空間,將內存區域分爲五個區,由低地址向高地址分類分別是:代碼區、常量區、全局靜態區、堆、棧。
- 代碼段 -- 程序編譯產生的二進制的數據
- 常量區 -- 存儲常量數據,通常程序結束後由系統自動釋放
- 全局靜態區 -- 全局區又可分爲未初始化全局區:.bss段和初始化全局區:data段。全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域, 未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域,在程序結束後有系統釋放。
- 堆(heap) -- 程序運行過程中,動態分配的內存
- 棧(stack) -- 存放局部變量,臨時變量
10.堆區和棧取的區別
-
按管理方式分
- 對於棧來講,是由系統編譯器自動管理,不需要程序員手動管理
- 對於堆來講,釋放工作由程序員手動管理,不及時回收容易產生內存泄露
-
按分配方式分
- 堆是動態分配和回收內存的,沒有靜態分配的堆
- 棧有兩種分配方式:靜態分配和動態分配
- 靜態分配是系統編譯器完成的,比如局部變量的分配
- 動態分配是有alloc函數進行分配的,但是棧的動態分配和堆是不同的,它的動 態分配也由系統編譯器進行釋放,不需要程序員手動管理
11.怎麼保證多人開發進行內存泄露的檢查.
- 使用Analyze進行代碼的靜態分析
- 爲避免不必要的麻煩, 多人開發時儘量使用ARC
- 使用leaks 進行內存泄漏檢測
- 使用一些三方工具
12.block在ARC中和MRC中的用法有什麼區別,需要注意什麼?
- 對於沒有引用外部變量的Block,無論在ARC還是非ARC下,類型都是 NSGlobalBlock,這種類型的block可以理解成一種全局的block,不 需要考慮作用域問題。同時,對他進行Copy或者Retain操作也是無效的
- 都需要應注意避免循環引用,ARC 下使用__weak 來解決,MRC下使用__Block 來解決;
13.OC 如何對內存管理解決方法?
Objective-C的內存管理主要有三種方式 自動內存管理、手動內存管理、自動釋放池。
- 自動內存計數
- 手動內存計數:
- 自動釋放池:
14.ARC 都幫我們做了什麼?
- LLVM + Runtime 會爲我們代碼自動插入 retain 和 release 以及 autorelease等代碼,不需要我們手動管理
15.weak指針的實現原理
- Runtime維護了一個weak表,用於存儲指向某個對象的所有weak指針。weak表其實是一個hash(哈希)表,Key是所指對象的地址,Value是weak指針的地址(這個地址的值是所指對象的地址)數組。
- runtime對註冊的類, 會進行佈局,對於weak對象會放入一個hash表中。 用weak指向的對象內存地址作爲key,當此對象的引用計數爲0的時候會dealloc,假如weak指向的對象內存地址是a,那麼就會以a爲鍵, 在這個weak表中搜索,找到所有以a爲鍵的weak對象,從而設置爲nil。
16.方法裏有局部對象,出了方法後會立即釋放嗎
- 如果是普通的 局部對象 會立即釋放
- 如果是放在了 autoreleasePool 自動釋放池,在 runloop 迭代結束的時候釋放
17.MRC情況下怎麼做單例模式
創建單例設計模式的基本步驟 : ·
- 聲明一個單件對象的靜態實例,並初始化爲nil。
- 創建一個類的類工廠方法,當且僅當這個類的實例爲nil時生成一個該類 的實例
- 實現NScopying協議, 覆蓋allocWithZone:方法,確保用戶在直接分配和 初始化對象時,不會產 生另一個對象。
- 覆蓋release、autorelease、retain、retainCount方法, 以此確保單例的 狀態。
- 在多線程的環境中,注意使用@synchronized關鍵字或GCD,確保靜態實 例被正確的創建和初始化。
精選全網 · iOS面試題答案PDF文集
- 獲取加小編的iOS技術交流圈:937 194 184,直接獲取
18.非OC對象如何管理內存?
- 非OC對象,其需要手動執行釋放操作例:CGImageRelease(ref),否則會造成大量的內存泄漏導致程序崩潰。其他的對於CoreFoundation框架下的某些對象或變量需要手動釋放、C語言代碼中的malloc等需要對應free。
19. CADisplayLink、NSTimer會出現的問題,以及解決辦法?
問題:
- CADisplayLink、NSTimer會對target產生強引用,如果target又對它們產生強引用,那麼就會引發循環引用
- CADisplayLink、NSTimer都是基於 runloop 實現的.runloop 會對 CADisplayLink、NSTimer進行強引用, CADisplayLink、NSTimer又 會對 target 進行引用,造成循環引用
- 解決方案1.使用block
// 內部使用 WeakSelf,並在視圖消失前,關閉定時器
__weak __typeof(self)weakSelf = self;
NSTimer * timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"timer");
}];
self.timer= timer;
[[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];
- 解決方案2.使用代理對象(NSProxy)
.h
// 解決循環引用問題
@interface MyProxy : NSProxy
- (instancetype)initWithObjc:(id)objc;
+ (instancetype)proxyWithObjc:(id)objc;
.m
@interface MyProxy()
@property(nonatomic,weak) id objc;
@end
@implementation MyProxy
- (instancetype)initWithObjc:(id)objc{
self.objc = objc;
return self;
}
+ (instancetype)proxyWithObjc:(id)objc{
return [[self alloc] initWithObjc:objc];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
return [self.objc methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)invocation {
if ([self.objc respondsToSelector:invocation.selector]) {
[invocation invokeWithTarget:self.objc];
}
}
使用方法:
NSTimer * timer = [NSTimer timerWithTimeInterval:1
target:[TimerProxy proxyWithTarget:self]
selector:@selector(test1)
userInfo:nil
repeats:YES];
self.timer = timer;
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
20.什麼是Tagged Pointer?
- 從64bit開始,iOS引入了Tagged Pointer技術,用於優化NSNumber、NSDate、NSString等小對象的存儲
- 在沒有使用Tagged Pointer之前, NSNumber等對象需要動態分配內存、維護引用計數等,NSNumber指針存儲的是堆中NSNumber對象的地址值
- 使用Tagged Pointer之後,NSNumber指針裏面存儲的數據變成了:Tag + Data,也就是將數據直接存儲在了指針中
- 當指針不夠存儲數據時,纔會使用動態分配內存的方式來存儲數據
21.copy和mutableCopy區別
22. 內存泄漏可能會出現的幾種原因?
- 第一種可能:第三方框架不當使用;
- 第二種可能:block循環引用;
- 第三種可能:delegate循環引用;
- 第四種可能:NSTimer循環引用
- 第五種可能:非OC對象內存處理
- 第六種可能:地圖類處理
- 第七種可能:大次數循環內存暴漲
23. ARC下什麼樣的對象由 Autoreleasepool 管理
- 當使用alloc/new/copy/mutableCopy開始的方法進行初始化時,會生成並持有對象(也就是不需要pool管理,系統會自動的幫他在合適位置release),不需要pool進行管理
- 一般類方法創建的對象需要使用Autoreleasepool進管理
24. 如何實現AutoreleasePool?
- AutoreleasePool(自動釋放池)其實並沒有自身的結構,他是基於多個AutoreleasePoolPage(一個C++類)以雙向鏈表組合起來的結構; 可以通過 push操作添加對象,pod 操作彈出對象,以及通過 release 操作釋放對象;
25. AutoreleasePoolPage的結構?以及如何 push 和 pod 的?
- 調用push方法會將一個POOL_BOUNDARY入棧,並且返回其存放的內存地址
- 調用pop方法時傳入一個POOL_BOUNDARY的內存地址,會從最後一個入棧的對象開始發送release消息,直到遇到這個POOL_BOUNDARY
- id *next指向了下一個能存放autorelease對象地址的區域
26.Autoreleasepool 與 Runloop 的關係
- 主線程默認爲我們開啓 Runloop,Runloop 會自動幫我們創建Autoreleasepool,並進行Push、Pop 等操作來進行內存管理
iOS在主線程的Runloop中註冊了2個Observer
- 第1個Observer監聽了
kCFRunLoopEntry
事件,會調用objc_autoreleasePoolPush()
- 第2個Observer 監聽了
kCFRunLoopBeforeWaiting
事件,會調用objc_autoreleasePoolPop()、objc_autoreleasePoolPush()
監聽了kCFRunLoopBeforeExit
事件,會調用objc_autoreleasePoolPop()
27.子線程默認不會開啓 Runloop,那出現 Autorelease 對象如何處理?不手動處理會內存泄漏嗎?
- 在子線程你創建了 Pool 的話,產生的 Autorelease 對象就會交給 pool 去管理。如果你沒有創建 Pool ,但是產生了 Autorelease 對象,就會調用 autoreleaseNoPage 方法。在這個方法中,會自動幫你創建一個 hotpage(hotPage 可以理解爲當前正在使用的 AutoreleasePoolPage,如果你還是不理解,可以先看看 Autoreleasepool 的源代碼,再來看這個問題 ),並調用 page->add(obj)將對象添加到 AutoreleasePoolPage 的棧中,也就是說你不進行手動的內存管理,也不會內存泄漏啦!StackOverFlow 的作者也說道,這個是 OS X 10.9+和 iOS 7+ 才加入的特性。
收錄 | 原文地址
結語
再次說一聲,對於答案,不一定都合適,歡迎大家積極討論;整理不易,如果您覺得還不錯,麻煩在文末 “點個贊” ,或者留下您的評論“Mark” 一下,謝謝您的支持