1.一個NSObject對象占用多少內存?
64bit: sizeof 也是以8字節(jié)對齊,是個運算符直接傳類型計算
class_getInstanceSize函數 傳類抑党,得最終創(chuàng)建得至少占用多少內存空間
系統(tǒng)分配了16個字節(jié)給NSObject對象(通過malloc_size函數獲得以16字節(jié)對齊)
但NSObject對象內部只使用了8個字節(jié)的空間(64bit環(huán)境下淹辞,可以通過class_getInstanceSize函數獲得以8字節(jié)對齊)
2.對象的isa指針指向哪里航攒?
instance對象的isa指向class對象
class對象的isa指向meta-class對象
meta-class對象的isa指向基類的meta-class對象
3.OC的類信息存放在哪里业扒?
instance對象的isa指向class對象 64位開始需要 &ISA_MASK獲取
class對象的isa指向meta-class對象 64位開始需要 &ISA_MASK獲取
meta-class對象的isa指向基類的meta-class對象
class的superclass指向父類的class
如果沒有父類叮称,superclass指針為nil
meta-class的superclass指向父類的meta-class
基類的meta-class的superclass指向基類的class
instance調用對象方法的軌跡
isa找到class,方法不存在笑窜,就通過superclass找父類
class調用類方法的軌跡
isa找meta-class致燥,方法不存在,就通過superclass找父類
4.iOS用什么方式實現對一個對象的KVO排截?(KVO的本質是什么嫌蚤?)
利用RuntimeAPI動態(tài)生成一個子類NSKVONotifying_xxx辐益,并且讓instance對象的isa指向這個全新的子類
當修改instance對象的屬性時,會調用Foundation的_NSSetXXXValueAndNotify函數
willChangeValueForKey:
父類原來的setter
didChangeValueForKey:
內部會觸發(fā)監(jiān)聽器(Oberser)的監(jiān)聽方法( observeValueForKeyPath:ofObject:change:context:)
5.如何手動觸發(fā)KVO搬葬?
手動調用willChangeValueForKey:和didChangeValueForKey:
6.直接修改成員變量會觸發(fā)KVO么荷腊?
不會觸發(fā)KVO
7.通過KVC修改屬性會觸發(fā)KVO么?
會觸發(fā)KVO 原因請參照8.KVC的setvalueforkey的賦值過程
8.KVC的賦值和取值過程是怎樣的急凰?原理是什么?
KVC的全稱是Key-Value Coding猜年,俗稱“鍵值編碼”抡锈,可以通過一個key來訪問某個屬性
2.valueForKey:的原理
9.Category的使用場景是什么?
Category的實現原理
Category編譯之后的底層結構是struct category_t乔外,里面存儲著分類的對象方法床三、類方法、屬性杨幼、協(xié)議信息
在程序運行的時候撇簿,runtime會將Category的數據,合并到類信息中(類對象差购、元類對象中)
struct _category_t {
const char *name; // 類名稱
struct _class_t *cls; //
const struct _method_list_t *instance_methods; //實例方法
const struct _method_list_t *class_methods; //類方法
const struct _protocol_list_t *protocols; // 協(xié)議
const struct _prop_list_t *properties; // 屬性
};
10.Category和Class Extension的區(qū)別是什么四瘫?
Class Extension在編譯的時候,它的數據就已經包含在類信息中
Category是在運行時欲逃,才會將數據合并到類信息中
11.Category中有l(wèi)oad方法嗎找蜜?load方法是什么時候調用的?load 方法能繼承嗎稳析?
有l(wèi)oad方法
load方法在runtime加載類洗做、分類的時候調用
load方法可以繼承,但是一般情況下不會主動去調用load方法彰居,都是讓系統(tǒng)自動調用
12.Category的加載處理過程
1.通過Runtime加載某個類的所有Category數據
2.把所有Category的方法诚纸、屬性、協(xié)議數據陈惰,合并到一個大數組中
后面參與編譯的Category數據畦徘,會在數組的前面
3.將合并后的分類數據(方法、屬性奴潘、協(xié)議)旧烧,插入到類原來數據的前面
13.load、initialize方法的區(qū)別什么画髓?它們在category中的調用的順序掘剪?以及出現繼承時他們之間的調用過程?
load與initialize區(qū)別:
1.調用方式
1>load是根據函數地址直接調用
2>initialize是通過objc_msgSend調用
2.調用時刻
1>load是runtime加載類奈虾,分類的時候調用(只會調用一次)
2>initialize是類第一次接收到消息的時候調用夺谁,每一個類只會被initialize一次(父類的initialize可能會被調用多次)
三個加載順序 load -> c++ > main
load廉赔、initialize的調用順序
1.load
先調用類的+load
按照編譯先后順序調用(先編譯,先調用)
調用子類的+load之前會先調用父類的+load
再調用分類的+load
按照編譯先后順序調用(先編譯匾鸥,先調用)
2.initialize
a>先初始化父類
b>在初始化子類(可能最終調用的也是父類的初始化方法)
+initialize和+load的很大區(qū)別是蜡塌,+initialize是通過objc_msgSend進行調用的,所以有以下特點
如果子類沒有實現+initialize勿负,會調用父類的+initialize(所以父類的+initialize可能會被調用多次)
如果分類實現了+initialize馏艾,就覆蓋類本身的+initialize調用
14.Category能否添加成員變量?如果可以奴愉,如何給Category添加成員變量琅摩?
不能直接給Category添加成員變量,但是可以間接實現Category有成員變量的效果
添加關聯對象
void objc_setAssociatedObject(id object, const void * key,
id value, objc_AssociationPolicy policy)
獲得關聯對象
id objc_getAssociatedObject(id object, const void * key)
移除所有的關聯對象
void objc_removeAssociatedObjects(id object)
內部實現邏輯可參考此圖
15.block的原理是怎樣的锭硼?本質是什么房资?
block本質上也是一個OC對象,它內部也有個isa指針
block是封裝了函數調用以及函數調用環(huán)境的OC對象
block的底層結構如下圖所示
為了保證block內部能夠正常訪問外部的變量檀头,block有個變量捕獲機制
/// 值捕獲
int multiplier = 6;
int (^myBlock)(int) = ^(int num) {
return num * multiplier;
};
// 這是值捕獲(multiplier 的值為 6)
NSLog(@"Result: %d", myBlock(5)); // 輸出: Result: 30
multiplier = 10;
NSLog(@"Result: %d", myBlock(5)); // 輸出: Result: 30
/// 引用捕獲/指針捕獲
// static int multiplier = 6;
__block int multiplier = 6;
int (^myBlock)(int) = ^(int num) {
multiplier += 1; // 修改 multiplier 的值
return num * multiplier;
};
// 這是引用捕獲(multiplier 的初值為 6)
NSLog(@"Result: %d", myBlock(5)); // 輸出: Result: 35 (7*5)
multiplier = 10;
NSLog(@"Result: %d", myBlock(5)); // 輸出: Result: 55 (11*5)
分類:
Block 有三種具體類型:
- _NSConcreteGlobalBlock:全局 Block轰异,不捕獲任何變量。通常聲明在全局作用域或函數中且不使用外部變量的 Block暑始。
2._NSConcreteStackBlock:棧上的 Block搭独,默認情況下定義在函數或方法中且捕獲外部變量的 Block。
3._NSConcreteMallocBlock:堆上的 Block蒋荚,通過 Block_copy 或 [block copy] 創(chuàng)建戳稽,通常用于延長 Block 生命周期。
#import <Foundation/Foundation.h>
void exampleFunction(void) {
int localVar = 10;
// 全局 Block期升,不捕獲任何外部變量
void (^globalBlock)(void) = ^{
NSLog(@"This is a global block!");
};
// 捕獲 localVar 的 Block
// ARC 環(huán)境下系統(tǒng)會自動將外部變量的 Block 從棧轉移到堆上
// MRC 會在棧上
void (^stackBlock)(void) = ^{
NSLog(@"Captured localVar: %d", localVar);
};
// 將捕獲 localVar 的 Block 顯式復制到堆上
void (^heapBlock)(void) = [stackBlock copy];
// 打印 Block 類型
NSLog(@"Global Block Type: %@", [globalBlock class]);
NSLog(@"Stack Block (likely malloc under ARC) Type: %@", [stackBlock class]);
NSLog(@"Heap Block Type: %@", [heapBlock class]);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
exampleFunction();
}
return 0;
}
16.如何在block內部修改外部的局部變量
1.使用static 修飾局部變量
2.把局部變量改為全局變量
3.使用__block修飾局部變量
其本質是:
編譯器會將__block變量包裝成一個對象惊奇,內部結構如下
16.解決循環(huán)飲用問題
ARC
1.__weak、__unsafe_unretained
2.用__block解決(必須要調用block)
MRC
__unsafe_unretained播赁、__block(無需調用block)
17.block的屬性修飾詞為什么是copy颂郎?使用block有哪些使用注意?
1容为、Blocks 位于棧上:默認情況下乓序,Blocks 在定義時位于棧內存上。這種棧上的 Block 是臨時的坎背,在作用域結束后會失效替劈。為了使 Block 在作用域之外保持有效,需要把它拷貝到堆內存得滤。
// __NSGlobalBlock__ 全局block
void (^myBlock)(void) = ^{
NSLog(@"Hello, World!");
};
2陨献、屬性存儲的持久性:在類中的屬性如果需要保存 Block,就必須將 Block 從椂拷貝到堆上眨业,這樣可以確保該 Block 在屬性生存期內不會被釋放急膀。因此,使用 copy 修飾符保證了 Block 的持久性龄捡。
@property (nonatomic, copy) void (^myBlock)(void);
使用注意:
1卓嫂、循環(huán)引用:在使用 Block 時,需要特別注意循環(huán)引用問題(retain cycle)聘殖,尤其是在 Block 捕獲 self 的情況下晨雳。如果 Block 內部直接引用 self,可能會導致 self 和 Block 相互持有就斤,導致內存泄露悍募。
self.myBlock = ^{
// 直接引用 self 可能導致循環(huán)引用
[self doSomething];
};
// 解決辦法:
// 使用 __weak 或 __block 來打破循環(huán)引用:
__weak typeof(self) weakSelf = self;
self.myBlock = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf doSomething];
}
};
2、使用 __block 變量:使用 __block 修飾符來捕獲并允許在 Block 內部修改的變量洋机。如果不使用 __block,那么這些變量在 Block 內部是只讀的洋魂。
__block int counter = 0;
void (^incrementCounter)(void) = ^{
counter++; // 沒有 __block 修飾符绷旗,這里會報錯
};
incrementCounter();
3、注意生命周期管理:特別是在異步操作或延遲執(zhí)行的情況下副砍,確保 Block 的生命周期和作用域的合理管理衔肢,防止意外的內存泄露或訪問無效內存。
4豁翎、線程安全:如果 Block 內部訪問或修改了屬性或全局變量角骤,確保在多線程環(huán)境下進行適當的同步和線程安全處理。
5心剥、注意 Block 返回值:在引用 Block 時邦尊,如果 Block 產生返回值,注意合理處理返回值优烧,防止意外的邏輯錯誤蝉揍。
6、避免捕獲大量變量:盡量避免在 Block 中捕獲大量的局部變量畦娄,合理設計 Block 的職責和放置位置又沾,以保持代碼清晰簡潔。
18.block在修改NSMutableArray熙卡,需不需要添加__block杖刷?
NSMutableArray *array = [NSMutableArray array];
void (^block)(void) = ^{
[array addObject:@10];
};
這種情況是不會報錯的,因為沒有修改array的指向驳癌,只是使用了array而已滑燃。所以不需要添加__block。
19.任務的執(zhí)行速度的影響因素
1.cpu的調度能力
2.任務的復雜度
3.優(yōu)先級
4.線程狀態(tài)
20.優(yōu)先級翻轉 (IO VS CPU 優(yōu)先級提升)
1.IO 密集型 頻繁等待
2.CPU 密集 很少等待
3.餓死
4.調度
優(yōu)先級影響因素
1.用戶指定
2.等待的頻繁程度
3.長時間不執(zhí)行
21.方法的本質喂柒,SEL是什么不瓶?IMP是什么禾嫉?兩者之間的關系是什么?
方法的本質:發(fā)送消息蚊丐,消息會有以下幾個流程
1.快速查找(objc_msgSend) ~cache_t 緩存消息
2.慢速查找~ 遞歸自己|父類 熙参!lookUpimpOrForward
3.查找不到消息:動態(tài)方法解析 ~resolveInstanceMethod
4.消息快速轉發(fā)~ forwardingTargetForSelector
5.消息慢速轉發(fā) ~methodSignatureForSelector & forwardInvocation
sel是方法編號 ~ 在read_image期間就編譯入了內存
imp就是我們函數實現指針,找imp就是找函數的過程
sel就相當于書本的目錄title
imp就是書本的頁碼
查找具體的函數就是想看這本書里面具體篇章的內容
1.我們首先知道想看什么 ~title(sel)
2.根據目錄對應的頁碼(imp)
3.翻看到具體的內容
總結如下:
SEL: 方法名稱的唯一標識符麦备,通過 @selector(methodName) 獲取孽椰。
IMP: 指向方法實現的指針,可以通過 [someObject methodForSelector:selector] 獲取凛篙。
關系: SEL 標識方法名稱黍匾, runtime 通過這個 SEL 來找到相應的 IMP,實現方法的調用呛梆。
// selector 是 sayHello 方法名的 SEL 標識符锐涯。
// imp 是實現 sayHello 方法的指針。
// 調用 imp(person, selector) 就等同于 [person sayHello]填物。
Person *person = [[Person alloc] init];
SEL selector = @selector(sayHello);
IMP imp = [person methodForSelector:selector];
// 調用 IMP
imp(person, selector);
這種設計使得 Objective-C 具備了動態(tài)性纹腌,可以在運行時靈活地查找和調用方法,這也是 Objective-C 語言強大動態(tài)特性的基礎滞磺。
22.能否向編譯后的得到的類中增加還實例變量升薯?能否向運行時創(chuàng)建的類中添加實例變量?
1.不能向編譯后的得到的類中增加實例變量
2.只要類沒有注冊到內存中還是可以添加的
原因:我們編譯好的示例變量存儲的位置在ro击困,一旦編譯完成涎劈,內存結構就完全確定無法修改
可以添加屬性 + 方法
23.main函數加載之前做了什么
代碼經過預處理階段-> 編譯階段->后端LLVM生成匯編碼->生成目標文件.o->鏈接靜動態(tài)庫->綁定架構
1.動態(tài)庫鏈接庫
2.ImageLoader加載可執(zhí)行文件, 里邊是被編譯過的符號,代碼等
3.runtime與+load
https://www.imooc.com/article/38338
24.runtime方法交換 有哪些弊端?
第一個風險是阅茶,需要在 +load 方法中進行方法交換蛛枚。因為如果在其他時候進行方法交換,難以保證另外一個線程中不會同時調用被交換的方法目派,從而導致程序不能按預期執(zhí)行坤候。
第二個風險是,被交換的方法必須是當前類的方法企蹭,不能是父類的方法白筹,直接把父類的實現拷貝過來不會起作用。父類的方法必須在調用的時候使用谅摄,而不是方法交換時使用徒河。
第三個風險是,交換的方法如果依賴了 cmd送漠,那么交換后顽照,如果 cmd 發(fā)生了變化,就會出現各種奇怪問題,而且這些問題還很難排查代兵。特別是交換了系統(tǒng)方法尼酿,你無法保證系統(tǒng)方法內部是否依賴了 cmd。
第四個風險是植影,方法交換命名沖突裳擎。如果出現沖突,可能會導致方法交換失敗思币。
25.JSBridge的底層原理
JSBridge是什么鹿响?JSBridge是一種橋接器,通過JS引擎或Webview容器為媒介 谷饿,約定協(xié)議進行通信惶我,實現Native端和Web端雙向通信的一種機制
將Native端的接口封裝成js接口
將Web端js接口封裝成原生接口
WebView是Native中加載網頁的一個控件,該組件提供一個evaluateJavascript()方法運行JS代碼博投。我們要做的是在Native端執(zhí)行一個js方法绸贡,在Web端進行監(jiān)聽。
當Web端要請求Native端的方法時毅哗,我們首先要自定義一個URL Schema恃轩,向Native端發(fā)起一個請求,最后在Native端的WebView進行監(jiān)聽黎做,下面我們看看具體實現
26.OC的協(xié)議與swift協(xié)議的區(qū)別:
OC中的協(xié)議:
1、受限于委托代理的含義松忍,多?于不同類之間的傳值與回調蒸殿。
Swift的協(xié)議:
1、可以通過協(xié)議 (extension) 擴展鸣峭,實現協(xié)議的?法(OC不?)
2宏所、定義屬性?法
3、通過抽取不同類中的相同?法和屬性摊溶,實現模塊化減少耦合爬骤。使面向協(xié)議編程成為可能
4、不需要單獨聲明協(xié)議對象和指定代理
5莫换、協(xié)議可以繼承其他協(xié)議
27.講一下 OC 的消息機制
OC中的方法調用其實都是轉成了objc_msgSend函數的調用霞玄,給receiver(方法調用者)發(fā)送了一條消息(selector方法名)
objc_msgSend底層有3大階段
消息發(fā)送(當前類、父類中查找)拉岁、動態(tài)方法解析坷剧、消息轉發(fā)
28.消息轉發(fā)機制流程
1.objc_msgSend執(zhí)行流程01-消息發(fā)送
2.objc_msgSend執(zhí)行流程02-動態(tài)方法解析
3.objc_msgSend的執(zhí)行流程03-消息轉發(fā)
29.什么是Runtime?平時項目中有用過么喊暖?
OC是一門動態(tài)性比較強的編程語言惫企,允許很多操作推遲到程序運行時再進行
OC的動態(tài)性就是由Runtime來支撐和實現的,Runtime是一套C語言的API陵叽,封裝了很多動態(tài)性相關的函數
平時編寫的OC代碼纽谒,底層都是轉換成了Runtime API進行調用
具體應用:
利用關聯對象(AssociatedObject)給分類添加屬性
遍歷類的所有成員變量(修改textfield的占位文字顏色浅萧、字典轉模型乞旦、自動歸檔解檔)
交換方法實現(交換系統(tǒng)的方法)
利用消息轉發(fā)機制解決方法找不到的異常問題
......
30.打印結果分別是什么?
@interface MJPerson:NSObject
@end
@implementation MJPerson
@end
@interface MJStudent:MJPerson
@end
@implementation MJStudent
- (instancetype)init {
if (self = [super init]) {
NSLog(@"[self class] = %@", [self class]);
NSLog(@"[super class] = %@", [super class]);
NSLog(@"[self superclass] = %@", [self superclass]);
NSLog(@"[super superclass] = %@", [super superclass]);
/*
[self class] = MJStudent
[super class] = MJStudent
[self superclass] = MJPerson
[super superclass] = MJPerson
**/
// [self class] 和 [super class] 都返回 self 的類页畦,因為 super 只是改變了方法查找的起點,但不改變 self禽车。
// [self superclass] 和 [super superclass] 都返回 MJStudent 的父類 MJPerson寇漫,因為 self 還是 MJStudent 的實例,方法查找不會改變這種關系殉摔。
}
return self;
}
@end
BOOL res1 = [[NSObject class] isKindOfClass:[NSObject class]];
BOOL res2 = [[NSObject class] isMemberOfClass:[NSObject class]];
BOOL res3 = [[MJPerson class] isKindOfClass:[MJPerson class]];
BOOL res4 = [[MJPerson class] isMemberOfClass:[MJPerson class]];
NSLog(@"%d,%d,%d,%d",res1,res2,res3,res4);
// 1 0 0 0
isKindOfClass
1.類方法
當前類的元類(父元類州胳,根元類,根類)是否與cls相等
2.實例方法
判斷當前類(父類逸月、根類)是否與cls相等
isMemberOfClass
1.類方法
判斷當前的元類是否與cls相等
2.實例方法
判斷當前類是否與cls相等
31.OC 與 swift的區(qū)別
1.swift是靜態(tài)語言栓撞,有類型推斷瓤湘,OC是動態(tài)語言恩尾。
2.swift面向協(xié)議編程,OC面向對象編程
3.swift注重值類型木人,OC注重引用類型。
4.swift支持泛型醒第,OC只支持輕量泛型
5.swift支持靜態(tài)派發(fā)(效率高)稠曼、動態(tài)派發(fā)(函數表派發(fā)霞幅、消息派發(fā))方式,OC支持動態(tài)派發(fā)(消息派發(fā))方式抵赢。
6.swift支持函數式編程
7.swift的協(xié)議不僅可以被類實現铅鲤,也可以被struct和enum實現
8.swift有元組類型鹏往、支持運算符重載
9.swift支持命名空間
10.swift支持默認參數
11.swift比oc代碼更加簡潔
32.super的本質
super調用伊履,底層會轉換為objc_msgSendSuper2函數的調用,接收2個參數
struct objc_super2
SEL
struct objc_super2 {
id receiver; //receiver是消息接收者
Class current_class; //current_class是receiver的Class對象
}
33.講講 RunLoop哄辣,項目中有用到嗎力穗?
runloop是事件接收和分發(fā)機制的一種實現。
runloop是線程的基礎組成部分超全,一個runloop就是一個事件處理循環(huán),用來不斷的處理輸入事件和調配工作,使用runloop的目的是為了讓線程在有工作的時候工作救军,沒有工作的時候休眠唱遭,從而節(jié)約資源拷泽。runloop單從其工作內容來看和快遞的中轉站有很大的相似之處拆吆。
iOS中有2套API來訪問和使用RunLoop
Foundation:NSRunLoop
Core Foundation:CFRunLoopRef
應用范疇
定時器(Timer)枣耀、PerformSelector
GCD Async Main Queue
事件響應、手勢識別颅围、界面刷新
網絡請求
AutoreleasePool
34.runloop內部實現邏輯?
35.runloop與線程的關系
每條線程都有唯一的一個與之對應的RunLoop對象
RunLoop保存在一個全局的Dictionary里,線程作為key墩邀,RunLoop作為value
線程剛創(chuàng)建時并沒有RunLoop對象,RunLoop會在第一次獲取它時創(chuàng)建
RunLoop會在線程結束時銷毀
主線程的RunLoop已經自動獲戎窈!(創(chuàng)建),子線程默認沒有開啟RunLoop
36.timer 與 runloop 的關系艰争?
我們創(chuàng)建timer時,之所以timer能運行逾柿,是因為創(chuàng)建timer時睦柴,一般情況下坦敌,是在主線程中創(chuàng)建,這時會默認將timer以defaultRunloopModel的類型加入主線程,而主線程的runloop對象默認是打開的搭儒,從而timer可以運行。
37.程序中添加每3秒響應一次的NSTimer铃岔,當拖動tableview時timer可能無法響應要怎么解決?
方法1:創(chuàng)建一條新的線程纺且,然后將timer加入到新創(chuàng)建線程對應的runloop對象中即可。
方法2:依舊是將timer注冊在主線程的runloop中,不過注冊的model類型為commonModels,之所以加入到commonModels中就行孤钦,是因為commonModels是一種公用的類型静袖,每當runloop內容發(fā)生改變時,runloop會將commonModels類型下所有注冊的內容同步到對應的model中捐康。
38.說說runLoop的幾種狀態(tài)
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), //即將進入runloop
kCFRunLoopBeforeTimers = (1UL << 1), //即將處理Timer
kCFRunLoopBeforeSources = (1UL << 2), //即將處理Source
kCFRunLoopBeforeWaiting = (1UL << 5),//即將進入休眠
kCFRunLoopAfterWaiting = (1UL << 6),//剛從休眠中醒來
kCFRunLoopExit = (1UL << 7), //剛從休眠中喚醒
kCFRunLoopAllActivities = 0x0FFFFFFFU //即將退出runloop
};
39.runloop的mode作用是什么姐仅?
1.kCFRunLoopDefaultMode:App的默認Mode掏膏,通常主線程是在這個Mode下運行
2. UITrackingRunLoopMode:界面跟蹤 Mode磕道,用于 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其他 Mode 影響
3. UIInitializationRunLoopMode: 在剛啟動 App 時第進入的第一個 Mode疯特,啟動完成后就不再使用,會切換到kCFRunLoopDefaultMode
4. GSEventReceiveRunLoopMode: 接受系統(tǒng)事件的內部 Mode,通常用不到
5. kCFRunLoopCommonModes: 這是一個占位用的Mode抱冷,作為標記kCFRunLoopDefaultMode和UITrackingRunLoopMode用赵讯,并不是一種真正的Mode
40.你理解的多線程?
在iOS中每個進程啟動后都會建立一個主線程(UI線程)组底,這個線程是其他線程的父線程。由于在iOS中除了主線程,其他子線程是獨立于Cocoa Touch的莫秆,所以只有主線程可以更新UI界面
1.進程:進程是指在系統(tǒng)中正在運行的一個應用程序,每一個程序都是一個進程,并且進程之間是獨立的用狱,每個進程均運行在其專用且受保護的內存空間內
2.線程(thread)一個進程要想執(zhí)行任務,必須得有線程。線程中任務的執(zhí)行是串行的鲁森,要在一個線程中執(zhí)行多個任務,那么只能一個一個地按順序執(zhí)行這些任務,也就是說榜晦,在同一時間內,一個線程只能執(zhí)行一個任務,由此可以理解線程是進程中的一條執(zhí)行路徑喻频。一個進程中至少包含一條線程,即主線程,創(chuàng)建線程的目的就是為了開啟一條新的執(zhí)行路徑狰挡,運行指定的代碼,與主線程中的代碼實現同時運行。
3.主線程:處理UI超凳,所有更新UI的操作都必須在主線程上執(zhí)行。不要把耗時操作放在主線程,會卡界面驰吓。
4.多線程:是指從軟件或者硬件上實現多個線程并發(fā)執(zhí)行的技術。具有多線程能力的計算機因有硬件支持而能夠在同一時間執(zhí)行多于一個線程桥言,進而提升整體處理性能。在同一時刻,一個CPU只能處理一條線程扰柠,但CPU可以在多條線程之間快速的切換程剥,只要切換的足夠快舔腾,就造成了多線程一同執(zhí)行的假象。
5.同步(sync):只能在當前線程按先后順序依次執(zhí)行扳还,不開啟新線程棘劣。
6.異步(async):可以在當前線程開啟多個新線程執(zhí)行首昔,可不按順序執(zhí)行
7.并發(fā):多個任務并發(fā)(同時)執(zhí)行
8.串行:一個任務執(zhí)行完畢后拘荡,再執(zhí)行下一個任務
41.iOS的多線程方案有哪幾種网缝?你更傾向于哪一種?
42.線程的狀態(tài)與生命周期
線程的生命周期是:新建 - 就緒 - 運行 - 阻塞 - 死亡
新建:實例化線程對象远寸。
就緒:向線程對象發(fā)送start消息矗愧,線程對象被加入可調度線程池等待CPU調度夜涕。
運行:CPU 負責調度可調度線程池中線程的執(zhí)行。線程執(zhí)行完成之前,狀態(tài)可能會在就緒和運行之間來回切換俏拱。就緒和運行之間的狀態(tài)變化由CPU負責,程序員不能干預。
阻塞:當滿足某個預定條件時逢捺,可以使用休眠或鎖绷柒,阻塞線程執(zhí)行伺绽。sleepForTimeInterval(休眠指定時長),sleepUntilDate(休眠到指定日期),@synchronized(self):(互斥鎖)。
死亡:正常死亡,線程執(zhí)行完畢挺份。非正常死亡朵你,當滿足某個條件后,在線程內部中止執(zhí)行或在主線程中止線程對象搞监。[NSThread exit]一旦強行終止線程秤标,后續(xù)的所有代碼都不會被執(zhí)行够委。線程外死亡屈嗤,[thread cancel] 并不會直接取消線程,只是給線程對象添加isCancelled標記。死亡后線程對象的 isFinished屬性為YES;如果是發(fā)送calcel消息料滥,線程對象的isCancelled 屬性為YES然眼;死亡后stackSize == 0,內存空間被釋放葵腹。
https://blog.csdn.net/qq_27247497/article/details/120047237
43.GCD 的隊列類型
并發(fā)隊列(Concurrent Dispatch Queue)
可以讓多個任務并發(fā)(同時)執(zhí)行(自動開啟多個線程同時執(zhí)行任務)
并發(fā)功能只有在異步(dispatch_async)函數下才有效
串行隊列(Serial Dispatch Queue)
讓任務一個接著一個地執(zhí)行(一個任務執(zhí)行完畢后,再執(zhí)行下一個任務)
44.說一下 OperationQueue 和 GCD 的區(qū)別践宴,以及各自的優(yōu)勢
1.GCD的核心是C語言寫的系統(tǒng)服務鲸匿,執(zhí)行和操作簡單高效,因此NSOperation底層也通過GCD實現阻肩,換個說法就是NSOperation是對GCD更高層次的抽象带欢,這是他們之間最本質的區(qū)別。因此如果希望自定義任務,建議使用NSOperation
2.依賴關系洪囤,NSOperation可以設置兩個NSOperation之間的依賴徒坡,第二個任務依賴于第一個任務完成執(zhí)行,GCD無法設置依賴關系瘤缩,不過可以通過dispatch_barrier_async來實現這種效果喇完;
3.KVO(鍵值對觀察),NSOperation很容易判斷Operation當前的狀態(tài)(是否執(zhí)行剥啤,是否取消)锦溪,對此GCD無法通過KVO進行判斷;
4.優(yōu)先級府怯,NSOperation可以設置自身的優(yōu)先級刻诊,但是優(yōu)先級高的不一定先執(zhí)行,GCD只能設置隊列的優(yōu)先級牺丙,無法在執(zhí)行的block設置優(yōu)先級则涯;
5.繼承,NSOperation是一個抽象類冲簿,實際開發(fā)中常用的兩個類是NSInvocationOperation和NSBlockOperation粟判,同樣我們可以自定義NSOperation,GCD執(zhí)行任務可以自由組裝峦剔,沒有繼承那么高的代碼復用度档礁;
6.效率,直接使用GCD效率確實會更高效吝沫,NSOperation會多一點開銷呻澜,但是通過NSOperation可以獲得依賴,優(yōu)先級惨险,繼承羹幸,鍵值對觀察這些優(yōu)勢,相對于多的那么一點開銷確實很劃算辫愉,魚和熊掌不可得兼睹欲,取舍在于開發(fā)者自己;
根據實際開發(fā)中來說一屋,GCD使用情況較多窘疮,簡單高效,從編程原則上來看冀墨,應該是使用高層次的抽象闸衫,避免使用低層次的抽象,那么無疑我們應該選擇NSOperation诽嘉,因為復雜的任務可以自己通過NSOperation實現蔚出,日常還是GCD的天下弟翘,畢竟GCD有更高的并發(fā)和執(zhí)行能力。
45.線程安全的處理手段有哪些骄酗?
1稀余、互斥鎖(synchronized):多條線程搶奪同一塊資源時,當一個線程獲得這個鎖之后趋翻,其他想要獲得此鎖的線程將會被阻塞睛琳,直到該鎖被釋放
缺點:需要消耗大量的CPU資源
2、自旋鎖(OS_SPINLOCK_INIT):獲取了自旋鎖后踏烙,線程會反復檢查鎖變量是否可用师骗。避免了進程上下文的調度開銷,適用于線程阻塞時間短的場合
3讨惩、信號量(DispatchSemaphore):一個計數信號辟癌,可用來資源訪問的并發(fā)控制
let semaphore = DispatchSemaphore(value: 1)// 初始化信號量
semaphore.wait() //等待信號,信號量-1荐捻,當信號總量為0時會一直等待
semaphore.signal() //發(fā)送信號黍少,信號量+1
46.OC你了解的鎖有哪些?
OSSpinLock //自旋鎖 IOS10后已廢棄
//os_unfair_lock用于取代不安全的OSSpinLock 处面,從iOS10開始才支持
os_unfair_lock //互斥鎖
//pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE) 遞歸鎖
// pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_DEFAULT); 互斥鎖
pthread_mutex //互斥鎖 / 遞歸鎖
dispatch_semaphore //信號量
dispatch_queue(DISPATCH_QUEUE_SERIAL) //穿行隊列
NSLock //互斥鎖 對pthread_mutex的封裝
NSRecursiveLock //遞歸鎖 對mutex遞歸鎖的封裝
NSCondition // NSCondition是對mutex和cond的封裝
//NSConditionLock是對NSCondition的進一步封裝厂置,可以設置具體的條件值
NSConditionLock
@synchronized //@synchronized是對mutex遞歸鎖的封裝
47.多線程的安全隱患,解決方式
資源共享
1塊資源可能會被多個線程共享鸳君,也就是多個線程可能會訪問同一塊資源
比如多個線程訪問同一個對象、同一個變量患蹂、同一個文件
當多個線程訪問同一塊資源時或颊,很容易引發(fā)數據錯亂和數據安全問題
解決方案:使用線程同步技術(同步,就是協(xié)同步調传于,按預定的先后次序進行)
常見的線程同步技術是:加鎖
48.下面輸出結果
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
NSLog(@"1");
[self performSelector:@selector(test) withObject:nil afterDelay:0];
NSLog(@"3");
// [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
//[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
});
- (void)test{
NSLog(@"2:%s", __fun__);
}
注釋后:
打印結果是:1囱挑、3
原因
performSelector:withObject:afterDelay:的本質是往Runloop中添加定時器
子線程默認沒有啟動Runloop
打開注釋:1 3 2
- (void)interview{
//performSelector 會崩潰
//因為 thread 開啟后執(zhí)行完會被釋放,在執(zhí)行方法會崩潰
NSThread * thread = [[NSThread alloc] initWithBlock:{{
NSLog(@"1");
}];
[thread start];
[self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:YES];
}
49.使用CADisplayLink沼溜、NSTimer有什么注意點平挑?
1.CADisplayLink、NSTimer會對target產生強引用系草,如果target又對它們產生強引用通熄,那么就會引發(fā)循環(huán)引用
2.不準時,CADisplayLink和Timer都需要依賴RunLoop找都,如果RunLoop的任務過于繁重唇辨,那么就會造成不準時,所以想準時的話能耻,我們應該使用GCD的定時器
循環(huán)引用解決方案:
50.介紹下內存的幾大區(qū)域
代碼段:編譯之后的代碼
數據段:
字符串常量:比如NSString *str = @"123"
已初始化數據:已初始化的全局變量赏枚、靜態(tài)變量等
未初始化數據:未初始化的全局變量亡驰、靜態(tài)變量等
棧:函數調用開銷,比如局部變量饿幅。分配的內存空間地址越來越小
堆:通過alloc凡辱、malloc、calloc等動態(tài)分配的空間栗恩,分配的內存空間地址越來越大
51.講一下你對 iOS 內存管理的理解
在iOS中透乾,使用引用計數來管理OC對象的內存
一個新創(chuàng)建的OC對象引用計數默認是1,當引用計數減為0摄凡,OC對象就會銷毀续徽,釋放其占用的內存空間
調用retain會讓OC對象的引用計數+1,調用release會讓OC對象的引用計數-1
內存管理的經驗總結
當調用alloc亲澡、new钦扭、copy、mutableCopy方法返回了一個對象床绪,在不需要這個對象時客情,要調用release或者autorelease來釋放它
想擁有某個對象,就讓它的引用計數+1癞己;不想再擁有某個對象膀斋,就讓它的引用計數-1
可以通過以下私有函數來查看自動釋放池的情況
extern void _objc_autoreleasePoolPrint(void);
52.ARC 都幫我們做了什么?
ARC是LLVM編譯器和RunLoop相互協(xié)作的一個結果痹雅,幫我們自動進行內存管理(如下所示)仰担,并且處理了弱引用(對象銷毀時,指向這個對象的弱引用绩社,都會被置為nil)
1.使用assign修飾普通數據類型時摔蓝,ARC會幫我們自動生成get、set方法愉耙,如下所示
@property (nonatomic,assign) NSInteger age;
- (void)setAge:(NSInteger)age{
_age = age;
}
- (NSInteger)age{
return _age;
}
2.使用retain贮尉、strong修飾對象類型,ARC會幫我們自動生成get朴沿、set方法猜谚,并且在set方法里幫我們retain、release對象赌渣,如下所示:
@property (nonatomic,retain) NSObject *status;
- (void)setStatus:(NSObject *)status{
if (_status != status){
//如果新對象和舊對象不相同名段,就先讓舊對象的引用計數-1
[_status release];
//然后把新對象的引用計數+1跷叉,在賦值給_status
_status = [status retain];
}
}
- (NSObject *)status{
return _status;
}
3.使用copy修飾對象類型,ARC會幫我們自動生成get、set方法警没,并且在set方法里幫我們copy妆丘、release對象鲜漩,如下所示:
@property (nonatomic,copy) NSString *name;
- (void)setName:(NSString *)name{
if (_name != name){
//如果新對象和舊對象不相同,就先讓舊對象的引用計數-1
[_name release];
//然后把新對象的引用計數+1疾渴,在賦值給_status
此處會使用copy產生一個不可變的對象,這就是為什么NSMutableArray等可變類型不能使用copy的根本原因M驼獭8惆印!
_name = [name copy];
}
}
- (NSString *)name{
return _name;
}
53.weak指針的實現原理
當一個對象obj被weak指針指向時魁袜,這個weak指針會以obj作為key桩撮,被存儲到sideTable類的weak_table這個散列表上對應的一個weak指針數組里面。
當一個對象obj的dealloc方法被調用時峰弹,Runtime會以obj為key店量,從sideTable的weak_table散列表中,找出對應的weak指針列表鞠呈,然后將里面的weak指針逐個置為nil融师。
54.autorelease對象在什么時機會被調用release
在所處的runloop的休眠之前釋放
自動釋放池的主要底層數據結構是:__AtAutoreleasePool、AutoreleasePoolPage
調用了autorelease的對象最終都是通過AutoreleasePoolPage對象來管理的
每個AutoreleasePoolPage對象占用4096字節(jié)內存蚁吝,除了用來存放它內部的成員變量旱爆,剩下的空間用來存放autorelease對象的地址
所有的AutoreleasePoolPage對象通過雙向鏈表的形式連接在一起
55.Runloop和Autorelease
iOS在主線程的Runloop中注冊了2個Observer
第1個Observer監(jiān)聽了kCFRunLoopEntry事件,會調用objc_autoreleasePoolPush()
第2個Observer
監(jiān)聽了kCFRunLoopBeforeWaiting事件窘茁,會調用objc_autoreleasePoolPop()怀伦、objc_autoreleasePoolPush()
監(jiān)聽了kCFRunLoopBeforeExit事件,會調用objc_autoreleasePoolPop()
56.方法里有局部對象山林, 出了方法后會立即釋放嗎
如果是普通的 局部對象 會立即釋放
如果是放在了 autoreleasePool 自動釋放池,在 runloop 迭代結束的時候釋放
57.Tagged Pointer
1.從64bit開始房待,iOS引入了Tagged Pointer技術,用于優(yōu)化NSNumber驼抹、NSDate桑孩、NSString等小對象的存儲
2.在沒有使用Tagged Pointer之前, NSNumber等對象需要動態(tài)分配內存砂蔽、維護引用計數等洼怔,NSNumber指針存儲的是堆中NSNumber對象的地址值
3.使用Tagged Pointer之后署惯,NSNumber指針里面存儲的數據變成了:Tag + Data左驾,也就是將數據直接存儲在了指針中
4.當指針不夠存儲數據時,才會使用動態(tài)分配內存的方式來存儲數據
objc_msgSend能識別Tagged Pointer极谊,比如NSNumber的intValue方法诡右,直接從指針提取數據,節(jié)省了以前的調用開銷
5.如何判斷一個指針是否為Tagged Pointer轻猖?
iOS平臺帆吻,最高有效位是1(第64bit)
Mac平臺,最低有效位是1
58.你在項目中是怎么優(yōu)化內存的咙边?
- 自動釋放池
使用@autoreleasepool可以幫助管理大量對象創(chuàng)建的場景猜煮,特別是在循環(huán)中次员。
for i in 0..<1000 {
@autoreleasepool {
let image = UIImage(named: "image_\(i)")
// 處理圖片
}
}
2.避免循環(huán)飲用
使用弱引用 (weak) 或未持有(unowned) 來避免循環(huán)引用,尤其是在閉包中王带。
class MyClass {
var closure: (() -> Void)?
init() {
closure = { [weak self] in
self?.doSomething()
}
}
func doSomething() {
// 需要執(zhí)行的操作
}
}
3.按需加載資源
使用延遲加載(lazy loading) 和懶加載 (lazy properties)或者重用機制 來按需加載資源淑蔚。
4.圖像處理
緩存圖片:對于頻繁使用的圖片,使用緩存機制愕撰,如 NSCache刹衫。
降低圖片分辨率:對于不需要高分辨率的圖片,可以在加載圖片時降低分辨率搞挣。
使用合適的圖片格式:例如带迟,對于簡單圖形和顏色塊使用 PNG,對于更復雜的圖像使用 JPEG 壓縮囱桨。
// 使用 NSCache 緩存圖片
let imageCache = NSCache<NSString, UIImage>()
func loadImage(named: String) -> UIImage? {
if let cachedImage = imageCache.object(forKey: named as NSString) {
return cachedImage
}
if let image = UIImage(named: named) {
imageCache.setObject(image, forKey: named as NSString)
return image
}
return nil
}
5.數據模型優(yōu)化
減少不必要的對象創(chuàng)建:只在需要時創(chuàng)建新的對象仓犬。
使用合適的數據結構:選擇合適的數據結構來優(yōu)化內存使用和性能。
6.監(jiān)控內存
使用 Xcode 的 Instruments 工具來監(jiān)控和分析內存使用情況蝇摸,找出和修復內存泄漏和高內存使用的問題婶肩。
7.釋放內存
在適當時候釋放對象:如當視圖控制器消失時,釋放不再需要的資源貌夕。
使用 notifications 來釋放資源:在收到內存警告通知時律歼,釋放可能被占用的大量內存的資源。
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// 釋放未使用的資源
}
8.減少內存消耗的空件
盡量減少大型控件的使用啡专,替代方案可以是自定義控件险毁,優(yōu)化布局,提高效率们童。
9.異步處理
將大量資源加載畔况、數據處理放在后臺線程,避免阻塞主線程慧库,從而提高應用響應速度跷跪。
DispatchQueue.global(qos: .background).async {
// 執(zhí)行后臺任務
let image = UIImage(contentsOfFile: "/path/to/image")
DispatchQueue.main.async {
// 更新 UI
}
}
59.OC與Swift的區(qū)別
1.swift是靜態(tài)語言,有類型推斷齐板,OC是動態(tài)語言吵瞻。
2.swift面向協(xié)議編程,OC面向對象編程
3.swift注重值類型甘磨,OC注重引用類型橡羞。
4.swift支持泛型,OC只支持輕量泛型
5.swift支持靜態(tài)派發(fā)(效率高)济舆、動態(tài)派發(fā)(函數表派發(fā)卿泽、消息派發(fā))方式,OC支持動態(tài)派發(fā)(消息派發(fā))方式滋觉。
6.swift支持函數式編程
7.swift的協(xié)議不僅可以被類實現签夭,也可以被struct和enum實現
8.swift有元組類型齐邦、支持運算符重載
9.swift支持命名空間
10.swift支持默認參數
11.swift比oc代碼更加簡潔
60.內存對齊規(guī)則(IOS 64位環(huán)境下)
1.Founction 框架對象最小要求內存占用16
2.結構體內存對齊原則:內存必須是最大成員大小的倍數
3.IOS 操作系統(tǒng)內存對齊原則: 內存是以16的倍數對齊
4.64位系統(tǒng)下數據類型占用字節(jié)數
char 1
char * 8
int 4
Short 2
BOOL 1
unsigned int 4
NSInterger 8
long 8
long long 8
float 4
double 8
CGFloat 8
CGSize 16
CGRect 32
61.堆、棧第租、隊列
棧(stack):又名堆棧侄旬,存放的是:在函數中定義的一些基本數據類型的變量和對象的引用;當超過作用域后釋放
棧(heap):存儲對象煌妈,所有new出來的對象和數組
堆(二叉樹):從根節(jié)點開始儡羔,每個節(jié)點都可以有左、右兩個節(jié)點璧诵,元素優(yōu)先將每一層按從左到右的方式填滿汰蜘,即父節(jié)點最多有2個子節(jié)點,有右子節(jié)點就一定有左子節(jié)點之宿,同一層的某個節(jié)點左邊一定是填滿的族操。堆分為最大堆,最小堆比被,最小堆就是根節(jié)點元素值是所有元素中最小的色难,最大堆則相反,左右節(jié)點的大小則沒有規(guī)定誰大誰小等缀。
堆和棧的區(qū)別:
1枷莉、堆可以動態(tài)的分配內存大小(由程序員分配釋放尺迂,如果程序員不釋放笤妙,程序結束時可能有OS釋放,分配方式類似于鏈表)噪裕,生存周期不需要事先告訴編譯器蹲盘,程序運行時申請;棧通常比堆小膳音,棧中存放的數據的大小和生存周期都必須是確定的(由編譯器自動分配釋放)
2召衔、棧的存儲速度比堆快,棧數據可共享
3祭陷、棧使用的是一級緩存苍凛,通常是被調用時處于存儲空間中,調用完畢后立即釋放颗胡;堆則是存放在二級緩存中毫深,生命周期由虛擬機的垃圾回收算法決定
4吩坝、棧是運行時單位毒姨,堆是存儲的單位,棧解決程序運行時的問題钉寝,即程序如何執(zhí)行弧呐,或者說如何處理數據闸迷,堆解決的是數據的存儲問題,即數據怎么放俘枫?放在那腥沽?
62.UIView和CALayer是什么關系?
創(chuàng)建UIView對象時,UIView內部會自動創(chuàng)建一個層(CALayer對象)鸠蚪,通過UIView的layer屬性可以訪問這個層今阳。當UIView需要顯示到屏幕上時,會調用drawRect:方法進行繪圖渲染茅信,并且會將所有內容繪制在自己的層上盾舌,繪圖完畢后,系統(tǒng)會將層拷貝到屏幕上蘸鲸,于是就完成了UIView的顯示
UIView相比CALayer最大區(qū)別是UIView繼承自UIResponder妖谴,可以響應用戶事件,而CALayer不可以酌摇;UIView側重于對顯示內容的管理膝舅,CALayer側重于對內容的繪制。
UIView本身窑多,更像是一個CALayer的管理器仍稀,訪問它的和繪圖、坐標相關的屬性埂息,如frame琳轿,bounds等,實際上內部都是訪問它所在CALayer的相關屬性
UIView和CALayer是相互依賴的關系耿芹。UIView依賴CALayer提供的內容崭篡,CALayer依賴UIView提供的容器來顯示繪制的內容。歸根到底CALayer是這一切的基礎吧秕,如果沒有CALayer琉闪,UIView自身也不會存在,UIView是一個特殊的CALayer實現砸彬,添加了響應事件的能力颠毙。
63.說一下 JS 和 OC 互相調用的幾種方式?
js調用oc的三種方式:
根據網頁重定向截取字符串通過url scheme判斷
替換方法.context[@"copyText"]
注入對象:遵守協(xié)議NSExport,設置context[@
oc調用js代碼兩種方式
通過webVIew調用 webView stringByEvaluatingJavaScriptFromString: 調用
通過JSContext調用[context evaluateScript:];
64.Http 和 Https 的區(qū)別砂碉?Https為什么更加安全蛀蜜?
區(qū)別
1.HTTPS 需要向機構申請 CA 證書,極少免費增蹭。
2.HTTP 屬于明文傳輸滴某,HTTPS基于 SSL 進行加密傳輸。
3.HTTP 端口號為 80,HTTPS 端口號為 443 霎奢。
4.HTTPS 是加密傳輸户誓,有身份驗證的環(huán)節(jié),更加安全幕侠。
安全
SSL(安全套接層) TLS(傳輸層安全)
以上兩者在傳輸層之上帝美,對網絡連接進行加密處理,保障數據的完整性晤硕,更加的安全悼潭。
65.編程中的六大設計原則?
1.單一職責原則
通俗地講就是一個人只做一件事
CALayer:動畫和視圖的顯示舞箍。
UIView:只負責事件傳遞女责、事件響應。
2.開閉原則
對修改關閉创译,對擴展開放抵知。 要考慮到后續(xù)的擴展性,而不是在原有的基礎上來回修改
3.接口隔離原則
使用多個專門的協(xié)議软族、而不是一個龐大臃腫的協(xié)議刷喜,如 UITableviewDelegate + UITableViewDataSource
4.依賴倒置原則
抽象不應該依賴于具體實現、具體實現可以依賴于抽象立砸。 調用接口感覺不到內部是如何操作的
5.里氏替換原則
父類可以被子類無縫替換掖疮,且原有的功能不受任何影響 如:KVO
6.迪米特法則
一個對象應當對其他對象盡可能少的了解,實現高聚合颗祝、低耦合
66.沙盒目錄結構是怎樣的浊闪?各自用于哪些場景?
Application:存放程序源文件螺戳,上架前經過數字簽名搁宾,上架后不可修改
Documents:常用目錄,iCloud備份目錄倔幼,存放數據
tmp:存放臨時文件盖腿,不會被備份,而且這個文件下的數據有可能隨時被清除的可能
Library包含以下目錄:
Caches:存放體積大又不需要備份的數據
Preference:設置目錄损同,iCloud會備份設置信息
Application Support:建議用來存儲除用戶數據相關以外的所有文件翩腐,如游戲的新關卡。在iTunes和iCloud備份時會備份該目錄膏燃。
Frameworks:用來保存動態(tài)庫的文件夾茂卦,在iOS系統(tǒng)中已不能使用,該目錄可以忽略组哩。
67.AFNetworking 底層原理分析
AFNetworking 主要由以下核心組件構成:
AFURLSessionManager
AFHTTPRequestSerializer
AFHTTPResponseSerializer
AFSecurityPolicy
AFNetworkReachabilityManager
AFNetworking是封裝的NSURLSession的網絡請求等龙,由五個模塊組成:分別由NSURLSession,Security,Reachability,Serialization,UIKit五部分組成
NSURLSession:網絡通信模塊(核心模塊) 對應 AFNetworking中的 AFURLSessionManager和對HTTP協(xié)議進行特化處理的AFHTTPSessionManager,AFHTTPSessionManager是繼承于AFURLSessionmanager的
Security:網絡通訊安全策略模塊 對應 AFSecurityPolicy
Reachability:網絡狀態(tài)監(jiān)聽模塊 對應AFNetworkReachabilityManager
Seriaalization:網絡通信信息序列化处渣、反序列化模塊 對應 AFURLResponseSerialization
UIKit:對于iOS UIKit的擴展庫
68.說一下工作中你怎么做性能優(yōu)化的
一般都是說關于tableView的優(yōu)化處理,
造成tableView卡頓的原因
1.沒有使用cell的重用標識符而咆,導致一直創(chuàng)建新的cell
2.cell的重新布局
3.沒有提前計算并緩存cell的屬性及內容
4.cell中控件的數量過多
5.使用了ClearColor,無背景色幕袱,透明度為0
6.更新只使用tableView.reloadData()(如果只是更新某組的話暴备,使用reloadSection進行局部更新)
7.加載網絡數據,下載圖片们豌,沒有使用異步加載涯捻,并緩存
8.使用addView 給cell動態(tài)添加view
9.沒有按需加載cell(cell滾動很快時,只加載范圍內的cell)
10.實現無用的代理方法(tableView只遵守兩個協(xié)議)
11.沒有做緩存行高(estimatedHeightForRow不能和HeightForRow里面的layoutIfNeed同時存在望迎,這兩者同時存在才會出現“竄動”的bug障癌。
建議是:只要是固定行高就寫預估行高來減少行高調用次數提升性能。如果是動態(tài)行高就不要寫預估方法了辩尊,用一個行高的緩存字典來減少代碼的調用次數即可)
12.做了多余的繪制工作(在實現drawRect:的時候涛浙,它的rect參數就是需要繪制的區(qū)域,這個區(qū)域之外的不需要進行繪制)
13.沒有預渲染圖像摄欲。(當新的圖像出現時轿亮,仍然會有短暫的停頓現象。解決的辦法就是在bitmap context里先將其畫一遍胸墙,導出成UIImage對象我注,然后再繪制到屏幕)
提升tableView的流暢度
*本質上是降低 CPU、GPU 的工作迟隅,從這兩個大的方面去提升性能但骨。
1.CPU:對象的創(chuàng)建和銷毀、對象屬性的調整智袭、布局計算奔缠、文本的計算和排版、圖片的格式轉換和解碼吼野、圖像的繪制
2.GPU:紋理的渲染
卡頓優(yōu)化在 CPU 層面
1.盡量用輕量級的對象添坊,比如用不到事件處理的地方,可以考慮使用 CALayer 取代 UIView
2.不要頻繁地調用 UIView 的相關屬性箫锤,比如 frame贬蛙、bounds、transform 等屬性谚攒,盡量減少不必要的修改
3.盡量提前計算好布局阳准,在有需要時一次性調整對應的屬性,不要多次修改屬性
4.Autolayout 會比直接設置 frame 消耗更多的 CPU 資源
5.圖片的 size 最好剛好跟 UIImageView 的 size 保持一致
6.控制一下線程的最大并發(fā)數量
7.盡量把耗時的操作放到子線程
8.文本處理(尺寸計算馏臭、繪制)
9.圖片處理(解碼野蝇、繪制)
卡頓優(yōu)化在 GPU層面
1.盡量避免短時間內大量圖片的顯示讼稚,盡可能將多張圖片合成一張進行顯示
2.GPU能處理的最大紋理尺寸是 4096x4096,一旦超過這個尺寸绕沈,就會占用 CPU 資源進行處理锐想,所以紋理盡量不要超過這個尺寸
3.盡量減少視圖數量和層次
4.減少透明的視圖(alpha<1),不透明的就設置 opaque 為 YES
5.盡量避免出現離屏渲染
69.iOS圖片設置圓角性能問題
1.直接使用setCornerRadius
這樣設置會觸發(fā)離屏渲染乍狐,比較消耗性能赠摇。比如當一個頁面上有十幾頭像這樣設置了圓角會明顯感覺到卡頓。
注意:png圖片UIImageView處理圓角是不會產生離屏渲染的浅蚪。(ios9.0之后不會離屏渲染藕帜,ios9.0之前還是會離屏渲染)
2.setCornerRadius設置圓角之后,shouldRasterize=YES光柵化
avatarImageView.layer.shouldRasterize = YES;
avatarImageViewUrl.layer.rasterizationScale=[UIScreen mainScreen].scale; //UIImageView不加這句會產生一點模糊
shouldRasterize=YES設置光柵化惜傲,可以使離屏渲染的結果緩存到內存中存為位圖洽故,
使用的時候直接使用緩存,節(jié)省了一直離屏渲染損耗的性能盗誊。
但是如果layer及sublayers常常改變的話时甚,它就會一直不停的渲染及刪除緩存重新
創(chuàng)建緩存,所以這種情況下建議不要使用光柵化哈踱,這樣也是比較損耗性能的撞秋。
3.直接覆蓋一張中間為圓形透明的圖片(推薦使用)
4.UIImage drawInRect繪制圓角
這種方式GPU損耗低內存占用大,而且UIButton上不知道怎么繪制嚣鄙,可以用
UIimageView添加個點擊手勢當做UIButton使用吻贿。
5.SDWebImage處理圖片時Core Graphics繪制圓角(暫時感覺是最優(yōu)方法)
70.以scheduledTimerWithTimeInterval的方式觸發(fā)的timer,在滑動頁面上的列表時哑子,timer會暫停舅列,為什么?該如何解決卧蜓?
原因在于滑動時當前線程的runloop切換了mode用于列表滑動帐要,導致timer暫停。
runloop中的mode主要用來指定事件在runloop中的優(yōu)先級弥奸,有以下幾種:
- Default(NSDefaultRunLoopMode):默認榨惠,一般情況下使用;
- Connection(NSConnectionReplyMode):一般系統(tǒng)用來處理NSConnection相關事件盛霎,開發(fā)者一般用不到赠橙;
- Modal(NSModalPanelRunLoopMode):處理modal panels事件空民;
- Event Tracking(NSEventTrackingRunLoopMode):用于處理拖拽和用戶交互的模式见剩。
- Common(NSRunloopCommonModes):模式合集。默認包括Default晦款,Modal规个,Event >Tracking三大模式凤薛,可以處理幾乎所有事件姓建。
回到題中的情境$蜕唬滑動列表時速兔,runloop的mode由原來的Default模式切換到了Event Tracking模式,timer原來好好的運行在Default模式中活玲,被關閉后自然就停止工作了涣狗。
解決方法其一是將timer加入到NSRunloopCommonModes中。其二是將timer放到另一個線程中翼虫,然后開啟另一個線程的runloop屑柔,這樣可以保證與主線程互不干擾屡萤,而現在主線程正在處理頁面滑動珍剑。
//方法1
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
//方法2
dispatch_async(dispatch_get_global_queue(0, 0), ^{
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(repeat:) userInfo:nil repeats:true];
[[NSRunLoop currentRunLoop] run];
});
71.對稱加密和非對稱加密的區(qū)別?
1死陆、對稱加密又稱公開密鑰加密招拙,加密和解密都會用到同一個密鑰,如果密鑰被攻擊者獲得措译,此時加密就失去了意義别凤。常見的對稱加密算法有DES、3DES领虹、AES规哪、Blowfish、IDEA塌衰、RC5诉稍、RC6。
2最疆、非對稱加密又稱共享密鑰加密杯巨,使用一對非對稱的密鑰,一把叫做私有密鑰努酸,另一把叫做公有密鑰服爷;公鑰加密只能用私鑰來解密,私鑰加密只能用公鑰來解密获诈。常見的公鑰加密算法有:RSA仍源、ElGamal、背包算法舔涎、Rabin(RSA的特例)镜会、迪菲-赫爾曼密鑰交換協(xié)議中的公鑰加密算法、橢圓曲線加密算法)终抽。
72.組件化有什么好處戳表?
業(yè)務分層桶至、解耦,使代碼變得可維護匾旭;
有效的拆分镣屹、組織日益龐大的工程代碼,使工程目錄變得可維護价涝;
便于各業(yè)務功能拆分女蜈、抽離,實現真正的功能復用色瘩;
業(yè)務隔離伪窖,跨團隊開發(fā)代碼控制和版本風險控制的實現;
模塊化對代碼的封裝性居兆、合理性都有一定的要求覆山,提升開發(fā)同學的設計能力;
在維護好各級組件的情況下泥栖,隨意組合滿足不同客戶需求簇宽;(只需要將之前的多個業(yè)務組件模塊在新的主App中進行組裝即可快速迭代出下一個全新App)
73.你是如何組件化解耦的?
分層
基礎功能組件:按功能分庫吧享,不涉及產品業(yè)務需求魏割,跟庫Library類似,通過良好的接口拱上層業(yè)務組件調用钢颂;不寫入產品定制邏輯钞它,通過擴展接口完成定制;
基礎UI組件:各個業(yè)務模塊依賴使用殊鞭,但需要保持好定制擴展的設計
業(yè)務組件:業(yè)務功能間相對獨立遭垛,相互間沒有Model共享的依賴;業(yè)務之間的頁面調用只能通過UIBus進行跳轉钱豁;業(yè)務之間的邏輯Action調用只能通過服務提供耻卡;
中間件:target-action,url-block牲尺,protocol-class
74.IOS 支持多繼承嗎卵酪?如何實現
Objective-C 不支持多繼承,但我們可以間接的實現(協(xié)議谤碳、分類溃卡、消息轉發(fā))
具體實現可參考http://www.reibang.com/p/020f8541f77b
75.APP啟動時間應從哪些方面優(yōu)化?
App啟動時間可以通過xcode提供的工具來度量蜒简,在Xcode的Product->Scheme-->Edit Scheme->Run->Auguments中瘸羡,將環(huán)境變量DYLD_PRINT_STATISTICS設為YES,優(yōu)化需以下方面入手
dylib loading time
核心思想是減少dylibs的引用
合并現有的dylibs(最好是6個以內)
使用靜態(tài)庫
rebase/binding time
核心思想是減少DATA塊內的指針
減少Object C元數據量搓茬,減少Objc類數量犹赖,減少實例變量和函數(與面向對象設計思想沖突)
減少c++虛函數
多使用Swift結構體(推薦使用swift)
ObjC setup time
核心思想同上队他,這部分內容基本上在上一階段優(yōu)化過后就不會太過耗時
initializer time
使用initialize替代load方法
減少使用c/c++的attribute((constructor));推薦使用dispatch_once() pthread_once() std:once()等方法
推薦使用swift
不要在初始化中調用dlopen()方法峻村,因為加載過程是單線程麸折,無鎖,如果調用dlopen則會變成多線程粘昨,會開啟鎖的消耗垢啼,同時有可能死鎖
不要在初始化中創(chuàng)建線程
75.imageName和ImageWithContextOfFile的區(qū)別?哪個性能高
用imageNamed的方式加載時张肾,圖片使用完畢后緩存到內存中芭析,內存消耗多,加載速度快吞瞪。即使生成的對象被 autoReleasePool釋放了馁启,這份緩存也不釋放,如果圖像比較大尸饺,或者圖像比較多进统,用這種方式會消耗很大的內存助币。
imageNamed采用了緩存機制浪听,如果緩存中已加載了圖片,直接從緩存讀就行了眉菱,每次就不用再去讀文件了迹栓,效率會更高。
ImageWithContextOfFile加載俭缓,圖片是不會緩存的克伊,加載速度慢。
大量使用imageNamed方式會在不需要緩存的地方額外增加開銷CPU的時間.當應用程序需要加載一張比較大的圖片并且使用一次性华坦,那么其實是沒有必要去緩存這個圖片的愿吹,用imageWithContentsOfFile是最為經濟的方式,這樣不會因為UIImage元素較多情況下,CPU會被逐個分散在不必要緩存上浪費過多時間.
76.Category 與 Extension本質區(qū)別
1)Extension在編譯期間就已經決定了惜姐,它就是類的一部分犁跪,在編譯器和頭文件里的@interface以及實現文件里的@implement一起形成一個完整的類,它伴隨類的產生而產生歹袁,亦隨之一起消滅坷衍。extension一般用來隱藏類的私有信息,你必須有一個類的源碼才能為一個類添加extension条舔,所以無法為系統(tǒng)的類枫耳,比如NSString添加extension。
2)Category則是在運行期間決定的孟抗。extension可以添加實例變量迁杨,而category是無法添加實例變量的(因為在運行期間钻心,對象的內存布局已經確定,如果添加實例變量就會破壞類的內部布局铅协,這對于編譯型語言來說是災難性的)扔役。
77.三種架構模式——MVC、MVP警医、MVVM
1.MVC(Model-View-Controller)
Model:模型層亿胸,數據模型及其業(yè)務邏輯,是針對業(yè)務模型建立的數據結構预皇,Model與View無關侈玄,而與業(yè)務有關。
View:視圖層吟温,用于與用戶實現交互的頁面序仙,通常實現數據的輸入和輸出功能。
Controller:控制器鲁豪,用于連接Model層和View層潘悼,完成Model層和View層的交互。還可以處理頁面業(yè)務邏輯爬橡,它接收并處理來自用戶的請求治唤,并將Model返回給用戶。
2.MVP(Model-View-Presenter)
Model:模型層糙申,用于數據存儲以及業(yè)務邏輯宾添。
View:視圖層,用于展示與用戶實現交互的頁面柜裸,通常實現數據的輸入和輸出功能缕陕。
Presenter:表示器,用于連接M層疙挺、V層扛邑,完成Model層與View層的交互,還可以進行業(yè)務邏輯的處理铐然。
3.MVVM(Model-View-ViewModel)
Model:數據模型(數據處理業(yè)務)蔬崩,指的是后端傳遞的數據。
View:視圖锦爵,將Model的數據以某種方式展示出來舱殿。
ViewModel:視圖模型,數據的雙向綁定(當Model中的數據發(fā)生改變時View就感知到险掀,當View中的數據發(fā)生變化時Model也能感知到)沪袭,是MVVM模式的核心。ViewModel 層把 Model 層和 View 層的數據同步自動化了,解決了 MVP 框架中數據同步比較麻煩的問題冈绊,不僅減輕了 ViewModel 層的壓力侠鳄,同時使得數據處理更加方便——只需告訴 View 曾展示的數據是 Model 層中的那一部分即可。
4.Viper(View死宣、Interactor伟恶、Presenter、Entity毅该、Route)
V - ViewView,組織對內部的布局博秫,對外暴露用戶更新UI的接口,自己不主動更新UI眶掌。
I - Interactor挡育,實現和封裝各種業(yè)務的Use Case,可以獲取各種Manager或者Service用于實現業(yè)務邏輯朴爬。
P - Presenter即寒,調用Interactor提供的UseCase執(zhí)行業(yè)務邏輯。Presenter作為業(yè)務和View的中轉站召噩,不包含業(yè)務實現代碼母赵。而是調用各種Use Case。
E - Entity具滴,這里是業(yè)務的Model層凹嘲,是業(yè)務邏輯的實體。
R - Router抵蚊,路由工具施绎,用于頁面之間的跳轉溯革,實現頁面之間的解耦贞绳。
78.iOS中常用的幾種設計模式
1.代理模式
代理模式完成委托方交給的任務,委托方有一些任務自己不想完成,但是還需要要實現,則將該任務存放到協(xié)議中,由代理完成.但是代理并不會主動的執(zhí)行任務,需要委托方通知代理。
應用場景:當一個類的某些功能需要由別的類來實現致稀,但是又不確定具體會是哪個類實現冈闭。
優(yōu)勢:解耦合
敏捷原則:開放-封閉原則
實例:tableview的 數據源delegate,通過和protocol的配合抖单,完成委托訴求萎攒。列表row個數delegate,自定義的delegate矛绘。
2.觀察者模式(通知機制,KVO機制)
觀察者模式本質上是一種發(fā)布-訂閱模型,用以消除具有不同行為的對象之間的耦合耍休,通過這一模式,不同對象可以協(xié)同工作货矮,同時它們也可以被復用于其他地方Observer從Subject訂閱通知羊精,ConcreteObserver實現重現ObServer并將其重載其update方法。
應用場景:一般為model層對囚玫,controller和view進行的通知方式喧锦,不關心誰去接收读规,只負責發(fā)布信息。
優(yōu)勢:解耦合
敏捷原則:接口隔離原則燃少,開放-封閉原則
實例:Notification通知中心束亏,注冊通知中心,任何位置可以發(fā)送消息阵具,注冊觀察者的對象可以接收碍遍。
3.單例模式
單例模式可以保證App在程序運行中,一個類只有唯一個實例阳液,從而做到節(jié)約內存雀久。
在整個App程序中,這一份資源是共享的趁舀。
提供一個固定的實例創(chuàng)建方法赖捌。
應用場景:確保程序運行期某個類,只有一份實例矮烹,用于進行資源共享控制越庇。
優(yōu)勢:使用簡單,延時求值奉狈,易于跨模塊
敏捷原則:單一職責原則
實例:[UIApplication sharedApplication]卤唉。
4.策略模式
策略模式定義了一系列的算法,并將每一個算法封裝起來仁期,而且使它們還可以相互替換桑驱。策略模式讓算法獨立于使用它的客戶而獨立變化。
應用場景:定義算法族跛蛋,封裝起來熬的,使他們之間可以相互替換。
優(yōu)勢:使算法的變化獨立于使用算法的用戶
敏捷原則:接口隔離原則赊级;
多用組合押框,少用繼承;
針對接口編程理逊,而非實現
實例:排序算法橡伞,NSArray的sortedArrayUsingSelector;經典的鴨子會叫晋被,會飛案例兑徘。
79.IOS內存管理
Objective-C提供了兩種種內存管理方式:manual reference counting(MRC,手動引用計數器)羡洛,automatic reference counting(ARC挂脑,自動引用計數)。ARC作為蘋果新提供的技術,蘋果推薦開發(fā)者使用ARC技術來管理內存最域;
1.引用計數器
采用引用計數進行管理
每個對象都有一個關聯的整數谴分,稱為引用計數器
當代碼需要使用該對象時,則將對象的引用計數加1
當代碼結束使用該對象時镀脂,則將對象的引用計數減1
當引用計數的值變?yōu)?時牺蹄,此時對象將被釋放。
與之對應的消息發(fā)送方法
當對象被創(chuàng)建(alloc薄翅、new或copy等方法)時沙兰,其引用計數初始值為1
給對象發(fā)送retain消息,其引用計數加
給對象發(fā)送release消息翘魄,其引用計數減1
當對象引用計數歸0時鼎天,ObjC給對象發(fā)送dealloc消息銷毀對象
2.自動釋放池
AutoreleasePool的原理
自動釋放池,系統(tǒng)有一個現成的自動內存管理池暑竟,他會隨著每一個mainRunloop的結束而釋放其中的對象斋射;自動釋放池也可以手動創(chuàng)建,他可以讓pool中的對象在執(zhí)行完代碼后馬上被釋放但荤,可以起到優(yōu)化內存罗岖,防止內存溢出的效果(如視頻針圖片的切換時、創(chuàng)建大量臨時對象時等)腹躁。
autorelease:自動釋放桑包,使對象在超出指定的生存范圍時能夠自動并正確地釋放 (release 即是立即釋放)。
參考以下鏈接
https://blog.csdn.net/z119901214/article/details/82417153
80.iOS性能優(yōu)化之內存篇
https://blog.csdn.net/hanhailong18/article/details/111350050
http://events.jianshu.io/p/235eae2e820c
81.事件響應鏈
用戶點擊屏幕時纺非,首先UIApplication對象先收到該點擊事件哑了,再依次傳遞給它上面的所有子view,直到傳遞到最上層烧颖,即UIApplication ——> UIWindow ——> RootViewController ——> View ——> Button弱左,即傳遞鏈。
而反之Button ——> View ——> RootViewController ——> UIWindow ——> UIApplication則稱為響應鏈倒信。
簡單總結科贬,事件鏈包含傳遞鏈和響應鏈,事件通過傳遞鏈傳遞上去鳖悠,通過響應鏈找到相應的UIResponse。
響應鏈的具體流程:
1优妙、若view的 viewcontroller 存在乘综,則將該事件傳遞給其viewcontroller響應;如若不存在套硼,則傳遞給其父視圖卡辰;
2、若view的最頂層不能處理事件,則傳遞給UIWindow進行處理九妈;
3反砌、若UIWindow不能處理,則傳遞給UIApplication萌朱;
4宴树、若UIApplication不能處理,則將該事件丟棄晶疼。
傳遞鏈的具體流程:
1酒贬、用戶在點擊屏幕。
2翠霍、系統(tǒng)將點擊事件加入到UIApplication管理的消息隊列中锭吨。
3、UIApplication會從消息隊列中取出該事件傳遞給UIWindow對象寒匙;
4零如、在UIWindow中調用方法hitTest:withEvent:返回最終相應的view;
82.bugly的卡頓監(jiān)控原理
Runloop的兩次source[kCFRunLoopBeforeSources 和 kCFRunLoopAfterWaiting]的監(jiān)控 創(chuàng)建信號量的方式
渲染界面的頻率來監(jiān)控幀率
http://www.reibang.com/p/2c7b13bfd82f
83.組件化解耦方案
http://www.reibang.com/p/1274062f38c5
84.xcode Instrument工具
http://www.reibang.com/p/6e3530cd375a
https://blog.csdn.net/m0_46110288/article/details/115698448
85.iOS中內省的幾個方法锄弱?class方法和objc_getClass方法有什么區(qū)別
在iOS和Objective-C編程中埠况,內省(introspection)是指在運行時查詢和操作對象的類型和屬性的能力棵癣。下面介紹幾種在iOS中常用的內省方法辕翰,以及 class 方法和 objc_getClass 函數之間的區(qū)別。
- isKindOfClass // 方法用于檢查對象是否是某個類或其子類的實例
- isMemberOfClass // 用于檢查對象是否是某個類的直接實例狈谊,而不是其子類的實例
- respondsToSelector // 用于檢查對象是否能響應某個特定的消息(方法)
- conformsToProtocol // 用于檢查對象是否實現了指定的協(xié)議
class 方法是每個實例方法的默認實現喜命,用于返回對象的類。它是Objective-C中的一部分河劝,無需導入任何額外的頭文件壁榕。
objc_getClass 是Objective-C運行時庫中的一個函數,用于根據類名字符串查找和返回一個類赎瞎。你需要導入 #import <objc/runtime.h> 才能使用它牌里。該函數通常用于動態(tài)分析和操作類,而不是在編譯期就確定類的名稱务甥。
class MyClass: NSObject {
}
let myObject = MyClass()
let objectClass: AnyClass = type(of: myObject) // Swift 中更推薦使用 type(of:)
print(objectClass) // 輸出: MyClass
import ObjectiveC
let className = "UIViewController"
if let viewControllerClass = objc_getClass(className) as? UIViewController.Type {
let viewController = viewControllerClass.init()
print(viewController) // 實例化一個 UIViewController 類
}
86.id和NSObject*的區(qū)別
在 Objective-C 中牡辽,id 和 NSObject* 均用于表示對象類型,但它們有一些關鍵的區(qū)別敞临。理解這些區(qū)別對編寫和閱讀 Objective-C 代碼非常重要态辛。
id
類型: id 是一個泛型指針,用于指向任意類型的 Objective-C 對象挺尿。
動態(tài)類型檢查: id 類型的變量在編譯時不會進行類型檢查奏黑。方法調用將推遲到運行時進行動態(tài)類型查找炊邦。
沒有靜態(tài)類型信息: 當你使用 id 時,編譯器不知道這個對象的類型熟史,因此你不能訪問任何特定于類的方法或屬性(除非使用運行時代碼馁害,如方法選擇器和動態(tài)分發(fā))。
適用性: 適用于表示不確定類型的對象蹂匹,或對多態(tài)對象編程碘菜。
id someObject = [NSString stringWithFormat:@"Hello, %@", @"world"];
[someObject length]; // 會在編譯時發(fā)出警告,因為編譯器不知道 `someObject` 的類型
NSObject*
類型: NSObject* 是一個指向 NSObject 類或其子類的指針怒详。
靜態(tài)類型檢查: NSObject* 類型變量在編譯時會進行類型檢查炉媒。編譯器知道對象至少具有 NSObject 類的接口。
靜態(tài)類型信息: 允許訪問 NSObject 類的所有方法和屬性昆烁,并適用多態(tài)吊骤,通過子類可以擁有更多的方法和屬性嘿般。
適用性: 使用于你明確知道對象至少是 NSObject 類的實例懦铺,或是 NSObject 的子類的實例。
NSObject *someObject = [NSString stringWithFormat:@"Hello, %@", @"world"];
[someObject description]; // 可以在編譯時成功雪情,因為 `description` 是 `NSObject` 的方法
87. 其他
http://www.reibang.com/p/d477e3884c5a
Swift系列面試題總結
http://www.reibang.com/p/431665354c65
http://www.reibang.com/p/d477e3884c5a
1.class 和 struct 的區(qū)別
struct是值類型鼠渺,class是引用類型鸭巴。
值類型的變量直接包含它們的數據,對于值類型都有它們自己的數據副本拦盹,因此對一個變量操作不可能影響另一個變量鹃祖。
引用類型的變量存儲對他們的數據引用,因此后者稱為對象普舆,因此對一個變量操作可能影響另一個變量所引用的對象恬口。
二者的本質區(qū)別:struct是深拷貝,拷貝的是內容沼侣;class是淺拷貝祖能,拷貝的是指針。
property的初始化不同:class 在初始化時不能直接把 property 放在 默認的constructor 的參數里蛾洛,而是需要自己創(chuàng)建一個帶參數的constructor养铸;而struct可以,把屬性放在默認的constructor 的參數里轧膘。
變量賦值方式不同:struct是值拷貝钞螟;class是引用拷貝。
immutable變量:swift的可變內容和不可變內容用var和let來甄別扶供,如果初始為let的變量再去修改會發(fā)生編譯錯誤筛圆。struct遵循這一特性;class不存在這樣的問題椿浓。
mutating function: struct 和 class 的差別是 struct 的 function 要去改變 property 的值的時候要加上 mutating太援,而 class 不用。
繼承: struct不可以繼承扳碍,class可以繼承提岔。
struct比class更輕量:struct分配在棧中,class分配在堆中笋敞。
2.Swift 是面向對象還是函數式的編程語言?
Swift 既是面向對象的碱蒙,又是函數式的編程語言。
說 Swift 是面向對象的語言夯巷,是因為 Swift 支持類的封裝赛惩、繼承、和多態(tài)趁餐,從這點上來看與 Java 這類純面向對象的語言幾乎毫無差別喷兼。
說 Swift 是函數式編程語言,是因為 Swift 支持 map, reduce, filter, flatmap 這類去除中間狀態(tài)后雷、數學函數式的方法季惯,更加強調運算結果而不是中間過程。
3.什么是泛型臀突,swift哪些地方使用了泛型勉抓?
泛型(generic)可以使我們在程序代碼中定義一些可變的部分,在運行的時候指定候学。使用泛型可以最大限度地重用代碼藕筋、保護類型的安全以及提高性能。
例如 optional 中的 map梳码、flatMap 隐圾、?? (泛型加逃逸閉包的方式,做三目運算)
4.swift 語法糖 边翁? 翎承!的本質(實現原理)
?為optional的語法糖
optional<T> 是一個包含了nil 和普通類型的枚舉符匾,確保使用者在變量為nil的情況下處理
叨咖!為optional 強制解包的語法糖
5.Optional(可選型) 是用什么實現的
Optional 是一個泛型枚舉
大致定義如下:
enum Optional<Wrapped> {
case none
case some(Wrapped)
}
除了使用 let someValue: Int? = nil 之外, 還可以使用let optional1: Optional<Int> = nil 來定義
6.什么是高階函數
一個函數如果可以以某一個函數作為參數, 或者是返回值, 那么這個函數就稱之為高階函數, 如 map, reduce, filter
7.如何解決引用循環(huán)
轉換為值類型, 只有類會存在引用循環(huán), 所以如果能不用類, 是可以解引用循環(huán)的,
delegate 使用 weak 屬性.
閉包中, 對有可能發(fā)生循環(huán)引用的對象, 使用 weak 或者 unowned, 修飾
8.定義靜態(tài)方法時關鍵字 static 和 class 有什么區(qū)別
static 定義的方法不可以被子類繼承, class 則可以
class AnotherClass {
static func staticMethod(){}
class func classMethod(){}
}
class ChildOfAnotherClass: AnotherClass {
override class func classMethod(){}
//override static func staticMethod(){}// error
}
9.請說明并比較以下關鍵詞:Open, Public, Internal, File-private, Private
Swift 有五個級別的訪問控制權限,從高到低依次為比如 Open, Public, Internal, File-private, Private啊胶。
他們遵循的基本原則是:高級別的變量不允許被定義為低級別變量的成員變量甸各。比如一個 private 的 class 中不能含有 public 的 String。反之焰坪,低級別的變量卻可以定義在高級別的變量中趣倾。比如 public 的 class 中可以含有 private 的 Int。
Open 具備最高的訪問權限某饰。其修飾的類和方法可以在任意 Module 中被訪問和重寫儒恋;它是 Swift 3 中新添加的訪問權限善绎。
Public 的權限僅次于 Open。與 Open 唯一的區(qū)別在于它修飾的對象可以在任意 Module 中被訪問诫尽,但不能重寫禀酱。
Internal 是默認的權限。它表示只能在當前定義的 Module 中訪問和重寫牧嫉,它可以被一個 Module 中的多個文件訪問剂跟,但不可以被其他的 Module 中被訪問。
File-private 也是 Swift 3 新添加的權限酣藻。其被修飾的對象只能在當前文件中被使用曹洽。例如它可以被一個文件中的 class,extension辽剧,struct 共同使用送淆。
Private 是最低的訪問權限。它的對象只能在定義的作用域內使用抖仅。離開了這個作用域坊夫,即使是同一個文件中的其他作用域,也無法訪問撤卢。
10.swift中,關鍵字 guard 和 defer 的用法 guard也是基于一個表達式的布爾值去判斷一段代碼是否該被執(zhí)行环凿。與if語句不同的是,guard只有在條件不滿足的時候才會執(zhí)行這段代碼放吩。
guard let name = self.text else { return }
defer的用法是智听,這條語句并不會馬上執(zhí)行,而是被推入棧中渡紫,直到函數結束時才再次被調用到推。
defer {
//函數結束才調用
}
11.async與await在隊列中使用時會創(chuàng)建新線程嗎?
不會惕澎,async/await本身不會創(chuàng)建新線程莉测,它的執(zhí)行線程取決于其被調用的隊列環(huán)境
12.逃逸閉包和非逃逸閉包
非逃逸閉包是指那些不能在定義它的函數返回之后繼續(xù)存在的閉包。換句話說唧喉,這種閉包只能在其定義的作用域內被調用捣卤,并且一旦該作用域結束,閉包也會被銷毀八孝。對于非逃逸閉包董朝,你可以直接將它作為參數傳遞給函數,而不需要特別標注干跛。
func someFunction(performClosure: () -> Void) {
performClosure()
}
someFunction { print("Hello, world!") }
逃逸閉包是指那些可以在定義它的函數執(zhí)行完畢后繼續(xù)存在的閉包子姜。這意味著閉包可以“逃逸”出其定義的作用域,并在外部被調用楼入。要定義一個逃逸閉包哥捕,你需要在閉包參數前加上 @escaping 屬性牧抽。
func someFunctionWithEscapingClosure(completion: @escaping () -> Void) {
// 可能會在稍后某個時間點調用 completion
DispatchQueue.global().async {
// 異步操作完成后調用 completion 閉包
completion()
}
}
someFunctionWithEscapingClosure {
print("閉包在異步操作完成后被調用")
}
在這個例子中,completion 是一個逃逸閉包扭弧,因為它被傳遞給了 DispatchQueue.global().async 方法阎姥,并且將在 someFunctionWithEscapingClosure 返回之后的某個時刻被調用记舆。
使用逃逸閉包需要注意的地方
- 安全性:由于逃逸閉包可以在函數返回后繼續(xù)存在鸽捻,因此必須小心管理它們的生命周期,以避免內存泄漏等問題泽腮。
- 性能:逃逸閉包可能會導致更多的內存分配御蒲,因為它們需要在堆上創(chuàng)建并保持引用計數。
- API 設計:在設計 API 時诊赊,應明確指出哪些閉包參數是逃逸的厚满,以幫助調用者理解閉包的行為。
通過使用逃逸和非逃逸閉包碧磅,Swift 提供了更細粒度的控制碘箍,使得開發(fā)者能夠更好地管理閉包的生命周期和內存使用情況。
13.Struct一般存儲在什么地方鲸郊,有可能會存在堆區(qū)嗎丰榴?
在Swift中,
struct
(結構體)默認是值類型秆撮,這意味著它們通常存儲在棧上四濒,并且當它們作為函數參數傳遞或被賦值給另一個變量時,它們會被復制职辨。然而盗蟆,在某些情況下,結構體會被存儲在堆上舒裤,這取決于幾個因素:
- 大小:如果一個結構體的大小超過了一定閾值(這個閾值沒有明確公布喳资,但通常是幾十個字節(jié)),那么它就會被放在堆上腾供,而不是棧上仆邓。這種行為被稱為“大結構體優(yōu)化”(Large Struct Optimization)。
- 引用循環(huán):如果一個結構體包含了一個對類(引用類型)的引用台腥,并且那個類又反過來引用了該結構體宏赘,可能會導致引用循環(huán)。為了處理這種情況黎侈,Swift編譯器有時會選擇將結構體放在堆上來打破這種循環(huán)察署。
- 可選類型中的結構體:如果一個結構體被聲明為可選類型并存儲在一個類實例中,那么這個結構體可能也會被存儲在堆上峻汉,因為可選類型的內部實現涉及到間接引用贴汪。
- 頂層閉包捕獲的結構體:如果一個結構體被捕獲在一個頂層閉包中(即閉包不是在一個函數內部定義的)脐往,那么這個結構體也會被移動到堆上,以允許閉包保持對其的引用扳埂。
總結來說业簿,雖然Swift中的結構體默認存儲在棧上,但在特定條件下阳懂,比如結構體過大或者存在引用循環(huán)的情況下梅尤,它們可能會被存儲在堆上。這種行為可以提高內存管理的效率和程序性能岩调。
算法題:
1.用遞歸寫一個算法巷燥,計算從1到100的和
func sum(value: Int) -> Int {
if value <= 0 {
return 0
}
var number = value
return value + sum(value: number - 1)
}
// 計算過程
let result = sum(value: 100)
print(result)
2.給定一個Int型數組,用里面的元素組成一個最大數号枕,因為數字可能非常大缰揪,用字符串輸出。
func largestNumber (_ nums: [Int]) -> String {
let sort = nums.map {"\($0)"}.sorted { (lStr, rStr) -> Bool in
return lStr + rStr > rStr + lStr
}
let result = sort.joined()
if result.prefix(1) == "0" {
return "0"
} else {
return result
}
}