作為一個開發(fā)者,有一個學習的氛圍跟一個交流圈子特別重要,這是一個我的iOS開發(fā)公眾號:編程大鑫,不管你是大牛還是小白都歡迎入駐 ,讓我們一起進步,共同發(fā)展?。ㄈ簝?nèi)會免費提供一些群主收藏的免費學習書籍資料以及整理好的幾百道面試題和答案文檔?。?/strong>
1、字符串常用方法
字符串截取
NSString *strSub = [str substringFormIndex:2];
NSString *strSubT = [str substringToIndex:2];
NSString *strSubR = [str substringWithRange:range];
字符串替換
NSString *newStr = [str stringByReplacingOccurencesOfString:@"ll" withString:@"al"];
編碼,把UTF8編碼的字符串編碼成URL中可用的字符串
url_cn = [url_cn stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
字符串轉(zhuǎn)數(shù)組
NSArray *array = [str componentsSeparatedByString:@","];--分隔符
數(shù)組轉(zhuǎn)字符串
NSString *str = [array componentsJoinedByString:@","];--分隔符
字符串轉(zhuǎn)字典
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *err;
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData
options:NSJSONReadingMutableContainers
error:&err];
字典轉(zhuǎn)字符串
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:&parseError];
2、數(shù)組
數(shù)組替換
[_dataSource replaceObjectAtIndex:1 withObject:recentArray];
數(shù)組倒序
NSArray *array = @[@1, @20, @3, @10, @2];//排序后到新數(shù)組里
NSArray *sortedArray = [array sortedArrayUsingComparator:^NSComparisonResult(NSNumber* obj1, NSNumber* obj2) {
if ([obj1 intValue] > [obj2 intValue]) {
return NSOrderedDescending;
} else {
return NSOrderedAscending;
}
}];
3、淺談iOS開發(fā)中方法延遲執(zhí)行的集中方式
1》performSelector方法
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;
2》NSTimer定時器
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
3》NSThread線程的sleep
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
4》GCD
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});
4、寫一個完整的代理,包括生命、實現(xiàn)
//創(chuàng)建
@protocol MyDelegate
@required
-(void)eat:(NSString *)food;
@optional
-(void)run;
@end
//聲明.h
@interface person:NSObject<MyDelegate>
@end
//實現(xiàn).m
@implementation person
-(void)eat:(NSString *)food{
}
-(void)run{
}
@end
5、BAD_ACCESS在什么情況下出現(xiàn)
這種問題在開發(fā)時經(jīng)常遇到,原因是訪問了野指針,比如訪問已經(jīng)釋放的對象的成員變量或者發(fā)消息、死循環(huán)等。
6、lldb(gdb)常用的控制臺調(diào)試命令
1》p 輸出基本類型,是打印命令,需要指定類型,是print的簡寫
p (int)[[[self view] subviews] count]
2》po 打印對象,會調(diào)用description方法,是print-object的簡寫
po [self view]
3》expr 可以在調(diào)試時動態(tài)執(zhí)行制定表達式,并將結(jié)果打印出來。常用于在調(diào)試過程中修改變量的值。
4》bt 打印調(diào)用堆棧,是thread backtrace的簡寫,加all可打印所有的thread的堆棧。
5》br l 是breakpoint list的簡寫。
7、你一般是怎么用Instruments的?
Instruments里面工具很多,常用的有:
1》Time Profiler:性能分析
2》Zoombies:檢查是否訪問了僵尸對象,但是這個工具只能從上往下檢查,不智能
3》Allocations:用來檢查內(nèi)存,寫算法的那批人也用這個來檢查
4》Leaks:檢查內(nèi)存,看是否有內(nèi)存泄漏
8、iOS常用的數(shù)據(jù)存儲方式有哪些?
數(shù)據(jù)存儲有四種方案:NSUserDefault、KeyChain、file、DB。
其中File有三種方式:plist、Archive(歸檔)
DB包括:SQLite、FMDB、CoreData
9、Runtime可以做什么事情
1》獲取類里面的所有成員變量
2》為類動態(tài)添加成員變量
3》動態(tài)改變類的方法實現(xiàn)
4》為類動態(tài)添加新的方法
10、WKWebView與UIWebView的比較替換
相比于UIWebView的優(yōu)勢
1》在性能、穩(wěn)定性、占用內(nèi)存方面有很大提升
2》允許JavaScript的Nitro庫加載并使用(UIWebView中限制)
3》增加加載進度實行:estimatedProgress,不用再自己寫進度條了
4》支持了更多的HTML屬性。
11、iOS常見加密方式
1》base64加密:基本原理是原本是8個bit一組表示數(shù)據(jù),改為6個bit一組表示數(shù)據(jù),不足的部分補零,每兩個0用一個 = 表示;用base64編碼之后,數(shù)據(jù)長度會變大,增加了大約1/3左右;可進行反向解密;編碼有個非常顯著的特點,末尾有個 = 號
2》MD5加密:把任意一個長度的字符串換成一定長度的十六進制的大整數(shù)
3》AES加密
4》RSA加密
12、drawRect
1》我們只能在繼承了UIView的子類中通過重寫drawRect方法來繪制圖形
2》如果需要繪制圖形的子類直接繼承自UIView,則子類的drawRect中不需要調(diào)用父類方法[super drawRect:rect]
;。如果子類繼承自其它繼承UIView的view類,則drawRect方法中需要調(diào)用父類方法[super drawRect:rect]
;。
3》drawRect方法不能手動直接調(diào)用,我們可以通過調(diào)用其它方法來實現(xiàn)drawRect方法的調(diào)用。如:在子類初始化時調(diào)用- (instancetype)initWithFrame:(CGRect)frame
方法,且frame不為CGRectZero時。
4》我們可以調(diào)用setNeedsDisplay()
方法或setNeedsDisplayInRect
方法,但是該方法不會自己調(diào)用drawRect方法,而是會標記視圖,并在下一次循環(huán)更新的時候讓視圖通過drawRect來進行重繪,前提是rect不為CGRectZero。
13、iOS開發(fā)中的鎖
1》臨界區(qū):指的是一塊對公共資源進行訪問的代碼,并非是一種機制或者算法。
2》互斥鎖:是一種用于多線程編程中,防止兩條線程同時對同一個公共資源進行讀寫的機制,該目的通過將代碼切片成一個一個的臨界區(qū)而達成。
3》自旋鎖:是用于多線程同步的一種鎖,線程會反復檢查鎖變量是否可用。由于線程在這一過程中保持執(zhí)行,因此是一種忙等待。一旦獲取了自旋鎖,線程會一直保持該鎖,直至顯示釋放自旋鎖。自旋鎖避免了進城上下文的調(diào)度開銷,因此對于線程只會阻塞很短時間的場合是有效的。
4》讀寫鎖:是計算機程序的并發(fā)控制的一種同步機制,也稱“共享-互斥鎖”、多讀者-單寫者鎖,用于解決多線程對公共資源的讀寫問題。讀操作可并發(fā)重入,寫操作是互斥的。讀寫鎖通常用互斥鎖、條件變量、信號量實現(xiàn)。
5》信號量:是一種更高級的同步機制,互斥鎖可以說是semaphore在僅取值0/1的特例。信號量可以有更多的取值空間,用來實現(xiàn)更加復雜的同步,而不單單是線程間互斥。
6》條件鎖:就是條件變量,當進城的某些資源要求不滿足時就進入休眠,也就是鎖住了。當資源被分配到了,條件鎖打開,進城繼續(xù)運行。
14、系統(tǒng)對象的 copy 與 mutableCopy 方法
不管是集合類對象(NSArray、NSDictionary、NSSet ... 之類的對象),還是非集合類對象(NSString, NSNumber ... 之類的對象),接收到copy和mutableCopy消息時,都遵循以下準則:
1. copy 返回的是不可變對象(immutableObject);如果用copy返回值調(diào)用mutable對象的方法就會crash。
2. mutableCopy 返回的是可變對象(mutableObject)。
一、非集合類對象的copy與mutableCopy
在非集合類對象中,對不可變對象進行copy操作,是指針復制,mutableCopy操作是內(nèi)容復制;
對可變對象進行copy和mutableCopy都是內(nèi)容復制。用代碼簡單表示如下:
NSString *str = @"hello word!";
NSString *strCopy = [str copy] // 指針復制,strCopy與str的地址一樣
NSMutableString *strMCopy = [str mutableCopy] // 內(nèi)容復制,strMCopy與str的地址不一樣
NSMutableString *mutableStr = [NSMutableString stringWithString: @"hello word!"];
NSString *strCopy = [mutableStr copy] // 內(nèi)容復制
NSMutableString *strMCopy = [mutableStr mutableCopy] // 內(nèi)容復制
二、集合類對象的copy與mutableCopy (同上)
在集合類對象中,對不可變對象進行copy操作,是指針復制,mutableCopy操作是內(nèi)容復制;
對可變對象進行copy和mutableCopy都是內(nèi)容復制。但是:集合對象的內(nèi)容復制僅限于對象本身,對集合內(nèi)的對象元素仍然是指針復制。(即單層內(nèi)容復制)
NSArray *arr = @[@[@"a", @"b"], @[@"c", @"d"];
NSArray *copyArr = [arr copy]; // 指針復制
NSMutableArray *mCopyArr = [arr mutableCopy]; //單層內(nèi)容復制
NSMutableArray *array = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
NSArray *copyArr = [mutableArr copy]; // 單層內(nèi)容復制
NSMutableArray *mCopyArr = [mutableArr mutableCopy]; // 單層內(nèi)容復制
【總結(jié)一句話】:
只有對不可變對象進行copy操作是指針復制(淺復制),其它情況都是內(nèi)容復制(深復制)!
15、寫一個 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;
}
16、KVC的底層實現(xiàn)?
當一個對象調(diào)用setValue方法時,方法內(nèi)部會做以下操作:
1). 檢查是否存在相應的key的set方法,如果存在,就調(diào)用set方法。
2). 如果set方法不存在,就會查找與key相同名稱并且?guī)聞澗€的成員變量,如果有,則直接給成員變量屬性賦值。
3). 如果沒有找到_key,就會查找相同名稱的屬性key,如果有就直接賦值。
4). 如果還沒有找到,則調(diào)用valueForUndefinedKey:和setValue:forUndefinedKey:方法。
這些方法的默認實現(xiàn)都是拋出異常,我們可以根據(jù)需要重寫它們。
17、你是否接觸過OC中的反射機制?簡單聊一下概念和使用
1). class反射
通過類名的字符串形式實例化對象。
Class class = NSClassFromString(@"student");
Student *stu = [[class alloc] init];
將類名變?yōu)樽址? Class class =[Student class];
NSString *className = NSStringFromClass(class);
2). SEL的反射
通過方法的字符串形式實例化方法。
SEL selector = NSSelectorFromString(@"setName");
[stu performSelector:selector withObject:@"Mike"];
將方法變成字符串。
NSStringFromSelector(@selector*(setName:));
18、調(diào)用方法有兩種方式
1). 直接通過方法名來調(diào)用。[person show];
2). 間接的通過SEL數(shù)據(jù)來調(diào)用 SEL aaa = @selector(show); [person performSelector:aaa];
19、iOS的沙盒目錄結(jié)構是怎樣的?
沙盒結(jié)構:
1). Application:存放程序源文件,上架前經(jīng)過數(shù)字簽名,上架后不可修改。
2). Documents:常用目錄,iCloud備份目錄,存放數(shù)據(jù)。(這里不能存緩存文件,否則上架不被通過)
3). Library:
Caches:存放體積大又不需要備份的數(shù)據(jù)。(常用的緩存路徑)
Preference:設置目錄,iCloud會備份設置信息。
4). tmp:存放臨時文件,不會被備份,而且這個文件下的數(shù)據(jù)有可能隨時被清除的可能。
20、什么是 TCP / UDP ?
TCP:傳輸控制協(xié)議。
UDP:用戶數(shù)據(jù)協(xié)議。
TCP 是面向連接的,建立連接需要經(jīng)歷三次握手,是可靠的傳輸層協(xié)議。
UDP 是面向無連接的,數(shù)據(jù)傳輸是不可靠的,它只管發(fā),不管收不收得到。
簡單的說,TCP注重數(shù)據(jù)安全,而UDP數(shù)據(jù)傳輸快點,但安全性一般。
21、通信底層原理(OSI七層模型)
OSI采用了分層的結(jié)構化技術,共分七層:
物理層、數(shù)據(jù)鏈路層、網(wǎng)絡層、傳輸層、會話層、表示層、應用層。
22、block的實質(zhì)是什么?有幾種block?分別是怎么產(chǎn)生的?
block本質(zhì)上是一個OC對象,他內(nèi)部也有一個isa指針。block是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的OC對象。
一共有三種block,分別是全局的、棧上的、堆上的
NSGlobalBlock直到程序結(jié)束才會被回收
NSStackBlock類型block存放在棧中,我們直到棧中的內(nèi)存由系統(tǒng)自動分配和釋放,作用域執(zhí)行完畢之后就會被立即釋放
NSMallocBlock是在平時編碼過程中最常用到的。存放在堆中需要我們自己進行內(nèi)存管理。
__block
用于解決block內(nèi)部不能修改auto變量值的問題,__block
不能修飾靜態(tài)變量(static) 和全局變量
23、不借用第三個變量,如何交換兩個變量的值?要求手動寫出交換過程
方法一:
a = a + b;
b = a - b; // b = (a +b)-b,即 b = a
a = a - b; // a = (a+b)-a
方法二:
a = a - b;
b = a + b; // b = (a-b)+b,即b=a
a = b - a; // a = a - (a-b)
24、設計模式有哪些
1》觀察者模式:KVO是典型的觀察者模式,觀察某個屬性的改變,改變時會通知觀察者
2》委托模式:代理+協(xié)議的組合,實現(xiàn)1對1的反向傳值操作
3》單利模式:通過static關鍵詞,聲明全局變量,在整個進程運行期間只會被賦值一次
25、@property的本質(zhì)是什么,有哪些屬性關鍵字
1》@property的本質(zhì)是實例變量+存取方法。
2》原子性與非原子性
讀寫權限
內(nèi)存管理語義 assign strong weak copy
方法名 getter setter
不常用的 nonnull nullable等
26、什么時候使用weak關鍵字,相比assign有什么區(qū)別
1》在ARC中,有可能出現(xiàn)循環(huán)引用的時候使用weak,比如代理,block;自身已經(jīng)對他進行一次強引用,沒有必要再強引用一次,比如IBOutlot,因為父控件的subviews數(shù)組已經(jīng)對他有一個強引用
2》weak表明該屬性定義了一種“非擁有關系”。在該屬性所指的對象銷毀時,屬性值會自動清空(nil)。
3》assign可以用于非OC對象,weak必須用于OC對象。
27、怎么使用copy關鍵字
1》NSString,NSArray,NSDictionary等經(jīng)常使用copy關鍵字,因為他們有對應的可變類型。如果使用strong關鍵字,那么這個屬性就有可能指向一個可變對象,如果這個可變對象修改了,那么會影響該屬性。如果可變對象使用了copy關鍵字,那么這個可變對象做增刪改的時候,系統(tǒng)會因為找不到對應的程序而崩潰。因為copy復制的是一個不可變對象,不可變對象是不能進行增刪改的操作的。
2》block也經(jīng)常使用copy關鍵字。block使用copy關鍵字是從MRC留下來的傳統(tǒng),方法內(nèi)部的block是在棧區(qū)的,使用copy可以把他放到堆區(qū)。在ARC中寫不寫都行,使用copy或者strong效果都是一樣的。
28、如何讓自己的類用copy修飾符?如何重寫帶copy關鍵字的setter方法?
1》該類需要遵從NSCopying協(xié)議
2》實現(xiàn)NSCopying的協(xié)議: - (id)copyWithZone:(NSZone *)zone;
29、@synthesize 和 @dynamic 分別有什么作用?
如果@synthesize和@dynamic都沒寫,那么默認的就是@synthesize var = _var ;@synthesize的語義是如果你沒有手動實現(xiàn)setter和getter方法,那么編譯器會自動為你加上這兩個方法。@dynamic告訴編譯器,屬性的setter和getter方法油用戶自己實現(xiàn),不自動生成。
30、常見的OC的數(shù)據(jù)類型有哪些,和C的基本數(shù)據(jù)類型有什么區(qū)別?如:NSInteger和int
OC的數(shù)據(jù)類型有NSString,NSArray,NSData等等,這些都是class,創(chuàng)建后便是對象,而C語言的基本數(shù)據(jù)類型是int,只是有一定字節(jié)的內(nèi)存空間,用于存放數(shù)值。NSinteger是基本數(shù)據(jù)類型,不是NSNumber的子類,當然也不是NSObject的子類。NSInteger是基本數(shù)據(jù)類型int或者long的別名。他的區(qū)別在于,NSInteger會根據(jù)系統(tǒng)是32位還是64位來決定本身是int還是long。
31、OC如何對內(nèi)存管理的,說說你的看法和解決方案?
OC的內(nèi)存管理模式主要有三種:ARC(自動內(nèi)存計數(shù))、手動內(nèi)存計數(shù)、內(nèi)存池
1》自動內(nèi)存計數(shù)(ARC):由Xcode自動在APP編譯階段,在代碼中添加內(nèi)存管理代碼
2》手動內(nèi)存計數(shù)(MRC):遵循內(nèi)存誰申請,誰釋放;誰添加,誰釋放的原則
3》內(nèi)存釋放池:把需要釋放的內(nèi)存統(tǒng)一放在一個池子里面,當池子被抽干后,池子中所有的內(nèi)存空間也會被自動釋放。內(nèi)存池的釋放操作分為自動和手動,自動釋放受runloop機制影響。
32、weak和strong的區(qū)別
1》strong指針能夠保持對象的生命,一個對象只要有strong指針指向他,那么他就不會被釋放,相反的,如果沒有strong指針指向他,那么他就會被自動釋放。默認的局部變量都是強指針,存放在堆里面
2》weak型的指針變量仍然可以是一個對象,但是不屬于對象的擁有者。即當對象被銷毀的時候,這個weak指針也就自動指向nil。
33、block在ARC和MRC中的區(qū)別
如何判斷當前文件是MRC還是ARC:dealloc方法中能否調(diào)用super,只有MRC才能調(diào)用super;能否用retain、release,如果可以就是MRC。
MRC沒有strong、weak,局部變量對象就是相當于基本數(shù)據(jù)類型;MRC給成員變量賦值一定要用set方法,不能直接訪問下劃線成員屬性賦值。
總之,只要block不引用外部變量,block放在全局區(qū)。
MRC 管理block:只要block引用外部變量,block放在棧區(qū),block只能使用copy不能使用retain,用retain,block還是在棧里面
ARC管理block:只要block引用外部變量,block就放在堆區(qū),block使用copy,盡量不要使用strong。
34、KVO、NSNotifaction、delegate、block的區(qū)別
1》KVO是觀察者模式,一般搭配KVC使用,通過KVO可以監(jiān)測一個值得變化,是一對多的關系,一個值的變化會通知所有的觀察者。
2》NSNotifaction是通知,一對多,在某些情況下,KVO和NSNotifaction是一樣的,都是狀態(tài)變化之后告知對方。不同的是,NSNotifaction需要被觀察者先主動發(fā)出通知,然后觀察者注冊監(jiān)聽后再來進行響應,比KVO多了發(fā)送通知這一步,但是其優(yōu)點是監(jiān)聽不局限與屬性的變化,還可以對多種多樣的狀態(tài)進行監(jiān)聽,監(jiān)聽范圍廣,使用也更靈活。
3》delegate是代理,就是把自己不想做的事情交給別人去做,不需要關心中間需要做的事情,只要調(diào)用delegate就可以了,由其他類完成所需要的動作,所以是一對一的。
4》block是delegate的另一種形式,是函數(shù)式編程的一種形式,使用場景和delegate一樣,相比delegate更為靈活,而且實現(xiàn)也更直觀。
5》KVO一般的使用場景是數(shù)據(jù),需求是數(shù)據(jù)變化。delegate一般的使用場景是行為,需要別人幫忙做一件事情。NSNotifaction一般是進行全局通知,只需要發(fā)出通知就可以,不關心你有沒有接受到通知。delegate是強關聯(lián),就是委托和代理雙方都知道。
35、談談UITablebView的優(yōu)化
1》正確的復用cell
2》設計統(tǒng)一規(guī)格的cell
3》提前計算并緩存好高度,因為heightForRowAtIndexPath是調(diào)用最頻繁的方法
4》異步繪制,遇到復雜頁面,遇到性能瓶頸時,可能就是突破口
5》滑動時按需加載,這個在大量圖片展示,網(wǎng)絡加載的時候很管用
6》減少子視圖的層級關系
7》不要動態(tài)的add或者remove子控件,最好在初始化的時候就添加完,然后通過hidden來控制是否顯示。
36、OC中堆和棧的區(qū)別
管理方式:棧是編譯器自動管理,堆的釋放工作由程序員空
棧:在Windows下,棧是向低地址擴展的數(shù)據(jù)結(jié)構,是一塊連續(xù)的內(nèi)存區(qū)域。這句話的意思是棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預先設計好的。在windows 下,棧的大小是2M(也有的說是1M),如果申請的空間超過棧的剩余空間是,將提示overFlow,因此,能從棧獲得的空間較小。
堆:堆是向高笛子擴展的數(shù)據(jù)結(jié)構,是不連續(xù)的內(nèi)存區(qū)域。堆得大小受限于計算機系統(tǒng)中有效的虛擬內(nèi)存,由此可見,堆獲得空間比較靈活,也比較大。
碎片問題:對于堆來講,頻繁的new/delete勢必會造成內(nèi)存空間的不連續(xù),從而造成大量的碎片,使程序效率降低。對于棧來講,則不會存在這個問題,因為棧是先進后出的隊列,他們是如此的一一對應,以至于永遠都不可能有一個內(nèi)存塊從棧中間彈出分配方式:堆都是動態(tài)分配的,沒有靜態(tài)分配的堆。
棧有2種分配方式:靜態(tài)分配和動態(tài)分配。靜態(tài)分配是編譯器完成的,比如局部變量的分配。動態(tài)分配由alloca函數(shù)進行分配,但是棧的動態(tài)分配和堆是不同的,他的動態(tài)分配是由編譯器進行釋放,無需我們手工實現(xiàn)。分配效率:棧是機器系統(tǒng)提供的數(shù)據(jù)結(jié)構,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執(zhí)行,這就決定了棧的效率比較高。堆則是C/C++函數(shù)庫提供的,它的機制是很復雜的。
37、分類和擴展
分類里面有類名,方法列表,協(xié)議列表,但是沒有屬性列表,所以原則上來說,分類只能添加方法,不能添加屬性的。當然,添加屬性也可以,只要不去調(diào)用他。因為添加的屬性沒有get和set方法,雖然我們可以用runtime去動態(tài)的添加set和get方法,但是調(diào)用屬性的時候還是不會通過的,因為他沒有實例變量。
分類里邊添加的方法,即使不實現(xiàn)也是不會報錯的,因為分類是在運行時添加到類里邊去的。但是擴展添加的方法必須要實現(xiàn),因為擴展是在編譯時添加進去的。
分類里邊添加的方法如果和原有類中的方法重名,則會優(yōu)先調(diào)用分類中的方法,因此盡量不要覆蓋原有類中的方法。
擴展添加的屬性默認是私有的,擴展沒有獨立的實現(xiàn)部分,也就是說,擴展中所聲明的方法必須依托對應的類的實現(xiàn)部分來實現(xiàn)。
38、atomic的實現(xiàn)機制:為什么不能保證絕對的線程安全
用atomic生成的set和get方法會進行加鎖操作,這個鎖僅僅保證了存取方法的線程安全,并非真正意義上的線程安全,因為線程安全還有除了讀寫之外的其他操作(比如:當一個線程正在進行讀取方法的操作時,同時又有一個線程在進行release操作,可能會直接出現(xiàn)crash)
atomic 更耗費資源,速度要慢,如果沒有多線程之間的通訊,盡量還是使用nonatomic。
舉例:當幾個線層同時調(diào)用同一屬性的讀取方法時,會get到一個完整的值,但是get的值不可控
線程1 調(diào)用get
線程2 調(diào)用set
線程3 調(diào)用set
這3個線程同時執(zhí)行,線程1 會get到一個值,但是get到的值不可控,有可能是線程2 線程3 之前的原始值,也有可能是線程2 線層3 set之后的值
nonatomic 生成的讀取方法沒有加鎖,線程不安全,但是更快,當同一個線程同時訪問同一個屬性時,會出現(xiàn)無法預料的結(jié)果。
39、被weak修飾的對象在被釋放的時候會發(fā)生什么?是如何實現(xiàn)的?知道sidetable嗎?里面的結(jié)構可以畫出來嗎
被weak修飾的對象在釋放時會被置為nil,不同于assign。
runtime 維護了一個weak 表,用于存儲指向某個對象的所有weak 指針。weak表其實是一個hash 表,key是所指對象的地址,value是weak指針的地址數(shù)組。
1》初始化時,runtime會調(diào)用objc_initWeak函數(shù),初始化一個新的weak指針指向?qū)ο蟮牡刂?/p>
2》添加引用時,objc_initWeak函數(shù)會調(diào)用objc_storeWeak函數(shù),objc_storeWeak的作用是更顯指針指向,創(chuàng)建對應的弱引用表。
3》釋放時,調(diào)用clearDeallocating函數(shù)。clearDeallocating函數(shù)首先根據(jù)對象地址獲取所有weak指針地址的數(shù)組,然后遍歷這個數(shù)組把其中的數(shù)據(jù)置為nil,最后把這個entry從weak表中刪除,最后清理對象的記錄。
sideTable
struct SideTable {
// 保證原子操作的自旋鎖
spinlock_t slock;
// 引用計數(shù)的 hash 表
RefcountMap refcnts;
// weak 引用全局 hash 表
weak_table_t weak_table;
}
struct weak_table_t {
// 保存了所有指向指定對象的 weak 指針
weak_entry_t *weak_entries;
// 存儲空間
size_t num_entries;
// 參與判斷引用計數(shù)輔助量
uintptr_t mask;
// hash key 最大偏移值
uintptr_t max_hash_displacement;
};
40、關聯(lián)對象有什么應用,系統(tǒng)如何關聯(lián)對象?其被釋放的指針需要手動將所有的關聯(lián)對象的指針置空嗎?
AssociationsManager里面是由一個靜態(tài)AssociationsHashMap來存儲所有的關聯(lián)對象的。這相當于把所有對象的關聯(lián)對象都存在一個全局map里面。而map的key是這個對象的指針地址(任意兩個不同對象的指針地址一定是不同的),而這個map的value又是另外一個AssociationsHashMap,里面保存了關聯(lián)對象的kv對。
在obj dealloc 的時候會調(diào)用object_dispose,檢查有無關聯(lián)對象,有的話就_object_remove_assocations刪除
41、KVO的底層實現(xiàn)?如何取消系統(tǒng)默認的KVO并手動觸發(fā)(給KVO的觸發(fā)設定條件:改變的值符合某個條件時再觸發(fā)KVO)?
當觀察某對象A時,KVO機制動態(tài)創(chuàng)建一個對象A當前的子類,并未這個新的子類重寫了被觀察屬性keyPath 的 setter 方法。setter 方法隨后負責通知觀察對象屬性的改變狀況。
Apple 使用了 isa 混寫(isa-swizzling)來實現(xiàn) KVO 。當觀察對象A時,KVO機制動態(tài)創(chuàng)建一個新的名為:NSKVONotifying_A 的新類,該類繼承自對象A的本類,且 KVO 為 NSKVONotifying_A 重寫觀察屬性的 setter 方法,setter 方法會負責在調(diào)用原 setter 方法之前和之后,通知所有觀察對象屬性值的更改情況。
使用方法,可實現(xiàn)取消系統(tǒng)kvo,自己觸發(fā),也就可控。
+(BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{
if ([key isEqualToString:@"name"]) {
return NO;
}else{
return [super automaticallyNotifiesObserversForKey:key];
}
}
-(void)setName:(NSString *)name{
if (_name!=name) {
[self willChangeValueForKey:@"name"];
_name=name;
[self didChangeValueForKey:@"name"];
}
}
42、class_ro_t和class_rw_t的區(qū)別
objc類中的屬性、方法還有遵循的協(xié)議等消息都保存在class_rw_t中;
其中還有一個指向常量的的指針ro,其中存儲到了當前類在編譯器就已經(jīng)確定的屬性、方法以及遵從的協(xié)議。
43、iOS中內(nèi)省的幾個方法?class方法和object_getClass有什么區(qū)別?
內(nèi)省方法:
判斷對象類型:
-(BOOL) isKindOfClass:判斷是否是這個類或者這個類的子類的實例
-(BOOL) isMemberOfClass:判斷是否是這個類的實例
判斷對象or類是否有這個方法
-(BOOL) respondsToSelector:判斷實例是否有這樣方法
+(BOOL) instancesRespondToSelector: 判斷類是否有這個方法
object_getClass:獲得的是isa的指向
self.class:當self是實例對象的時候,返回的是類對象,否則返回自身。類方法class,返回的是self,所以當查找meta class,需要類對象調(diào)用object_getClass方法。
44、一個int變量被_block修飾與否的區(qū)別
沒有修飾,被block捕獲,是值拷貝。
使用_block修飾,會生成一個結(jié)構體,復制int的引用地址,達到修改數(shù)據(jù)。
45、為什么block在外部使用_weak修飾的同時需要在內(nèi)部使用__strong修飾
涉及到捕獲的時候該變量是存在的,在執(zhí)行block的時候可能被捕獲對象釋放了。
46、Runloop的作用是什么?他的內(nèi)部工作機制了解嗎
Runloop的作用是用來管理線程的,當線程的Runloop開啟后,線程就會在執(zhí)行任務后,處于休眠狀態(tài),隨時等待接受新的任務,而不是退出。
只有主線程的Runloop是默認開啟的,所以線程在開啟后,才會一直運行,不會退出。其他線程的Runloop如果需要開啟,就需要手動開啟。
在Runloop內(nèi)部有一個判斷循環(huán)的條件,如果滿足條件,就一直循環(huán),線程得到喚醒事件被喚醒,事件處理完畢以后,回到睡眠狀態(tài),等待下次喚醒。
47、哪些場景可以觸發(fā)離屏渲染
設置了一下屬性時,都會觸發(fā)離屏渲染:
1》shouldRasterize(光柵化)
2》masks(遮罩)
3》shadows(陰影)
4》edge antialiasing(抗鋸齒)
5》group opacity(不透明)
6》復雜形狀設置圓角等
7》漸變
48、block
1》當沒有外部變量時,block為__NSMallocBlock,它由開發(fā)者創(chuàng)建,存儲在堆內(nèi)存上。
2》當有__weak
修飾時block為__NSStackBlock,存儲在棧區(qū)。
3》當block有參數(shù)時(捕獲了外部變量時)block為__NSGlobalBlock,存儲在全局區(qū)。
4》block的本質(zhì)是一個結(jié)構體。
5》在block內(nèi)部使用外部指針且會造循環(huán)引用情況下,需要使用__weak修飾外部指針
6》在block內(nèi)部如果調(diào)用了延時函數(shù)還是用弱指針會取不到該指針,因為已經(jīng)被銷毀了,需要在block內(nèi)部再將弱指針重新強引用一下。
7》如果需要在block內(nèi)部改變外部棧區(qū)變量的話,需要用__block修飾外部變量。
49、delegate和block的使用比較
1》共同點:block和delegate的方法都可以理解成回調(diào)函數(shù),當某件事情發(fā)生的時候執(zhí)行一段代碼片段。
2》block優(yōu)點:是一種輕量級的回調(diào),能夠直接訪問上下文,使用塊的地方和塊的實現(xiàn)地方在同一個地方,使得代碼組織更加連貫。
3》delegate:相對來說是重量級的回調(diào),因為方法的生命和實現(xiàn)分離開來,代碼的連貫性不是很好,代理很多時候都需要存儲一些臨時數(shù)據(jù)。代理的回調(diào)函數(shù)可以是一組多個函數(shù),在不同的時機調(diào)用不同的回調(diào)函數(shù)。
4》怎么選擇:當回調(diào)函數(shù)多余3個的時候,采用代理比較好;使用代碼塊容易造成循環(huán)引用,代理不會出現(xiàn)該問題;其他情況下優(yōu)先考慮代碼塊。
50、UIViewController的生命周期
1》[ViewController initWithCoder:]
或[ViewController initWithNibName:Bundle]
:首先從歸檔文件中加載UIViewController
對象。即使是純代碼,也會把nil
作為參數(shù)傳給后者。
2》[UIView awakeFromNib]:
作為第一個方法的助手,方法處理一些額外的設置。
3》[ViewController loadView]
:創(chuàng)建或加載一個view
并把它賦值給UIViewController
的view
屬性。
-[ViewController viewDidLoad]
: 此時整個視圖層次(view hierarchy)
已經(jīng)放到內(nèi)存中,可以移除一些視圖,修改約束,加載數(shù)據(jù)等。
4》[ViewController viewWillAppear:]
: 視圖加載完成,并即將顯示在屏幕上。還沒設置動畫,可以改變當前屏幕方向或狀態(tài)欄的風格等。
5》[ViewController viewWillLayoutSubviews]
即將開始子視圖位置布局
6》[ViewController viewDidLayoutSubviews]
用于通知視圖的位置布局已經(jīng)完成
7》[ViewController viewDidAppear:]
:視圖已經(jīng)展示在屏幕上,可以對視圖做一些關于展示效果方面的修改。
8》[ViewController viewWillDisappear:]
:視圖即將消失
9》[ViewController viewDidDisappear:]
:視圖已經(jīng)消失
10》[ViewController dealloc:]
:視圖銷毀的時候調(diào)用
51、AppDelegate的幾個方法
1》當程序第一次運行并且將要顯示窗口的時候執(zhí)行該方法
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
2》程序進入后臺的時候需要先執(zhí)行程序取消活躍的方法
- (void)applicationWillResignActive:(UIApplication *)application
3》當程序進入后臺的時候
- (void)applicationDidEnterBackground:(UIApplication *)application
4》當程序?qū)⒁M入前臺的時候
- (void)applicationWillEnterForeground:(UIApplication *)application
5》當程序變得活躍的時候
- (void)applicationDidBecomeActive:(UIApplication *)application
6》當程序?qū)⒁顺龅臅r候
- (void)applicationWillTerminate:(UIApplication *)application
如果程序在后臺可以運行,則上面的方法會被替換成applicationDidEnterBackground
52、反射是什么?可以舉出幾個應用場景嗎
在計算機科學中,反射是指計算機程序在運行時(Runtime)可以訪問、檢測和修改它本身狀態(tài)或行為的一種能力。用比喻來說,反射就是程序在運行的時候能夠“觀察”并且改變自己的而行為。
要注意術語“反射”和“內(nèi)省”的關系:內(nèi)省機制僅指程序在運行時對自身信息的檢測;反射機制不僅包括要能在運行時對程序自身信息進行檢測,還要求程序能進一步根據(jù)這些信息改變程序狀態(tài)或結(jié)構。
一個重點是改變,一個重點是檢測。
比如通過類名,生成類 Class * tempClass = NSClassFromString(str);
為類增加方法等。
53、有哪些場景是NSOperation比GCD更容易實現(xiàn)的?(或者是NSOperation優(yōu)于GCD的幾點)
GCD是基于C的底層API,NSOperation屬于object-c類。
相對于GCD:
1》NSOperation擁有更多的函數(shù)可用
2》在NSOperationQueue中,可以建立各個NSOperation之間的依賴關系。
3》有KVO,可以檢測NSOperation是否正在執(zhí)行,是否結(jié)束,是否取消
4》NSOperationQueue可以方便的管理開發(fā)
54、APP啟動優(yōu)化策略?最好結(jié)合啟動流程來說(main函數(shù)的執(zhí)行前后都分別說一下)
55、OC中創(chuàng)建線程的方法是什么?如果在主線程中執(zhí)行代碼,方法是什么?
// 創(chuàng)建線程的方法
- [NSThread detachNewThreadSelector:nil toTarget:nil withObject:nil]
- [self performSelectorInBackground:nil withObject:nil];
- [[NSThread alloc] initWithTarget:nil selector:nil object:nil];
- dispatch_async(dispatch_get_global_queue(0, 0), ^{});
- [[NSOperationQueue new] addOperation:nil];
// 主線程中執(zhí)行代碼的方法
- [self performSelectorOnMainThread:nil withObject:nil waitUntilDone:YES];
- dispatch_async(dispatch_get_main_queue(), ^{});
- [[NSOperationQueue mainQueue] addOperation:nil];
56、用偽代碼寫一個線程安全的單例模式
static id _instance;
+ (id)allocWithZone:(struct _NSZone *)zone {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
}
+ (instancetype)sharedData {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
- (id)copyWithZone:(NSZone *)zone {
return _instance;
}
57、AFNetworking 底層原理分析
AFNetworking主要是對NSURLSession和NSURLConnection(iOS9.0廢棄)的封裝,其中主要有以下類:
1). AFHTTPRequestOperationManager:內(nèi)部封裝的是 NSURLConnection, 負責發(fā)送網(wǎng)絡請求, 使用最多的一個類。(3.0廢棄)
2). AFHTTPSessionManager:內(nèi)部封裝是 NSURLSession, 負責發(fā)送網(wǎng)絡請求,使用最多的一個類。
3). AFNetworkReachabilityManager:實時監(jiān)測網(wǎng)絡狀態(tài)的工具類。當前的網(wǎng)絡環(huán)境發(fā)生改變之后,這個工具類就可以檢測到。
4). AFSecurityPolicy:網(wǎng)絡安全的工具類, 主要是針對 HTTPS 服務。
5). AFURLRequestSerialization:序列化工具類,基類。上傳的數(shù)據(jù)轉(zhuǎn)換成JSON格式
(AFJSONRequestSerializer).使用不多。
6). AFURLResponseSerialization:反序列化工具類;基類.使用比較多:
7). AFJSONResponseSerializer; JSON解析器,默認的解析器.
8). AFHTTPResponseSerializer; 萬能解析器; JSON和XML之外的數(shù)據(jù)類型,直接返回二進
制數(shù)據(jù).對服務器返回的數(shù)據(jù)不做任何處理.
9). AFXMLParserResponseSerializer; XML解析器;
58、描述下SDWebImage里面給UIImageView加載圖片的邏輯
SDWebImage 中為 UIImageView 提供了一個分類UIImageView+WebCache.h, 這個分類中有一個最常用的接口sd_setImageWithURL:placeholderImage:,會在真實圖片出現(xiàn)前會先顯示占位圖片,當真實圖片被加載出來后再替換占位圖片。
加載圖片的過程大致如下:
1.首先會在 SDWebImageCache 中尋找圖片是否有對應的緩存, 它會以url 作為數(shù)據(jù)的索引先在內(nèi)存中尋找是否有對應的緩存
2.如果緩存未找到就會利用通過MD5處理過的key來繼續(xù)在磁盤中查詢對應的數(shù)據(jù), 如果找到了, 就會把磁盤中的數(shù)據(jù)加載到內(nèi)存中,并將圖片顯示出來
3.如果在內(nèi)存和磁盤緩存中都沒有找到,就會向遠程服務器發(fā)送請求,開始下載圖片
4.下載后的圖片會加入緩存中,并寫入磁盤中
5.整個獲取圖片的過程都是在子線程中執(zhí)行,獲取到圖片后回到主線程將圖片顯示出來
SDWebImage原理:
調(diào)用類別的方法:
1\. 從內(nèi)存(字典)中找圖片(當這個圖片在本次使用程序的過程中已經(jīng)被加載過),找到直接使用。
2\. 從沙盒中找(當這個圖片在之前使用程序的過程中被加載過),找到使用,緩存到內(nèi)存中。
3\. 從網(wǎng)絡上獲取,使用,緩存到內(nèi)存,緩存到沙盒。
59、HTTPS和HTTP的區(qū)別
1、https協(xié)議需要到ca申請證書,一般免費證書較少,因而需要一定費用。
2、http是超文本傳輸協(xié)議,信息是明文傳輸,https則是具有安全性的ssl加密傳輸協(xié)議。
3、http和https使用的是完全不同的連接方式,用的端口也不一樣,前者是80,后者是443。
4、http的連接很簡單,是無狀態(tài)的;HTTPS協(xié)議是由SSL+HTTP協(xié)議構建的可進行加密傳輸、身份認證的網(wǎng)絡協(xié)議,比http協(xié)議安全。
60、iOS中imageNamed 和 imageWithContentOfFile的區(qū)別
使用imageNamed:加載圖片
- 加載到內(nèi)存中后,會一直停留在內(nèi)存中,不會隨著對象銷毀而銷毀
- 加載進圖片后,占用的內(nèi)存歸系統(tǒng)管理,我們無法管理
- 相同的圖片,圖片不會重新加載
- 加載到內(nèi)存中后,占據(jù)內(nèi)存空間較大
使用 imageWithContentOfFile:加載圖片
- 加載到內(nèi)存中后,占據(jù)內(nèi)存空間比較小
- 相同的圖片會被重復加載到內(nèi)存中
- 對象銷毀的時候,加載到內(nèi)存中得圖片會被一起銷毀
結(jié)論:如果圖片較小,頻繁使用的圖片,使用imageNamed來加載圖片(如按鈕圖片、主頁圖片、展位圖)
如果圖片較大,使用次數(shù)少,建議使用imageWithContentOfFile來加載圖片(相冊、版本新特性)
61、為什么assign不用用于修飾對象?
首先我們需要明確,對象的內(nèi)存一般被分配到堆上,基本數(shù)據(jù)類型和oc數(shù)據(jù)類型的內(nèi)存一般被分配在棧上。
如果用assign修飾對象,當對象被釋放后,指針的地址還是存在的,也就是說指針并沒有被置為nil,從而造成了野指針。因為對象是分配在堆上的,堆上的內(nèi)存由程序員分配釋放。而因為指針沒有被置為nil,如果后續(xù)的內(nèi)存分配中,剛好分配到了這塊內(nèi)存,就會造成崩潰。
而assign修飾基本數(shù)據(jù)類型或oc數(shù)據(jù)類型,因為基本數(shù)據(jù)類型是分配在棧上的,由系統(tǒng)分配和釋放,所以不會造成野指針。
62、id類型的指針為什么可以指向任意類型?
id是一個比較靈活的對象指針,并且是一個指向任何一個繼承自Object(或者NSObject)類的對象,而在cocoa的開發(fā)環(huán)境里,NSObject是所有類的根類,所以id可以指向任何一個cocoa的合法對象。
typedef struct objc_object {
Class isa;
} *id;
id和NSObject的區(qū)別:
NSObject是一個靜態(tài)數(shù)據(jù)類型,id是一個動態(tài)數(shù)據(jù)類型,默認情況下所有的數(shù)據(jù)類型都是靜態(tài)數(shù)據(jù)類型。
63、load和initalize
+ (void)load;
1.對于加入運行期系統(tǒng)的類以及分類,必定會調(diào)用此方法,且僅調(diào)用一次。
2.iOS會在應用程序啟動的時候調(diào)用load方法,在main函數(shù)之前調(diào)用
3.執(zhí)行子類的load方法前,會先執(zhí)行所有超類的load方法,順序為父類->子類->分類
4.load方法不遵從繼承規(guī)則,如果類本身沒有實現(xiàn)load方法,那么系統(tǒng)就不會調(diào)用,不管父類有沒有實現(xiàn)
5.盡可能的精簡load方法,因為整個應用程序在執(zhí)行l(wèi)oad方法時會阻塞,即,程序會阻塞知道所有的load方法執(zhí)行完畢,才會繼續(xù)
7.load方法中最常用的就是方法交換 method swizzling
+ (void)initialize;
1.在首次使用該類之前有運行期系統(tǒng)(非人為)調(diào)用,且僅調(diào)用一次
2.惰性調(diào)用,只有當程序使用相關類時,才會調(diào)用
3.如果類未實現(xiàn)initialize方法,而其超類實現(xiàn)了,那么會運行超類的實現(xiàn)代碼,且會運行兩次,且第一次打印出來是父類,第二次打印出來是子類
4.initialize遵循繼承規(guī)則
5.初始化子類的時候會優(yōu)先初始化父類,然后調(diào)用父類的initialize方法,而子類沒有覆寫initialize方法,因此會再次調(diào)用父類方法
64、深入解構objc_msgSend函數(shù)的實現(xiàn)
通常情況下每個OC對象的最開始處都有一個隱藏的數(shù)據(jù)成員isa,isa保存有類的描述信息(包含方法數(shù)組列表和緩存)
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class
OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols
OBJC2_UNAVAILABLE;#endif
} OBJC2_UNAVAILABLE;
通過isa指針,去objc_cache里面查找是否有緩存的方法,如果有,則直接調(diào)用,如果沒有則去objc_method_list里面去尋找對應的方法的實現(xiàn),如果再找不到,就進入到消息轉(zhuǎn)發(fā)的階段了。
65、AFNetworking3.0后為什么不再需要常駐線程?
AF2.x為什么需要常駐線程:
AF2.x 首先需要在子線程去start connection,請求發(fā)送成功后,所在的子線程需要?;钜员WC正常接收到NSURLConnectionDelegate回調(diào)方法。如果每來一個請求就開辟一條線程,并且?;罹€程,這樣開銷就太大了。所以只需要?;钜粭l固定的線程,在這個線程里發(fā)起請求,接收回調(diào)。
AF3.x為什么不需要常駐線程?
NSURLSession發(fā)起的請求,不再需要在當前線程進行代理方法的回調(diào),可以指定回調(diào)的delegateQueue,這樣我們就不用為了等待代理回調(diào)方法而苦苦保活線程了。
作為一個開發(fā)者,有一個學習的氛圍跟一個交流圈子特別重要,這是一個我的iOS開發(fā)交流群:130595548,不管你是大牛還是小白都歡迎入駐 ,讓我們一起進步,共同發(fā)展!(群內(nèi)會免費提供一些群主收藏的免費學習書籍資料以及整理好的幾百道面試題和答案文檔?。?/strong>