喜歡就關(guān)注我唄!
1.設(shè)計模式是什么? 你知道哪些設(shè)計模式,并簡要敘述?
設(shè)計模式是一種編碼經(jīng)驗,就是用比較成熟的邏輯去處理某一種類型的事情。
1). MVC模式:Model View Control,把模型 視圖 控制器 層進行解耦合編寫。
2). MVVM模式:Model View ViewModel 把模型 視圖 業(yè)務(wù)邏輯 層進行解耦和編寫。
3). 單例模式:通過static關(guān)鍵詞,聲明全局變量。在整個進程運行期間只會被賦值一次。
4). 觀察者模式:KVO是典型的通知模式,觀察某個屬性的狀態(tài),狀態(tài)發(fā)生變化時通知觀察者。
5). 委托模式:代理+協(xié)議的組合。實現(xiàn)1對1的反向傳值操作。
6). 工廠模式:通過一個類方法,批量的根據(jù)已有模板生產(chǎn)對象。
2.#import跟 #include 有什么區(qū)別,@class呢
1). #import是Objective-C導(dǎo)入頭文件的關(guān)鍵字,#include是C/C++導(dǎo)入頭文件的關(guān)鍵字,使用#import頭文件會自動只導(dǎo)入一次,不會重復(fù)導(dǎo)入。
2). @class告訴編譯器某個類的聲明,當執(zhí)行時,才去查看類的實現(xiàn)文件,可以解決頭文件的相互包含。
3.Objective-C的類可以多重繼承么?可以實現(xiàn)多個接口么?Category是什么?重寫一個類的方式用繼承好還是分類好?為什么?
答:Objective-C的類不可以多重繼承;可以實現(xiàn)多個接口(協(xié)議);Category是類別;一般情況用分類好,用Category去重寫類的方法,僅對本Category有效,不會影響到其他類與原有類的關(guān)系。
4.@property 的本質(zhì)是什么?ivar、getter、setter 是如何生成并添加到這個類中的
@property 的本質(zhì)是什么?
@property = ivar + getter + setter;
“屬性” (property)有兩大概念:ivar(實例變量)、getter+setter(存取方法)
“屬性” (property)作為 Objective-C 的一項特性,主要的作用就在于封裝對象中的數(shù)據(jù)。 Objective-C 對象通常會把其所需要的數(shù)據(jù)保存為各種實例變量。實例變量一般通過“存取方法”(access method)來訪問。其中,“獲取方法” (getter)用于讀取變量值,而“設(shè)置方法” (setter)用于寫入變量值。
5.什么情況使用 weak 關(guān)鍵字,相比 assign 有什么不同?
1.在 ARC 中,在有可能出現(xiàn)循環(huán)引用的時候,往往要通過讓其中一端使用 weak 來解決,比如: delegate 代理屬性。
2.自身已經(jīng)對它進行一次強引用,沒有必要再強引用一次,此時也會使用 weak,自定義 IBOutlet 控件屬性一般也使用 weak;當然,也可以使用strong。
IBOutlet連出來的視圖屬性為什么可以被設(shè)置成weak?
因為父控件的subViews數(shù)組已經(jīng)對它有一個強引用。
不同點:
assign 可以用非 OC 對象,而 weak 必須用于 OC 對象。
weak 表明該屬性定義了一種“非擁有關(guān)系”。在屬性所指的對象銷毀時,屬性值會自動清空(nil)。
6.淺拷貝和深拷貝的區(qū)別?
答:
淺拷貝:只復(fù)制指向?qū)ο蟮闹羔槪粡?fù)制引用對象本身。
深拷貝:復(fù)制引用對象本身。內(nèi)存中存在了兩份獨立對象本身,當修改A時,A_copy不變。
NSString *str = @"hello word!";
NSString *strCopy = [str copy] // 指針復(fù)制,strCopy與str的地址一樣
NSMutableString *strMCopy = [str mutableCopy] // 內(nèi)容復(fù)制,strMCopy與str的地址不一樣
7.寫一個 setter 方法用于完成 @property (nonatomic, retain) NSString*name,寫一個 setter 方法用于完成 @property (nonatomic, copy) NSString *name
答:
// retain
- (void)setName:(NSString *)str {
[str retain];
[_name release];
_name = str;
}
// copy
- (void)setName:(NSString *)str {
id t = [str copy];
[_name release];
_name = t;
}
8.@synthesize 和 @dynamic 分別有什么作用?
@property有兩個對應(yīng)的詞,一個是@synthesize(合成實例變量),一個是@dynamic。
如果@synthesize和@dynamic都沒有寫,那么默認的就是 @synthesize var = _var;
// 在類的實現(xiàn)代碼里通過 @synthesize 語法可以來指定實例變量的名字。(@synthesize var = _newVar;)
1. @synthesize 的語義是如果你沒有手動實現(xiàn)setter方法和getter方法,那么編譯器會自動為你加上這兩個方法。
2. @dynamic 告訴編譯器,屬性的setter與getter方法由用戶自己實現(xiàn),不自動生成(如,@dynamic var)。
9.id 聲明的對象有什么特性?
答:id 聲明的對象具有運行時的特性,即可以指向任意類型的Objcetive-C的對象。
10.Objective-C 中創(chuàng)建線程的方法是什么?如果在主線程中執(zhí)行代碼,方法是什么?如果想延時執(zhí)行代碼、方法又是什么?
答:線程創(chuàng)建有三種方法:使用NSThread創(chuàng)建、使用GCD的dispatch、使用子類化的NSOperation,然后將其加入NSOperationQueue;在主線程執(zhí)行代碼,方法是performSelectorOnMainThread,如果想延時執(zhí)行代碼可以用performSelector:onThread:withObject:waitUntilDone:
11.我們說的OC是動態(tài)運行時語言是什么意思?
答:主要是將數(shù)據(jù)類型的確定由編譯時,推遲到了運行時。簡單來說, 運行時機制使我們直到運行時才去決定一個對象的類別,以及調(diào)用該類別對象指定方法。
12.什么是 KVO 和 KVC?
1). KVC(Key-Value-Coding):鍵值編碼 是一種通過字符串間接訪問對象的方式(即給屬性賦值)
舉例說明:
stu.name = @"張三" // 點語法給屬性賦值
[stu setValue:@"張三" forKey:@"name"]; // 通過字符串使用KVC方式給屬性賦值
stu1.nameLabel.text = @"張三";
[stu1 setValue:@"張三" forKey:@"nameLabel.text"]; // 跨層賦值
2). KVO(key-Value-Observing):鍵值觀察機制 他提供了觀察某一屬性變化的方法,極大的簡化了代碼。
KVO只能被KVC觸發(fā),包括使用setValue:forKey:方法和點語法。
// 通過下方方法為屬性添加KVO觀察
- (void)addObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(nullable void *)context;
// 當被觀察的屬性發(fā)送變化時,會自動觸發(fā)下方方法
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context{}
KVC 和 KVO 的 keyPath 可以是屬性、實例變量、成員變量。
13.KVC的底層實現(xiàn)?
當一個對象調(diào)用setValue方法時,方法內(nèi)部會做以下操作:
1). 檢查是否存在相應(yīng)的key的set方法,如果存在,就調(diào)用set方法。
2). 如果set方法不存在,就會查找與key相同名稱并且?guī)聞澗€的成員變量,如果有,則直接給成員變量屬性賦值。
3). 如果沒有找到_key,就會查找相同名稱的屬性key,如果有就直接賦值。
4). 如果還沒有找到,則調(diào)用valueForUndefinedKey:和setValue:forUndefinedKey:方法。
這些方法的默認實現(xiàn)都是拋出異常,我們可以根據(jù)需要重寫它們。
24.談?wù)?UITableView 的優(yōu)化
1). 正確的復(fù)用cell。
2). 設(shè)計統(tǒng)一規(guī)格的Cell
3). 提前計算并緩存好高度(布局),因為heightForRowAtIndexPath:是調(diào)用最頻繁的方法;
4). 異步繪制,遇到復(fù)雜界面,遇到性能瓶頸時,可能就是突破口;
4). 滑動時按需加載,這個在大量圖片展示,網(wǎng)絡(luò)加載的時候很管用!
5). 減少子視圖的層級關(guān)系
6). 盡量使所有的視圖不透明化以及做切圓操作。
7). 不要動態(tài)的add 或者 remove 子控件。最好在初始化時就添加完,然后通過hidden來控制是否顯示。
8). 使用調(diào)試工具分析問題。
14.delegate 和 notification 的區(qū)別
1). 二者都用于傳遞消息,不同之處主要在于一個是一對一的,另一個是一對多的。
2). notification通過維護一個array,實現(xiàn)一對多消息的轉(zhuǎn)發(fā)。
3). delegate需要兩者之間必須建立聯(lián)系,不然沒法調(diào)用代理的方法;notification不需要兩者之間有聯(lián)系。
15.您一般是怎么用 Instruments 的?
Time Profiler:性能分析
Zombies:檢查是否訪問了僵尸對象,但是這個工具只能從上往下檢查,不智能
Allocations:用來檢查內(nèi)存,寫算法的那批人也用這個來檢查
Leaks:檢查內(nèi)存,看是否有內(nèi)存泄露
Core Animation:
1)查看幀數(shù) <55就得優(yōu)化 ?優(yōu)化。
Core Animation工具中有幾個和離屏渲染相關(guān)的檢查選項:
2)Color Offscreen-Rendered Yellow
開啟后會把那些需要離屏渲染的圖層高亮成黃色,這就意味著黃色圖層可能存在性能問題。
3)Color Hits Green and Misses Red
如果shouldRasterize被設(shè)置成YES,對應(yīng)的渲染結(jié)果會被緩存,如果圖層是綠色,就表示這些緩存被復(fù)用;如果是紅色就表示緩存會被重復(fù)創(chuàng)建,這就表示該處存在性能問題了。
16.您開發(fā)常用的工具有哪些?
1).友盟統(tǒng)計
2).青花瓷:這個軟件還是蠻不錯的,可以用來過濾網(wǎng)絡(luò)請求,模擬低速網(wǎng)路,還可以修改網(wǎng)絡(luò)請求內(nèi)容這些
3).Reveal:調(diào)試頁面不錯,還有用來學(xué)習(xí)別人的demo時候可以拿來看UI層次結(jié)構(gòu),還可以用
17.您是怎么調(diào)試iOS程序的(談?wù)勀膇OS調(diào)試經(jīng)驗)
斷點調(diào)試,讓程序在執(zhí)行某一行代碼是停止下來,然后來檢查當前程序是否正常。斷點的種類很多,可以幫助我們快速定位到問題發(fā)生時的上下文。
- 普通斷點
- 符號斷點
- 異常斷點
- watch斷點
- 條件斷點
運行時變量:查看運行時變量值
運行時堆棧:查看函數(shù)的調(diào)用關(guān)系,順序
日志:通過在程序中添加NSLog代碼,在控件臺中輸出顯示日志。
靜態(tài)代碼檢查:通過對代碼靜態(tài)分析,找出代碼潛在的錯誤,如內(nèi)存泄漏、空引用、未使用函數(shù)等。
動態(tài)分析:通過Instruments工具跟蹤分析程序運行時的數(shù)據(jù)
18.有哪些常見的 Crash 場景?
訪問了僵尸對象
訪問野指針
訪問了不存在的方法
數(shù)組越界
在定時器下一次回調(diào)前將定時器釋放
19.如何調(diào)試BAD_ACCESS錯誤
1)重寫object的respondsToSelector方法,現(xiàn)實出現(xiàn)EXEC_BAD_ACCESS前訪問的最后一個object
2)通過 Zombie
3)設(shè)置全局斷點快速定位問題代碼所在行
4)Xcode 7 已經(jīng)集成了BAD_ACCESS捕獲功能:Address Sanitizer。 用法如下:在配置中勾選?
Enable Address Sanitizer
20.lldb(gdb)常用的調(diào)試命令?
breakpoint 設(shè)置斷點定位到某一個函數(shù)
n 斷點指針下一步
po打印對象
21.如果在Cocoa中發(fā)現(xiàn)一個Bug,你會如何處理?
復(fù)現(xiàn)bug,確認bug發(fā)生的軟硬件環(huán)境,詳細描述后發(fā)報告給Apple。
22.CocoaPods的原理
CocoaPods的原理是將所有的依賴庫都放到另一個名為Pods的項目中,然后讓主項目依賴Pods項目,這樣,源碼管理工作都從主項目移到了Pods項目中。Pods項目最終會編譯成一個名為libPods.a的文件,主項目只需要依賴這個.a文件即可。
23.請用預(yù)處理指令#define聲明一個常數(shù),用以表明1年中有多少秒(忽略閏年問題)
#define SECONDS_PER_YEAR (60*60*24*365)_U_LONG
24.volatile表示變量隨時可以改變
25.請談?wù)刓#include與\#import的區(qū)別、\#import與@class 的區(qū)別
#include和#import 其效果相同,都是導(dǎo)入類中定義的行為(方法);
#import 不會引起交叉編譯,確保頭文件只會被導(dǎo)入一次;
@class 表明只定義了類的名稱,而具體類的行為是未知的,一般用于.h 文件
@class比#import編譯效率更高。此外@class和#import的主要區(qū)別在于解決引用死鎖的問題。
26.什么是id類型,id 聲明的對象有什么特性?
id 聲明的對象具有運行時的特性,即可以指向任意類型的objcetive-c的對象;
27.請談一談關(guān)鍵字self、super的作用
self:當前消息的接收者。
super:向父類發(fā)送消息。
28.請解釋self = [super init]方法
容錯處理,當父類初始化失敗,會返回一個nil,表示初始化失敗。由于繼承的關(guān)系,子類是需要擁有父類的實例和行為,因此,我們必須先初始化父類,然后再初始化子類
29.請說明如何使用Instancetype及其重要性
instancetype,編譯器能正確的推斷出返回實例的實際類型!
instancetype只能用于返回值!
建議在返回self實例的方法中,用instancetype,別用id.
30.請問@property中有哪些屬性關(guān)鍵字?
原子性---atomic/nonatomic? 讀/寫權(quán)限---readwrite(讀寫)、readonly(只讀)? 、內(nèi)存管理語義---assign、strong、weak、unsafe_unretained、copy、方法名---getter=和setter=、不常用的:nonnull,null_resettable,nullable
31.ARC下,不顯式指定任何屬性關(guān)鍵字時,默認的關(guān)鍵字都有哪些?
atomic,readwrite,assign,strong
32.@protocol 和 category 中如何使用 @property
在 protocol 中使用 property 只會生成 setter 和 getter 方法聲明,我們使用屬性的目的,是希望遵守我協(xié)議的對象能實現(xiàn)該屬性
category 使用 @property 也是只會生成 setter 和 getter 方法的聲明,如果我們真的需要給 category 增加屬性的實現(xiàn),需要借助于運行時的兩個函數(shù):
objc_setAssociatedObject
objc_getAssociatedObject
33.這個寫法會出什么問題: @property (copy) NSMutableArray *array;
兩個問題:
添加,刪除,修改數(shù)組內(nèi)的元素的時候,程序會因為找不到對應(yīng)的方法而崩潰.因為 copy 就是復(fù)制一個不可變 NSArray 的對象;
使用了 atomic 屬性會嚴重影響性能 ;
34.@synthesize合成實例變量的規(guī)則是什么?假如property名為foo,存在一個名為_foo的實例變量,那么還會自動合成新變量么?
總結(jié)下 @synthesize 合成實例變量的規(guī)則,有以下幾點:
如果指定了成員變量的名稱,會生成一個指定的名稱的成員變量,
如果這個成員已經(jīng)存在了就不再生成了.
如果是 @synthesize foo;
還會生成一個名稱為foo的成員變量,也就是說:
如果沒有指定成員變量的名稱會自動生成一個屬性同名的成員變量,
如果是 @synthesize foo = _foo;
就不會生成成員變量了.
假如 property 名為 foo,存在一個名為 _foo的實例變量,那么還會自動合成新變量么? 不會。
34.如何為 Class 定義一個對外只讀對內(nèi)可讀寫的屬性?
在頭文件中將屬性定義為readonly, 在.m文件中將屬性重新定義為readwrite
35.在一個對象的方法里面:`self.name = @"object";`和`name =@"object";`有什么不同嗎?
self.name = @"object";會調(diào)用對象的setName()方法.
name =@"object";會直接把@"object"賦值給當前對象的name 屬性。
block 不能修改局部變量,如果需要修改需要加上block.
block 會對對象強引用,引起retain-cycle,需要使用weak
37.使用block有什么好處?請使用`NSTimer`寫出一個使用block顯示(在`UILabel`上)秒表的代碼
//iOS10 才有效
NSTimer*timer=[NSTimerscheduledTimerWithTimeInterval:1.0repeats:YEScallback:^(){weakSelf.secondsLabel.text=...} ? ?
[[NSRunLoopcurrentRunLoop]addTimer:timerforMode:NSRunLoopCommonModes];
38.談?wù)刡lock使用時的注意點?
在block內(nèi)部使用外部指針且會造成循環(huán)引用情況下,需要用weak修飾外部指針weak typeof(self) weakSelf = self;
在block內(nèi)部如果調(diào)用了延時函數(shù)還使用弱指針會取不到該指針,因為已經(jīng)被銷毀了,需要在block內(nèi)部再將弱指針重新強引用一下__strong typeof(self) strongSelf = weakSelf;
如果需要在block內(nèi)部改變外部變量的話,需要在用__block修飾外部變量
39.block和代理的區(qū)別,哪個更好?
代理回調(diào)更面向過程,block更面向結(jié)果。FBRetainCycleDetector? ?檢測循環(huán)引用。
如果你使用一些參數(shù)中可能含有 ivar 的系統(tǒng) api ,如 GCD 、NSNotificationCenter就要小心一點:比如GCD 內(nèi)部如果引用了 self,而且 GCD 的其他參數(shù)是 iva
_operationsQueue 這個
__weak__typeof__(self)weakSelf =self;dispatch_group_async(_operationsGroup, _operationsQueue, ^{? ? __typeof__(self)strongSelf= weakSelf;[strongSelfdoSomething];[strongSelfdoSomethingElse];} );
__weak__typeof__(self) weakSelf =self;_observer = [[NSNotificationCenterdefaultCenter] addObserverForName:@"testKey"object:nilqueue:nilusingBlock:^(NSNotification*note) {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? __typeof__(self) strongSelf = weakSelf;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [strongSelf dismissModalViewControllerAnimated:YES];? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }];
40.類別和類擴展的區(qū)別
category和extensions的不同在于后者可以添加屬性。另外后者添加的方法是必須要實現(xiàn)的。
extensions可以認為是一個私有的Category。
41.分類的作用?分類和繼承的區(qū)別?
分類可以在不獲悉,不改變原來代碼的情況下往里面添加新的方法,只能添加,不能刪除修改,并且如果分類和原來類中的方法產(chǎn)生名稱沖突,則分類將覆蓋原來的方法,因為分類具有更高的優(yōu)先級。
繼承可以增加,修改或者刪除方法,并且可以增加屬性;但是分類只能添加方法,不能刪除修改,也不能增加屬性。
42.iOS Extension 是什么?能列舉幾個常用的 Extension 么?
Extension是Category的一個特例,沒有分類名字,可以擴展屬性,成員變量和方法。
常用的擴展是在.m文件中聲明私有屬性和方法,基本上我們天天都在用。
43.addObserver:forKeyPath:options:context:各個參數(shù)的作用分別是什么,observer中需要實現(xiàn)哪個方法才能獲得KVO回調(diào)?
// 添加鍵值觀察/*
1 觀察者,負責處理監(jiān)聽事件的對象
2 觀察的屬性
3 觀察的選項
4 上下文
*/[self.personaddObserver:selfforKeyPath:@"name"options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOldcontext:@"Person Name"];
// 所有的 kvo 監(jiān)聽到事件,都會調(diào)用此方法/*
1. 觀察的屬性
2. 觀察的對象
3. change 屬性變化字典(新/舊)
4. 上下文,與監(jiān)聽的時候傳遞的一致
*/- (void)observeValueForKeyPath:(NSString *)keyPathofObject:(id)objectchange:(NSDictionary *)changecontext:(void*)context;
44.如何手動觸發(fā)一個value的KVO
那么“手動觸發(fā)”的使用場景是什么?一般我們只在希望能控制“回調(diào)的調(diào)用時機”時才會這么做。
具體做法如下:
如果這個? value 是 表示時間的 self.now ,那么代碼如下:最后兩行代碼缺一不可。
@property (nonatomic, strong) NSDate *now;
- (void)viewDidLoad{ ??
?[super viewDidLoad]; ? ?
[self willChangeValueForKey:@"now"];// “手動觸發(fā)self.now的KVO”,必寫。
[self didChangeValueForKey:@"now"];// “手動觸發(fā)self.now的KVO”,必寫。
}
45.若一個類有實例變量NSString *_foo,調(diào)用setValue:forKey:時,可以以foo還是_foo作為key?
可以。kvc機制。
46.KVC的keyPath中的集合運算符如何使用?
必須用在集合對象上或普通對象的集合屬性上
簡單集合運算符有@avg, @count , @max , @min ,@sum,
格式 @"@sum.age"或 @"集合屬性.@max.age"
http://www.cnblogs.com/panda1024/p/6014399.html
47.KVC和KVO的keyPath一定是屬性么?
KVO支持實例變量
48.如何關(guān)閉默認的KVO的默認實現(xiàn),并進入自定義的KVO實現(xiàn)?
http://tech.glowing.com/cn/implement-kvo/
49.apple用什么方式實現(xiàn)對一個對象的KVO?
KVO 在實現(xiàn)中通過isa 混寫(isa-swizzling)把這個對象的 isa 指針 ( isa 指針告訴 Runtime 系統(tǒng)這個對象的類是什么 ) 指向這個新創(chuàng)建的子類,對象就神奇的變成了新創(chuàng)建的子類的實例
50.代理的作用?
代理的目的是改變或傳遞控制鏈。允許一個類在某些特定時刻通知到其他類,而不需要獲取到那些類的指針。可以減少框架復(fù)雜度。
另外一點,代理可以理解為java中的回調(diào)監(jiān)聽機制的一種類似。
51.OC有多繼承嗎?沒有的話用什么代替?
OC中沒有多繼承,可以用委托代理Protocol來實現(xiàn)
52.什么是 Protocol,Delegate 一般是怎么用的?
Protocol是協(xié)議,只有.h文件,甚至可以不獨立文件
使用時 遵守協(xié)議,在本類中實現(xiàn)方法就可以
協(xié)議有required和optional的,required是必須實現(xiàn)的協(xié)議方法。
協(xié)議聲明了可以被任何類實現(xiàn)的方法
協(xié)議不是類,它是定義了一個其他對象可以實現(xiàn)的接口
如果在某個類中實現(xiàn)了協(xié)議中的某個方法,也就是這個類實現(xiàn)了那個協(xié)議。
Delegate:它本身是一個設(shè)計模式,它的意思是委托別人去做某事。
53.為什么 NotificationCenter 要 removeObserver? 如何實現(xiàn)自動 remove?
如果不移除的話,萬一注冊通知的類被銷毀以后又發(fā)了通知,程序會崩潰.因為向野指針發(fā)送了消息
實現(xiàn)自動remove:通過自釋放機制,通過動態(tài)屬性將remove轉(zhuǎn)移給第三者,解除耦合,達到自動實現(xiàn)remove。
54.我們說的oc是動態(tài)運行時語言是什么意思?
OC的動態(tài)運行時,是指OC具有動態(tài)類型和動態(tài)綁定的特性。動態(tài)類型能使程序直到執(zhí)行時才確定對象的所屬類, 其具體引用的對象在運行時才能確定。 動態(tài)綁定能使程序直到運行時才確定調(diào)用對象的實際方法。
多態(tài)。 主要是將數(shù)據(jù)類型的確定由編譯時,推遲到了運行時。
這個問題其實淺涉及到兩個概念,運行時和多態(tài)。
簡單來說,運行時機制使我們直到運行時才去決定一個對象的類別,以及調(diào)用該類別對象指定方法。
多態(tài):不同對象以自己的方式響應(yīng)相同的消息的能力叫做多態(tài)。意思就是假設(shè)生物類(life)都用有一個相同的方法-eat;
那人類屬于生物,豬也屬于生物,都繼承了life后,實現(xiàn)各自的eat,但是調(diào)用是我們只需調(diào)用各自的eat方法。
也就是不同的對象以自己的方式響應(yīng)了相同的消息(響應(yīng)了eat這個選擇器)。
55.objc中向一個nil對象發(fā)送消息將會發(fā)生什么?
objc在向一個對象發(fā)送消息時,runtime庫會根據(jù)對象的isa指針找到該對象實際所屬的類,然后在該類中的方法列表以及其父類方法列表中尋找方法運行,然后在發(fā)送消息的時候,objc_msgSend方法不會返回值,所謂的返回內(nèi)容都是具體調(diào)用時執(zhí)行的。那么,回到本題,如果向一個nil對象發(fā)送消息,首先在尋找對象的isa指針時就是0地址返回了,所以不會出現(xiàn)任何錯誤。
56.objc中向一個對象發(fā)送消息[obj foo]和objc_msgSend()函數(shù)之間有什么關(guān)系?
[obj foo];在objc動態(tài)編譯時,會被轉(zhuǎn)意為:objc_msgSend(obj,@selector(foo));
57.為什么其他語言里叫函數(shù)調(diào)用,Object-C里則叫給我對象發(fā)消息
在OC中,方法調(diào)用是向類發(fā)送消息,如[bady cry]在運行時會轉(zhuǎn)換成objc_msgSend(bady,cry),向?qū)ο蟀l(fā)送消息時根據(jù)isa指針找到類,在根據(jù)類的調(diào)度表查找方法,沒找到方法則在父類中查找直至基類,如果始終沒有找到返回nil。
Objective-C 的 Runtime 鑄就了它動態(tài)語言的特性。Objc Runtime使得C具有了面向?qū)ο竽芰Γ诔绦蜻\行時創(chuàng)建,檢查,修改類、對象和它們的方法。可以使用runtime的一系列方法實現(xiàn)。
58.isMemberOfClass 和 isKindOfClass 聯(lián)系與區(qū)別
聯(lián)系:兩者都能檢測一個對象是否是某個類的成員
區(qū)別:isKindOfClass 不僅用來確定一個對象是否是一個類的成員,也可以用來確定一個對象是否派生自該類的類的成員 ,而isMemberOfClass 只能做到第一點。
舉例:如 ClassA派 生 自NSObject 類 , ClassA *a = [ClassA alloc] init];,[a isKindOfClass:[NSObject class]] 可以檢查出 a 是否是 NSObject派生類 的成員,但 isMemberOfClass 做不到。
59.什么時候會報unrecognized selector的異常?
簡單來說:
當調(diào)用該對象上某個方法,而該對象上沒有實現(xiàn)這個方法的時候,
可以通過“消息轉(zhuǎn)發(fā)”進行解決。
簡單的流程如下,在上一題中也提到過:
objc是動態(tài)語言,每個方法在運行時會被動態(tài)轉(zhuǎn)為消息發(fā)送,即:objc_msgSend(receiver, selector)。
objc在向一個對象發(fā)送消息時,runtime庫會根據(jù)對象的isa指針找到該對象實際所屬的類,然后在該類中的方法列表以及其父類方法列表中尋找方法運行,如果,在最頂層的父類中依然找不到相應(yīng)的方法時,程序在運行時會掛掉并拋出異常unrecognized selector sent to XXX 。但是在這之前,objc的運行時會給出三次拯救程序崩潰的機會:
Method resolution
objc運行時會調(diào)用+resolveInstanceMethod:或者 +resolveClassMethod:,讓你有機會提供一個函數(shù)實現(xiàn)。如果你添加了函數(shù),那運行時系統(tǒng)就會重新啟動一次消息發(fā)送的過程,否則 ,運行時就會移到下一步,消息轉(zhuǎn)發(fā)(Message Forwarding)。
Fast forwarding
如果目標對象實現(xiàn)了-forwardingTargetForSelector:,Runtime 這時就會調(diào)用這個方法,給你把這個消息轉(zhuǎn)發(fā)給其他對象的機會。
只要這個方法返回的不是nil和self,整個消息發(fā)送的過程就會被重啟,當然發(fā)送的對象會變成你返回的那個對象。否則,就會繼續(xù)Normal Fowarding。
這里叫Fast,只是為了區(qū)別下一步的轉(zhuǎn)發(fā)機制。因為這一步不會創(chuàng)建任何新的對象,但下一步轉(zhuǎn)發(fā)會創(chuàng)建一個NSInvocation對象,所以相對更快點。
Normal forwarding
這一步是Runtime最后一次給你挽救的機會。首先它會發(fā)送-methodSignatureForSelector:消息獲得函數(shù)的參數(shù)和返回值類型。如果-methodSignatureForSelector:返回nil,Runtime則會發(fā)出-doesNotRecognizeSelector:消息,程序這時也就掛掉了。如果返回了一個函數(shù)簽名,Runtime就會創(chuàng)建一個NSInvocation對象并發(fā)送-forwardInvocation:消息給目標對象。
60.一個objc對象的isa的指針指向什么?有什么作用?
指向他的類對象,從而可以找到對象上的方法
61._objc_msgForward函數(shù)是做什么的,直接調(diào)用它將會發(fā)生什么?
_objc_msgForward是 IMP 類型,用于消息轉(zhuǎn)發(fā)的:當向一個對象發(fā)送一條消息,但它并沒有實現(xiàn)的時候,_objc_msgForward會嘗試做消息轉(zhuǎn)發(fā)。
直接調(diào)用_objc_msgForward是非常危險的事,如果用不好會直接導(dǎo)致程序Crash,但是如果用得好,能做很多非常酷的事。
一旦調(diào)用_objc_msgForward,將跳過查找 IMP 的過程,直接觸發(fā)“消息轉(zhuǎn)發(fā)”,如果調(diào)用了_objc_msgForward,即使這個對象確實已經(jīng)實現(xiàn)了這個方法,你也會告訴objc_msgSend:“我沒有在這個對象里找到這個方法的實現(xiàn)”
62.能否向編譯后得到的類中增加實例變量?能否向運行時創(chuàng)建的類中添加實例變量?為什么?
不能向編譯后得到的類中增加實例變量;
能向運行時創(chuàng)建的類中添加實例變量;
原因如下:
因為編譯后的類已經(jīng)注冊在 runtime 中,類結(jié)構(gòu)體中的 objc_ivar_list 實例變量的鏈表 和 instance_size 實例變量的內(nèi)存大小已經(jīng)確定,同時runtime 會調(diào)用 class_setIvarLayout 或 class_setWeakIvarLayout 來處理 strong weak 引用。所以不能向存在的類中添加實例變量;
運行時創(chuàng)建的類是可以添加實例變量,調(diào)用 class_addIvar 函數(shù)。但是得在調(diào)用 objc_allocateClassPair 之后,objc_registerClassPair 之前,原因同上。
63.Objective-C 中,meta-class 指的是什么?
meta-class 是 Class 對象的類, 為這個Class類存儲類方法, 當一個類發(fā)送消息時,就去那個類對應(yīng)的meta-class中查找那個消息。
每個Class都有不同的meta-class, 所有的meta-class都使用基類的meta-class(假如類繼承NSObject,那么他所對應(yīng)的meta-class也是NSObject) 作為他們的類
63.Toll-Free Bridging 是什么?什么情況下會使用?
Toll-Free Bridging用于在Foundation對象與Core Foundation對象之間交換數(shù)據(jù),俗稱橋接
在ARC環(huán)境下,Foundation對象轉(zhuǎn)成 Core Foundation對象
使用__bridge橋接以后ARC會自動管理2個對象
使用__bridge_retained橋接需要手動釋放Core Foundation對象
在ARC環(huán)境下, Core Foundation對象轉(zhuǎn)成 Foundation對象
使用__bridge橋接,如果Core Foundation對象被釋放,Foundation對象也同時不能使用了,需要手動管理Core Foundation對象
使用__bridge_transfer橋接,系統(tǒng)會自動管理2個對象
63.iOS 是如何管理內(nèi)存的
Objective-C中的對象都是基于引用計數(shù)來管理生命周期的。簡單來說就是,我們在需要持有一個對象時,調(diào)用retain讓它的引用計數(shù)+1。不需要這個對象的時候,調(diào)用release讓它的引用計數(shù)-1。當一個對象引用計數(shù)為0的時候,這個對象就會被自動銷毀
64.ARC通過什么方式幫助開發(fā)者管理內(nèi)存?
ARC相對于MRC,不是在編譯時添加retain/release/autorelease這么簡單。應(yīng)該是編譯期和運行期兩部分共同幫助開發(fā)者管理內(nèi)存。
在編譯期,ARC用的是更底層的C接口實現(xiàn)的retain/release/autorelease,這樣做性能更好,也是為什么不能在ARC環(huán)境下手動retain/release/autorelease,同時對同一上下文的同一對象的成對retain/release操作進行優(yōu)化(即忽略掉不必要的操作);
ARC也包含運行期組件,這個地方做的優(yōu)化比較復(fù)雜,但也不能被忽略。
65.一個objc對象如何進行內(nèi)存布局?(考慮有父類的情況)
所有父類的成員變量和自己的成員變量都會存放在該對象所對應(yīng)的存儲空間中.
每一個對象內(nèi)部都有一個isa指針,指向他的類對象,類對象中存放著本對象的
對象方法列表(對象能夠接收的消息列表,保存在它所對應(yīng)的類對象中)
成員變量的列表,
屬性列表,
它內(nèi)部也有一個isa指針指向元對象(meta class),元對象內(nèi)部存放的是類方法列表,類對象內(nèi)部還有一個superclass的指針,指向他的父類對象。
每個 Objective-C 對象都有相同的結(jié)構(gòu),如下圖所示:
翻譯過來就是
ISA指針
根類的實例變量
倒數(shù)第二層父類的實例變量
...
父類的實例變量
類的實例變量
根對象就是NSobject,它的superclass指針指向nil
類對象既然稱為對象,那它也是一個實例。類對象中也有一個isa指針指向它的元類(meta class),即類對象是元類的實例。元類內(nèi)部存放的是類方法列表,根元類的isa指針指向自己,superclass指針指向NSObject類
66.不手動指定autoreleasepool的前提下,一個autorealese對象在什么時刻釋放?(比如在一個vc的viewDidLoad中創(chuàng)建)
分兩種情況:手動干預(yù)釋放時機、系統(tǒng)自動去釋放。
手動干預(yù)釋放時機 - 指定autoreleasepool就是所謂的:當前作用域大括號結(jié)束時釋放。
系統(tǒng)自動去釋放 - 不手動指定autoreleasepool
Autorelease對象出了作用域之后,會被添加到最近一次創(chuàng)建的自動釋放池中,并會在當前的 runloop 迭代結(jié)束時釋放。
如果在一個vc的viewDidLoad中創(chuàng)建一個 Autorelease對象,那么該對象會在 viewDidAppear 方法執(zhí)行前就被銷毀了。
67.蘋果是如何實現(xiàn)autoreleasepool的?
autoreleasepool 以一個棧的形式實現(xiàn),主要通過下列三個函數(shù)完成.
objc_autoreleasepoolPush
objc_autoreleasepoolPop
objc_aurorelease
看函數(shù)名就可以知道,對 autorelease 分別執(zhí)行 push,和 pop 操作。銷毀對象時執(zhí)行release操作。
68.請談?wù)剝?nèi)存的使用和優(yōu)化的注意事項
重用問題:如UITableViewCells、UICollectionViewCells、UITableViewHeaderFooterViews設(shè)置正確的reuseIdentifier,充分重用;
盡量把views設(shè)置為不透明:當opque為NO的時候,圖層的半透明取決于圖片和其本身合成的圖層為結(jié)果,可提高性能;
不要使用太復(fù)雜的XIB/Storyboard:載入時就會將XIB/storyboard需要的所有資源,包括圖片全部載入內(nèi)存,即使未來很久才會使用。那些相比純代碼寫的延遲加載,性能及內(nèi)存就差了很多;
選擇正確的數(shù)據(jù)結(jié)構(gòu):學(xué)會選擇對業(yè)務(wù)場景最合適的數(shù)組結(jié)構(gòu)是寫出高效代碼的基礎(chǔ)。比如,數(shù)組: 有序的一組值。使用索引來查詢很快,使用值查詢很慢,插入/刪除很慢。字典: 存儲鍵值對,用鍵來查找比較快。集合: 無序的一組值,用值來查找很快,插入/刪除很快。gzip/zip壓縮:當從服務(wù)端下載相關(guān)附件時,可以通過gzip/zip壓縮后再下載,使得內(nèi)存更小,下載速度也更快。
延遲加載:對于不應(yīng)該使用的數(shù)據(jù),使用延遲加載方式。對于不需要馬上顯示的視圖,使用延遲加載方式。比如,網(wǎng)絡(luò)請求失敗時顯示的提示界面,可能一直都不會使用到,因此應(yīng)該使用延遲加載。
數(shù)據(jù)緩存:對于cell的行高要緩存起來,使得reload數(shù)據(jù)時,效率也極高。而對于那些網(wǎng)絡(luò)數(shù)據(jù),不需要每次都請求的,應(yīng)該緩存起來,可以寫入數(shù)據(jù)庫,也可以通過plist文件存儲。
處理內(nèi)存警告:一般在基類統(tǒng)一處理內(nèi)存警告,將相關(guān)不用資源立即釋放掉重用大開銷對象:一些objects的初始化很慢,比如NSDateFormatter
和NSCalendar,但又不可避免地需要使用它們。通常是作為屬性存儲起來,防止反復(fù)創(chuàng)建。
避免反復(fù)處理數(shù)據(jù):許多應(yīng)用需要從服務(wù)器加載功能所需的常為JSON或者XML格式的數(shù)據(jù)。在服務(wù)器端和客戶端使用相同的數(shù)據(jù)結(jié)構(gòu)很重要;
使用Autorelease Pool:在某些循環(huán)創(chuàng)建臨時變量處理數(shù)據(jù)時,自動釋放池以保證能及時釋放內(nèi)存;
正確選擇圖片加載方式:詳情閱讀UIImage加載方式
69.dispatch_barrier_async的作用是什么?
在并行隊列中,為了保持某些任務(wù)的順序,需要等待一些任務(wù)完成后才能繼續(xù)進行,使用 barrier 來等待之前任務(wù)完成,避免數(shù)據(jù)競爭等問題。
dispatch_barrier_async 函數(shù)會等待追加到Concurrent Dispatch Queue并行隊列中的操作全部執(zhí)行完之后,然后再執(zhí)行 dispatch_barrier_async 函數(shù)追加的處理,等 dispatch_barrier_async 追加的處理執(zhí)行結(jié)束之后,Concurrent Dispatch Queue才恢復(fù)之前的動作繼續(xù)執(zhí)行。
70.蘋果為什么要廢棄dispatch_get_current_queue?
dispatch_get_current_queue容易造成死鎖
71.如何用GCD同步若干個異步調(diào)用?(如根據(jù)若干個url異步加載多張圖片,然后在都下載完成后合成一張整圖)
使用Dispatch Group追加block到Global Group Queue,這些block如果全部執(zhí)行完畢,就會執(zhí)行Main Dispatch Queue中的結(jié)束處理的block
dispatch_queue_tqueue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_group_tgroup= dispatch_group_create();
dispatch_group_async(group,queue, ^{/*加載圖片1 */});
dispatch_group_async(group,queue, ^{/*加載圖片2 */});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{// 合并圖片});
73.什么是 Runloop?RunLoop都有些模式?為什么 UIScrollView 的滾動會導(dǎo)致 NSTimer 失效?
model 主要是用來指定事件在運行循環(huán)中的優(yōu)先級的,分為:
NSDefaultRunLoopMode(kCFRunLoopDefaultMode):默認,空閑狀態(tài)
UITrackingRunLoopMode:ScrollView滑動時
UIInitializationRunLoopMode:啟動時
NSRunLoopCommonModes(kCFRunLoopCommonModes):Mode集合
蘋果公開提供的 Mode 有兩個:
NSDefaultRunLoopMode(kCFRunLoopDefaultMode)
NSRunLoopCommonModes(kCFRunLoopCommonModes)
定時器里面有個runoop mode,一般定時器是運行在defaultmode上。但是如果滑動了這個頁面,主線程runloop會轉(zhuǎn)到UITrackingRunLoopMode中,這時候就不能處理定時器了,造成定時器失效,原因就是runroop mode選錯了,解決辦法有2個
一個是更改mode為NSRunLoopCommonModes(無論runloop運行在哪個mode,都能運行),
還有種辦法是切換到主線程來更新UI界面的刷新
74.UITableViewCell上有個UILabel,顯示NSTimer實現(xiàn)的秒表時間,手指滾動cell過程中,label是否刷新,為什么?
這是否刷新取決于timer加入到Run Loop中的Mode是什么。
75.猜想runloop內(nèi)部是如何實現(xiàn)的?
functionloop()
{ ? initialize();
do{
var message = get_next_message();? ? ??
? process_message(message);? ?
?}while(message != quit);}
int ? main(intargc,char* argv[]){
//程序一直運行狀態(tài)
while(AppIsRunning) {
//睡眠狀態(tài),等待喚醒事件
id whoWakesMe = SleepForWakingUp();
//得到喚醒事件
idevent= GetEvent(whoWakesMe);
//開始處理事件
HandleEvent(event);??
? }return0;}
76.runloop和線程有什么關(guān)系?
主線程的run loop默認是啟動的。
對其它線程來說,run loop默認是沒有啟動的,如果你需要更多的線程交互則可以手動配置和啟動,如果線程只是去執(zhí)行一個長時間的已確定的任務(wù)則不需要。
在任何一個 Cocoa 程序的線程中,都可以通過以下代碼來獲取到當前線程的 run loop 。
run loop和線程是緊密相連的,可以這樣說run loop是為了線程而生,沒有線程,它就沒有存在的必要。Run loops是線程的基礎(chǔ)架構(gòu)部分
77.iOS 7的多任務(wù)添加了哪兩個新的 API? 各自的使用場景是什么?
后臺獲取(Background Fetch):后臺獲取使用場景是用戶打開應(yīng)用之前就使app有機會執(zhí)行代碼來獲取數(shù)據(jù),刷新UI。這樣在用戶打開應(yīng)用的時候,最新的內(nèi)容將已然呈現(xiàn)在用戶眼前,而省去了所有的加載過程。
推送喚醒(Remote Notifications):使用場景是使設(shè)備在接收到遠端推送后讓系統(tǒng)喚醒設(shè)備和我們的后臺應(yīng)用,并先執(zhí)行一段代碼來準備數(shù)據(jù)和UI,然后再提示用戶有推送。這時用戶如果解鎖設(shè)備進入應(yīng)用后將不會再有任何加載過程,新的內(nèi)容將直接得到呈現(xiàn)。
78.請解釋一下Handoff是什么,并簡述它是如何實現(xiàn)iOS、Mac/網(wǎng)頁應(yīng)用互通的。
Handoff 是在 OS X 和 iOS 跨設(shè)備連續(xù)性的拓展的用戶體驗的功能。Handoff 使用戶可以在一臺設(shè)備中開始一個活動,然后切換到其他設(shè)備并在該設(shè)備恢復(fù)相同的活動。例如,某個用戶從 Safari 移到登陸了相同的 Apple ID 賬戶的 iOS 設(shè)備瀏覽一篇很長的文章,相同的頁面會在 iOS 的 Safari 打開,并且滾動條位置和啟始設(shè)備相同,Handoff 使這個體驗盡可能平穩(wěn)順暢。
79.內(nèi)存管理主要分兩種模式,MRC 和ARC
1、MRC MRC是手動管理內(nèi)存,xcode4.1以及一下版本沒有ARC
引用計數(shù)概念 retain +1 release -1
內(nèi)存釋放池Release Pool:把需要釋放的內(nèi)存統(tǒng)一放在一個池子中,當池子被抽干后(drain),池子中所有的內(nèi)存空間也被自動釋放掉。內(nèi)存池的釋放操作分為自動和手動。自動釋放受runloop機制影響。
2、ARC ACR是自動管理內(nèi)存,自動引用計數(shù) 重點:循環(huán)引用,用weak 修飾來釋放(block,delegate會出現(xiàn)這種情況)