一、圖片縮放功能的實現
1.需要一個scrollview或者繼承于scrollview的視圖(如collectionview)。
2.實現代理方法- (UIView*)viewForZoomingInScrollView:(UIScrollView*)scrollView{return 需要縮放的view}
3.使imgView始終處于視圖中心
- (void)scrollViewDidZoom:(UIScrollView*)scrollView
{
? ? self.imgView.center = CGPointMake(self.view.center.x, self.view.center.y);
}
二、零散知識
1.自動布局無法獲取view的frame時,調用以下兩個方法
? ? [redView setNeedsLayout];
? ? [redView layoutIfNeeded];
2.移除控制器
? ? NSMutableArray *vcArray = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
? ? intindex = (int)[vcArrayindexOfObject:self];
? ? [vcArrayremoveObjectAtIndex:index];
? ? [self.navigationController setViewControllers:vcArray];
3.@synthesize關鍵字
synthesize語意為如果你沒有手動實現setter和getter方法,系統會自動給你添加以上兩個方法。一般的使用場景為當你想同時重寫一個屬性的setter和getter方法的時候,需要定義@synthesize var = _var;
4.@dynamic關鍵字
dynamic告訴編譯器,屬性的setter和getter方法由用戶自己生成,不自動生成。
比如當你定義了一個屬性:@property (nonatomic,strong) UIColor *textColor;
如果你要自己生成setter和getter方法,你要定義?@dynamic textColor并且定一個實例變量_textColorTow要在存取方法中用到;然后實現:
- (UIColor*)textColor
{
? ? if (_testColor2) {
? ? ? ? _testColor2 = [UIColor redColor];
? ? }
? ? return _testColor2;
}
- (void)setTextColor:(UIColor*)textColor
{
? ? _testColor2= textColor;
}
dynamic一般用不到,會增加代碼量
5.Objective-C是一門什么樣的額語言?
Objective-C作為一種動態消息型語言,它將數據類型的確定工作推遲到了運行期間來執行,并且它調用方法的實質是向對象發送消息,根據selector在對象的本類以及父類的方法列表里找,如果找不到就啟動消息轉發機制。
6.什么是多態
相同類型調用同一個方法呈現不同的行為特征就是多態。
當子類對象直接賦值給父類指針變量,父類 *p = [子類 new];運行類型hi父類編譯類型是子類,所以p不能調用子類單獨實現的方法,如果子類重寫了父類方法,p調用的方法將會是子類重寫的方法
7.OC和Swift的區別
(1).import的類
OC:某個只要要使用某個類就要將該類import。?
swift:如果是用戶自己創建類,其他類無需import可以直接使用。pod的一些三方類和系統的一些類,在使用的時候需要import
(2)Swift沒有地址和指針的概念
(3)
8.調用系統聲音和震動
(1)引入#import?<AudioToolbox/AudioToolbox.h>
(2)? ? ? ? AudioServicesPlaySystemSound(1007); //系統的通知聲音
? ? ? ? AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);//震動
9.NSCache
NSCache是一個繼承NSObjec的可變集合,是蘋果提供的一套緩存機制,用鍵值(key-value)對來臨時存儲只需要短暫存儲在內存中的數據,并且當內存空間很少的時候會可以自動釋放一些資源。
10.setNeedsDisplay和setNeedsLayout
首先兩個方法都是異步執行的。而setNeedsDisplay會調用自動調用drawRect方法,setNeedsLayout會默認調用layoutSubviews.setNeedDisplay方便繪圖,setNeedsLayout方便重新布局
11.layoutSubviews什么情況下會被調用
(1)初始化frame
(2)addSubview
(3)滾動一個UIScrollView會觸發layoutSubviews。
(4)直接調用setLayoutSubviews
12.成員變量和屬性
成員變量不會自動生成set和get方法,需要自己手動實現。成員變量不能用點語法調用,因為沒有set和get方法,只能使用->調用。
屬性會自動生成set和get方法。屬性用點語法調用,點語法實際上調用的是set和get方法。
13.Int和Integer的區別
NSInteger與int的區別是NSInteger會根據系統的位數(32or64)自動選擇int的最大數值(int or long)
Integer和NSUInteger的區別是后者是無符號的,沒有負數,前者有符號
14.Category
分類一般用于給現有系統類或者自定義類添加方法,以減少類的代碼量,比如我們要計算label的寬高,可以給UILabel類添加一個分類,里面定義兩個類方法,返回值為寬度和高度。
分類的特點:
(1)分類是用于給原有類添加方法的,因為分類的結構體指針中,沒有屬性列表,只有方法列表
(2)如果分類中有和原有類同名的方法, 會優先調用分類中的方法, 就是說會忽略原有類的方法,同名方法調用的優先級為?分類 > 本類 > 父類;
(3)如果要給分類添加屬性,需要用到runtime的關聯對象,使用關聯對象添加屬性。比如我以前寫過一個簡單的下拉刷新控件,要給繼承于Scrollview的類添加一個方法,還要給這些類加屬性,比如header footer等等。
15.分類和繼承的區別
(1)繼承
?a. 當需要擴展的方法與原方法同名時,并且需要調用父類的同名方法,則需要繼承。若此時使用分類,則分類的方法的實現會覆蓋原方法的實現,不會訪問到原方法。
? b. 當需要擴展屬性時。
(2)分類
a. 針對系統的一些類進行擴展。例如,NSString, NSArray, NSNumber等類,系統本身不提倡使用繼承去擴展方法,因為這些類內部實現對繼承有所限制,所以最好用分類的方式擴展。
b.類別支持開發人員針對自己構建的類,把相關的方法分組到多個單獨的文件中,針對大型而復雜的類,可以提高維護性和可讀性,并簡化單個源文件的管理。
16.isa指針的作用
(1)當我們調用某個對象的對象方法的時候,它會首先在自身isa指針指向的objc_class的methodLists中查找該方法,如果找不到的話就會通過objc_class的super_class指針找到其父類,然后從父類的methodLists中查找該方法,如果仍然找不到,則繼續通過super_class向上一級結構體中查找,直至根class
(2)當我們調用某個類方法的時候,它會首先通過自己的isa指針找到metaclass元類,并從其methodLists中查找該類方法,如果找不到則會通過metaclass的super_class指針找到父類的metaclass結構體,然后接著找。
(3)有個細節是運行的時候編譯器會將代碼轉化為objc_msgSend(obj,@selector(test)),在class中先去cache中查找SEL查找對應method,如果cache里面沒有找到,那就去methodLists中查找。
17.如何避免循環引用
(1)block引發的循環引用,在block中使用對自身對象的弱引用來替換self,__weak typeof(self) weakSelf = self
(2)delegate引發的循環引用,對代理使用弱引用
(3)使用了NSTimer沒有銷毀,定時器對當前控制器持有強引用,如果定時器不銷毀,則控制器無法釋放。解決方法位在viewWillDisappear或者viewDidDisappear或者其他原因離開該控制器的方法中銷毀定時器
18.建立一次TCP協議需要三次握手
(1)第一次握手,建立連接時,客戶端發送syn包到服務器,并進入syn_sent狀態,等待服務器確認。
(2)第二次握手,服務器收到syn包,同時自己也發送一個syn+ack包,等待客戶端確認
(3)第三次握手,客戶端收到syn+ack包,向服務器發送確認包ack,此包發送完畢客戶端和服務器進入ESTABLISHED狀態,tcp連接成功,完成三次握手
四次揮手:
(1)第一次客戶端請求關閉客戶端到服務端的連接,這時客戶端就要發送一個FIN=1,表示要關閉連接。
(2)第二次服務端收到后需要確認一下,返回ACK=1,并且帶上自己的序列號,服務端進入了CLOSE-WAIT狀態,TCP服務區通知高層的應用進程,客戶端向服務端的方向就釋放了,這時候處于半關閉狀態,即客戶端已經沒有數據要發送了,但是服務端發送的數據客戶端還是要接受。
(3)第三次第二次揮手完畢后之關閉了客戶端向服務端的方向,另一個方向也需要關閉,所以服務端也向客戶端發送FIN=1 ACK=1
(4)第四次客戶端收到并且發送ACK=1,一次TCP連接結束
19.loadView viewDidLoad?viewDidUnload
loadView方法負責創建Controller的view,view創建完畢后調用viewDidLoad,應用程序占用的內存過多的話,系統會對應用發出內存警告,Controller就會收到didReceiveMemoryWarning,如果當前UIViewController的view不在應用程序的視圖層次結構(View Hierarchy)中,即view的superview為nil的時候,就會將view釋放,并且調用viewDidUnload方法。
viewDidUnload和dealloc方法的區別在于dealloc是在控制器被釋放的時候調用的,而viewDidUnload在收到警告時調用,并且只是釋放了view
20.內存分區情況
(1)堆,堆是存放進程運行中被動態分配的內存段,它的大小不固定,可以動態擴張或者縮減,當進程調用alloc
函數的時候新分配的內存就被添加到堆上面,release后就被釋放。
(2)棧,棧是用戶存放程序臨時創建的局部變量,也就是說函數括弧中的變量,當函數執行完畢,棧區的數據被清理
21.iOS系統架構
(1)UI層,為應用程序開發提供了各種常用的框架并且大部分框架與界面有關,本質上來說它負責用戶在iOS設備上的觸摸交互操作。最常用的就是UIKit
(2)媒體層,提供應用中視聽方面的技術,如圖形圖像相關的CoreGraphics,CoreImage,GLKit,OpenGL ES,CoreText,ImageIO等等。聲音技術相關的CoreAudio,OpenAL,AVFoundation,視頻相關的CoreMedia,Media Player框架,音視頻傳輸的AirPlay框架等等
(3)核心服務層,提供應用所需要的基礎的系統服務,比如Accounts賬戶框架、CoreData數據存儲框架等等
(4)操作系統核心,包含大多數低級別接近硬件的功能,它所包含的框架常常被其它框架所使用。常用的就是CoreBluetooth框架利用藍牙和外設交互。
22.視圖生命周期
(1)loadView,控制器被創建首先調用loadView
(2)viewDidLoad,視圖加載完成后調用此函數,一般在此方法中添加一些控件,設置視圖的初始屬性,類似于初始化
(3)viewWillAppear,即將加載到窗口時調用此方法。一般在此方法做一些較為耗時的。這樣就可以先顯示基本的視圖,呈現給用戶(讓用戶感覺不是那么卡),然后再顯示比較耗時的。以免顯示一個白屏給用戶
(4)viewDidAppear,視圖已加載完畢
(5)viewWillDisappear
(6)viewDidDisappear
23.App項目的生命周期
執行main函數 -> 調用UIApplicationMain函數 -> 初始化UIApplication對象并為他設置代理對象 -> 程序退出
UIApplication代理對象:
(1)applicationDidFinishLanching,程序載入后
(2)applicationWillResignActive 將要進入非活動狀態
(3)applicationDidBecomeActive程序進入活動狀態
(4)applicationDidEnterBackground程序進入后臺
(5)applicaitonWillEnterForeground程序將要進入前臺
(6)applicationDidReceiveMemoryWarning內存警告,程序將要終止
(7)applicatioWillTerminate程序將要結束
24.Cocoa Touch提供了哪幾種Core Animation過度類型
過渡動畫通過?type?設置不同的動畫效果,?CATransition?有多種過渡效果, 但其實?Apple?官方的SDK只提供了四種:fade(淡出)、movein(覆蓋原圖)、push(推出)、reveal(底部顯示出來),私有api提供了替他很多非常炫的動畫,如cube、pageCurl
25.UIView和CALayer有什么區別
最明顯的區別UIView繼承與UIResponder,可以響應事件,CALayer繼承與NSObject,不能響應事件.
兩者的關系:每一個view內部都有一個CALayer在背后提供內容的繪制和顯示,并且view的尺寸都由內部的layer提供,兩者都有樹狀層級結構,layer內部有sublayer,view內部有subview.
26.什么時候用Delegate?什么時候用Notification?
delegate是一種一對一的設計模式,誰遵循代理協議,誰才可以調用代理方法。一般在自定義控件的中常用delegate,比如UITableView,UICollectionView、UITextField等都有代理協議,你要實現一個列表就要遵循tableview的uitableviewdelegate。notification采用的是單例設計模式,是一種多對多的設計模式,因為實現一個delegate協議的代碼比較繁瑣,尤其是向多個對象同時傳值的時候delegate的易用性就比較差了,所以向多個對象發送消息或者傳值的時候通知比較好用。
27.如何定義一個delegate
(1)創建代理
@protocol TestDelegate ?<NSObject>
- (void)loadNewData;
@end
(2)聲明屬性
@property(nonatomic,weak) id<TestDelegate>delegate
(3)遵循代理,并實現代理方法
28.如何定義一個NSNotification
發送通知 -> 接受通知 -> 頁面消失的方法里移除通知
29.全局變量和局部變量在內存中是否有區別?如果有,是什么區別?
有區別,全局變量保存在內存的全局存儲區中,占用靜態的存儲單元。局部變量保存在棧中,只有在所在函數被調用的時候才會動態的為變量分配存儲單元。
全局變量聲明在函數外面,可以跨文件訪問也可以在聲明的時候賦上初始值。
30.static關鍵字的作用
當static修飾局部變量時可以延長局部變量的生命周期,如果局部變量不佳static那么當該函數結束的時候局部變量的內存就會被釋放,如果加static那么該局部變量將一直存在于全局靜態存儲區里面,知道控制器銷毀該變量才會被回收。statis關鍵字修飾的局部變量只會初始化一次,內存地址也只有一份,
static修飾全局變量的時候可以把全局變量的作用域縮小到當前文件,保證外部類無法訪問。
31.extern關鍵字的作用
在A類里面定義一個全局變量NSInteger age = 10,如果B類沒有引入A類頭文件時想得到A類的age變量,就可以使用extern關鍵字,extern NSInteger age;
32.const關鍵字的作用
const修飾的變量的讀寫權限變為只讀,
33.Volatile關鍵字的作用
volatile告訴編譯器他修飾的變量是隨時可能發生變化的,每次使用他的時候必須從i的地址中讀取。
34.iOS進程間通信的幾種途徑
(1)URL Scheme
這個事iOS通信最常用到的通信方式,App1通過openURL的方法跳轉到App2,并且在URL上帶上想要的參數,有點類似于http中get請求那樣進行參數傳遞。使用方法為在App1的info.plist中配置LSApplicationQueriesSchemes,指定App2的scheme。典型的使用場景就是各開放平臺SDK的分享功能,比如分享到微信朋友圈微博等等。
(2)UIPasteboard
剪切板功能,每個app都能使用剪切板功能,可以通過這一功能進行進程間的通信。最典型的就是淘寶。
(3)UIDocumentInteractionController
UIDocumentInteractionController主要用來實現同設備上app之間的共享文檔,以及文檔預覽、打印、發郵件和復制功能
(4)AirDrop
通過AirDrop實現不同設備的App之間文檔和數據的分享
(5)UIActivityViewController
iOS SDK中封裝好的類在App之間發送數據、分享數據和操作數據
35.進程死鎖的原因
所謂死鎖,通常指有兩個線程T1和T2都卡住了,并等待對方完成某些操作。T1不能完成是因為它在等待T2完成,但是T2也不能完成,因為它在等待T1完成。于是大家都完不成,所以就造成了死鎖。具體原因就是在一個串行隊列的任務里,再向這個隊列同步添加任務。我是這樣理解的,比如我現在有一個viewdidload方法,在viewdidload方法里使用dispatch_sync函數給主隊列添加一個同步任務,現在將viewdidload看作是任務一,dispatch_sync函數是任務二,任務二只有等任務一執行完畢之后才能執行,這是同步隊列的特點,但是任務二是一個同步任務,必須等待任務二執行完畢之后才能執行其他任務,因此造成互相等待的死鎖。
死鎖的處理:將任務放到異步隊列中執行,異步隊列也是先進先出的規則,但是異步隊列并不會阻塞線程,任務出隊列是依據其執行時間來確定的。
36.簡述單例
單例對象即如果你不主動銷毀這個對象,這個單例對象一直存在在內存中,并且這個單例對象在內存中都是同一個地址。登錄模塊使用單例。此外,一些其他的設計模式也依托于單例,比如通知NSNotificationCenter ? NSUserDefaults等等
+ (LXFMDBTest*)manager
{
? ? staticdispatch_once_tonceToken;
? ? staticLXFMDBTest*manager;
? ? dispatch_once(&onceToken, ^{
? ? ? ? manager = [[LXFMDBTestalloc]init];
? ? });
? ? returnmanager;
}
37.簡述@selector的作用
SEL為方法選擇器,@selector是取得一個SEL指針,方法選擇器是一個char *指針,標示它所代表的是方法的名字。簡單來說,@selector就是用字符串表示某個類的某個方法。
38.多繼承的實現(通過消息轉發)
消息轉發是Runtime的黑魔法,其中一個用處就是可以實現多繼承,當發送的消息找不到對應的方法的時候,會經過如下過程:
1.動態方法添加:通過resolveInstanceMethod方法動態添加方法
2.直接消息轉發:不修改原本方法簽名,直接檢查forwardingTargetForSelector是否實現,若返回nil并且非self,則向該返回對象直接轉發消息,前提是該類沒有與轉發對象同名的方法
3.標準消息轉發:先處理方法調用再轉發消息,重寫methodSignatureForSelector:和forwardInvocation:方法,前者為該消息創建一個合適的方法簽名,后者則將該消息轉發給其他對象。
第二種實現方式如下圖
第三種實現方式如下圖:
39.列舉幾種進程同步機制并比較優劣
(1)信號量,用于多線程同步的,根鎖不一樣的是,信號量不一定是鎖定某一資源,而是流程上的概念,比如有A、B兩個線程,B線程一定要等到A線程完成某一任務時候才能進行,這個任務并不是鎖定某一資源,而對于鎖來說,鎖住的資源無法被其余的線程訪問,從而阻塞線程實現線程同步。
信號量主要有三個函數,分別為dispatch_semaphore_create(M)(創建一個值為M的信號量)、dispatch_semaphore_wait()(如果信號量的值大于0,則使其信號量的值-1,否則阻塞線程到該信號量的值大于0或者超過等待時間)、dispatch_semaphore_signal(使得該信號量+1)。
(2)自旋鎖,自旋鎖是專為防止多處理器而引入的一種鎖,和互斥鎖類似,都是為了保證線程安全的鎖,但是二者的區別不一樣,對于互斥鎖來說,當一個線程獲得這個鎖之后,其他想要獲取該鎖的線程就會被阻塞,直到該鎖被釋放,但是自旋鎖不一樣,當一個線程獲得鎖之后,其他線程會一直循環在那里查看該鎖是否被釋放,所以,該鎖比較適用于鎖的持有者保存時間比較短的情況下。
(3)互斥鎖,
40.自動釋放池***
通常情況下我們是不需要手動添加autoreleasepool的,使用線程自動維護的autoreleasepool就可以了,但有一些情況下我們需要自動添加,比如循環中創建了大量的臨時對象,這些臨時對象可能長時間都不會被釋放,一直在內存中保留,那么內存的峰值就會一直增加,但是其實這個臨時變量我們不再需要了,這個時候可以創建自動釋放池來縮短臨時變量的生命周期來降低內存的峰值。
底層實現:源碼中有一個結構體__AtAutoreleasePool,結構體由objc_autoreleasePoolPush() 和 objc_autoreleasePoolPop() 這兩個方法構成,在這兩個方法里有一個類叫AutoreleasePoolPage,實質是一個雙向鏈表,page就是鏈表的一個節點。自動釋放池的工作過程就是先調用push函數,當page不為空那么就添加對象到自動釋放池中。釋放工作交給pop函數。
41.OC優缺點
優點:(1)category,非常實用的擴展機制,可以很方便的為一個已有的類添加屬性和方法。
(2)運行時的概念。
42.為什么說OC是一門動態語言
動態語言是指程序在運行時可以改變結構,可以添加新的函數、屬性或者刪除已有的函數、屬性。這是一點,另外一點就是動態語言類型的檢查實在運行時做的,OC可以用runtime動態偉類添加方法、屬性,比如我有一個Person類,里面只有一個name屬性,但是我可以在其他地方為這個類添加age、gender等其他屬性。OC的動態性主要體現在三個方面,一是動態類型,比如id類型,到了運行時才決定接受者。二是動態綁定,讓代碼在運行時判斷需要調用什么方法,而不是在編譯的時候。三是動態載入,在程序運行時才去決定調用哪些代碼和資源,而不是啟動的時候就家在所有。
靜態類型的語言的類型判斷是在編譯階段判斷的。
43.通知和協議的不同之處***
44.響應鏈
響應者對象,iOS中只有繼承UIResponder的對象才能處理事件,我們叫他響應者對象。用戶觸摸屏幕,會產生一個觸摸事件,操作系統會將該觸摸事件加入到UIApplication管理事件隊列,UIApplication會從事件隊列中取出最前面的事件,并將事件分發下去處理,會將事件首先交給UIWindow,UIWindow將事件下發,即UIWIndow的UIView,UIView首先看自己能否處理事件以及觸摸點是否在自己身上,如果不在自己身上那么繼續尋找子視圖,遍歷子控件,重復進行判斷能否處理事件,如果沒有找到,那么自己就是事件處理者,如果自己不能處理,那么不做任何處理。這一尋找的過程,叫做事件響應鏈。
45.frame和bounds的區別
frame的坐標x y是相對于父控件而言的,bounds的x y相對于其自身而言的,自己的bounds的x y都是(0 0)
46.方法和選擇器的區別
Selector是一個對象中選擇方法來執行的名字,Selector本身并不能做任何事情,它簡單的標識了一個方法,通過它可以找到方法。方法是相對于對象來說,包含方法名稱和實現。
47.OC的垃圾回收機制
MRC下面是手動引用計數,ARC下面是自動引用計數。當對象的引用計數等于0的時候該對象被銷毀。
48.代理為什么要用weak修飾?
比如我現在有兩個類ClassA和ClassB,ClassA中定義了一個代理delegate,該delegate使用strong修飾。那么在B中初始化A并且讓B遵守A的協議,因為strong修飾符是強引用,會使對象的引用計數加1,這個時候B持有A,A也持有B,會造成循環應用。如果使用weak就不會出現這種情況,因為weak是弱引用,對對象的引用計數不會造成影響。
49.weak實現原理
Runtime維護了一個weak表,key是所指對象的地址,value是weak指針的地址數組。weak的實現原理可以概括為三步:
1.初始化時:runtime會調用objc_initWeak函數,初始化一個新的weak指針指向對象的地址
2.添加引用時:objc_initWeak會調用objc_storeWeak函數,objc_storeWeak的作用是更新指針指向,創建對應的弱引用表
3.釋放時:調用clearDeallocation函數,clearDealocation函數首先根據對象地址獲取所有weak指針地址的數組,然后遍歷這個數組把其中的數據設為nil,最后把這個entry從weak表中刪除,最后清理對象的記錄
50.有哪些NSObject的方法
isKindOfClass和isMemberOfClass
51.UIView的動畫效果有哪些
位移、大小、透明度
52.深拷貝和淺拷貝的區別
淺拷貝就是對內存地址的拷貝,讓目標對象指針和源對象指向同一片內存,使對象的引用計數加1,可以理解為創建了一個指向原對象的新指針而已,并沒有創建一個新的對象,也就是說沒有開辟新的內存。
深拷貝是拷貝對象的具體內容,內存地址是自主分配的,拷貝后兩個對象的值是一樣的,但是內存地址不一樣,兩個對象的互不影響,所以也不涉及到增加引用計數的問題。
深拷貝是內容拷貝,淺拷貝是指針拷貝,本質區別在于是否開辟新的內存,是否影響內存地址的引用計數。
如果一個對象使用淺拷貝,拷貝出來的對象一定是不可變的,比如一個NSString對象使用copy,那么拷貝出來的對象就算定義為NSMutableString對象該對象也是不可變的,如果執行可變操作程序就會崩潰,比如該NSMutableString執行appendString操作就會崩潰。但是如果使用深拷貝,那么拷貝出來的對象就是可變的。
53.字符串為什么要用copy修飾
首先要理解一個概念,copy是淺拷貝,淺拷貝就是指針拷貝,讓目標對象指針和源對象的指針指向同一片內存,但是copy也并不是一定不會開辟新的內存,比如說當我copy一個不可變得對象的時候那么一定是指針拷貝,但是當copy的對象為可變對象的時候,copy就會開辟一片新的內存,因為這樣做會比較安全,如果源對象的值被改變了,那么這個時候copy的對象就不會受影響,因為開辟了新的內存和原來的內存就沒有關系了。我寫過一個demo,定義一個由copy修飾的字符串屬性比如它叫text,然后在函數中定義一個可變字符串比如叫mutableText,這個時候我執行self.text = mutableText的操作時候,輸出self.text和mutableText的對象地址,會發現兩個地址不一樣,所以這個時候copy應該是內容拷貝。然后我將text的修飾符換為strong我發現輸出的兩個地址是一樣的,這個時候我如果修改mutableText值得時候,輸出兩個字符串的值發現self.text的值也被修改,因為他們的內存地址是一樣的,這樣就不具備安全性。其實總結一句話就是copy出來的不管是否開辟了新的內存,他就是不可變的,也不存在值被修改的情況,也不存在安全性的問題。
54.基于Socket聊天室實現流程
a.首先進行服務器請求,獲得直播流地址和房間id,請求成功后使用socket與與服務器連接,并且發送一些參數參數比如用戶名、用戶id等等發送給服務器告訴服務器連接成功,連接地址是寫死的,url+端口號。
b.在Socket的delegate方法didReceiveMessage里面去執行與服務器交互的一些操作,比如給服務器發送心跳、用戶在聊天室發送彈幕的權限等等。
c.退出房間斷開socket連接
55.題庫圖文混排實現過程
a.比如說一道題里面服務器給我一段html的富文本,html里面會有一些自定義的標簽,我通過正則表達式找到標簽,比如latex小圖、image大圖、underline下劃線等等,像這些公式都是圖片,每一道圖的圖片都是服務器給我的一個zip包,我將zip包解壓后存到本地,然后根據圖片名去本地文件夾里面找圖片,找到圖片以后根據得到的range確定圖片的顯示位置。
b.使用的是一個叫TYAttributelabel的第三方庫實現的,它是基于coretext實現的。
c.題庫實現的一個關鍵點是我們在加載的時候做了優化,比如有的資料會有很多道題,因為zip包是一次性返給我的,如果我將這個資料的所有zip包都解壓完存到本地再展示頁面那么這個時候用戶等待的時間就太長了,我們的加載模式是一次只加載這道題本身以及前后各兩道題,這樣就不會造成資源浪費。實現的過程就是這是一個collectionview,我回去監聽它本身以及前后各兩道題是否加載過了,如果加載過了那么久不去加載,如果沒有加載就去解壓zip包。
56.視頻解密過程
將服務器給我的key先進行base64解碼得到一個NSData數據,然后用根據這個NSData數據和key使用AES解密的得到解密后的字符串,將這個字符串給播放器
57.Cocoapods工作流程及原理
pod install
a.當我執行命令pod install --verbose的時候可以看到pod install詳細的輸出信息,它首先有一個操作叫Finding Podfile changes,它去podfile這個文件里面查看有哪些變化,如果有更新信息,接下來會在在CocoaPods/Pods/Release文件夾下面執行git clone操作,將這個第三方庫從github上clone下來,然后copy到項目的pods文件夾下面。
b.將文件clone下來后會執行Generating Pods project的操作,它會自動在你項目目錄下面創建一個project,然后形成一個workspace。
pod update
a.在cocoapods的本地git倉庫的master分支執行fetch+merge操作,master分支下的specs文件夾里存的都是spec配置文件。同時update檢查更新cocoapods。
b.fetch+merge后會將本地沙盒里的podfile信息和本地倉庫里的spec文件做比較,去判斷是否需要更新第三方庫。
podfile.lock文件的作用
這個文件最大的用處在于多人開發,cocoapods的podflie文件如果沒有指定第三方版本的話那么會獲取該第三方最新的版本,當團隊中某個人執行完pod install命令以后,生成的podfile.lock就記錄下最新第三方庫的版本。這時候如果其他成員pull下來包含這份podfile文件的代碼,獲取的也是最新版本的第三方庫代碼,但如果其他成員執行pod install的時候給這個第三方指定版本了的話,那么一個團隊里就會有不同版本的第三方,但是.lock文件會阻止他做這件事。
58.View和layer的區別
(1)首先,最明顯的區別是view可以響應事件,layer不可以,UIKit使用UIResponder作為響應對象,來響應系統傳遞過來的事件并進行處理。UIApplication、UIViewcontroller、UIView等UIKit類都直接或者間接的繼承于UIResponder類,UIResponder定義了各種處理事件和事件傳遞的接口,而CALayer直接繼承于NSObject,并沒有響應的事件處理接口。
(2)每個UIView內部都有一個CALayer在背后提供內容的繪制和顯示,并且UIView的尺寸樣式都由內部的layer提供,兩者都有樹狀層級結構,layer內部都sublayer,view內部有subview,layer比view多了一個AnchorPoint
(3)layer內部維護著三個layer tree,分別是presentlayer tree(動畫樹)、modeLayer tree(模型樹)、Render tree(渲染樹),我們修改動畫的屬性其實是在修改動畫樹的屬性,最終顯示在界面上其實依賴的是modelLayer
59.TCP和UDP的區別
UDP在傳送數據前不需要先建立連接,遠地的主機在收到UDP報文后也不需要給出任何確認,因為這樣省去很多開銷,使得它的速度比較快,比如一些對實時性要求比較高的服務常常使用UDP。
TCP提供面向連接的服務,在傳送數據前必須建立連接,數據傳送完成后要釋放連接,因為TCP是一種可靠的運輸服務,但是正因為這樣,不可避免的增加了很多開銷,對應的應用層協議有SMTP、HTTP、FTP
60.TCP是如何實現可靠傳輸的?
(1)TCP的每一端必須設有兩個窗口,一個發送窗口和一個接受窗口,TCP的可靠傳輸機制用字節的序號進行控制,TCP所有的確認都是基于序號而不是基于報文段
(2)發送過的數據未收到確認之前必須保留,以便超時重傳時使用。
(3)發送緩存用來暫時存放:發送應用程序傳送給發送方TCP準備發送的數據,TCP已發送但尚未收到確認的數據
(4)接收緩存用來暫時存放:按序到達的、但是尚未被接收應用程序讀取的數據
61.load方法
load可以說是調用時間最靠前的方法,在主函數運行之前就會調用。調用順序為父類優先于子類調用,類優先于分類調用。它的調用不是惰性的,在程序調用期間只調用一次,最重要的是,如果在類和分類中都實現了load方法,它們都會調用,不會像其他的在分類中實現的方法被覆蓋,這就使load成為方法調劑的絕佳時機。但是由于load方法的運行時間過早,如果要調用其他類的方法這里并不是一個理想的環境,因為在當前類并不能確定其他類已經加載了。這個時間點所有framework都已經加載到了運行時中,所以調用framework中方法是安全的。
62.Runtime替換方法
1.一般會在load方法里替換系統方法,因為load方法在main函數之前執行,這樣可以確保類初始化之后執行的是已經被替換過的方法。在load方法里實現替換操作一般只能替換系統方法,不建議替換自定義的方法,因為在load方法執行的時候自定義的方法不一定被添加到runtime中去,我寫過一個demo,在一個UIViewController類中自定義一個方法比如叫methodExchange方法,同時我定義一個UIViewController的分類,在分類的load方法里輸出這個類的methodList,發現并沒有這個自定義方法。
63.靜態庫和動態庫
什么是庫:庫是程序代碼的集合,是共享程序代碼的一種方式,根據源代碼的公開情況,庫可以分為兩種類型,開源庫,公開源代碼,能看到具體實現,比如SDWebImage,閉源庫不公開源代碼,是經過編譯后的二進制文件,看不到具體實現,主要分為靜態庫和動態庫。
(1)存在形式
靜態庫:以.a和.framework為文件后綴名(.a是一個純二進制文件,frameword=.a + .h + sourcefile,所以創建靜態庫最好還是用.framework的形式)
動態庫:以.tbd和.framework為文件后綴名,系統直接提供給我們的都是動態庫
(2)區別
靜態庫和動態庫是相對于編譯期和運行期的,靜態庫在編譯時會被鏈接到目標代碼中,動態庫在編譯期間不會被鏈接到目標代碼中,只是程序運行時才會被載入,因為在程序運行期間還需要動態庫的存在。
64.NSCache理解
iOS中需要頻繁讀取的數據,都可以用NSCache把數據緩存到內存中提高讀取性能,SDWebImage緩存圖片就是用的這個類。是系統提供的類似于集合的緩存類,它和集合的不同之處在于NSCache有自動刪除的功能,以減少系統占用的內存;NSCache是線程安全的,不需要加線程鎖;
屬性:totalCostLimit,設置緩存占用的緩存大小,并不是一個嚴格的限制,當緩存大小超過了設定的值得時候系統會清除一部分內存,直至緩存內存小于所設的值
countLimit,緩存數量,如果超過數量會清除一部分緩存,直至緩存數量小于等于所設數量。
代理方法willEvictObject,緩存被清理的時候調用該方法
65.內存區域分布
代碼區、常量區、全局靜態區、堆、棧
代碼區:用來存放程序執行代碼的一塊內存區域,這部分區域的大小在程序運行前就已經確定,這塊區域通常屬于只讀。
常量區:存儲常量的區域,程序結束后釋放
全局靜態區:包含全局變量和靜態變量,程序結束后系統釋放。
堆:就是那些由new alloc創建的對象分配的內存塊,ARC下會在當前線程Runloop退出或者休眠的時候銷毀這些對象,MRC需要程序員自己釋放。
棧:創建臨時變量時由編譯器自動分配,在不需要的時候自動清除的變量的存儲區,函數執行完畢后銷毀。
66.加載一個頁面需要多長時間,如何優化一個頁面
從loadview到viewdidappear輸出當前時間,發現從loadview到viewdidappear需要將近0.02毫秒。
優化建議:將關鍵且耗時比較少的操作放到viewdidload里面,將必要的UI渲染放到viewwillappear,復雜運算放到viewdidappear,一些觸發加載的做懶加載或者延時加載
67.MJExtention源碼解析
(1)最核心的類是一個NSObject的分類叫做MJKeyvalue,字典轉模型的方法都在該類中,其中最核心的方法就是將字典中的值賦值給模型中的屬性,這個方法叫做mj_setKeyValues,參數為id對象。
(2)一進來會將這個id對象轉換為字典,然后通過一個封裝的方法獲取Model類及其繼承鏈中的父類的所有屬性,取得屬性列表的核心方法是runtime提供的函數class_copyPropertyList,然后遍歷獲取到的屬性列表,通過property_getName函數取得每一個屬性的名稱,通過property_getAttributes獲得每一個屬性所對應的類,之后會調用自定義的MJFoundation類的方法去判斷屬性所對應的類是否是Foundation框架下的類,比如NSString、NSArray,如果是Foundation下的類那就可以直接賦值,如果是自定義的類,也就是字典嵌套字典模型嵌套模型的那種情況,那么初始化這個自定義類然后再次調用mj_setKeyValues方法給這個自定義類的屬性賦值。循環結束返回這個Model類的實例對象,一次解析結束。
(3)其他核心類,MJProperty(屬性對應的類,這個類里面定義了一些屬性比如name:屬性名稱,type:枚舉,是foundation框架下的類還是自定義類,srcClass:屬性來源于哪個類,有可能是父類),NSObject的分類MJClass,主要用來獲得Model類繼承鏈中的所有父類。
68.離屏渲染
為圖層設置遮罩、圓角、陰影都會造成離屏渲染,離屏渲染會影響性能。
離屏渲染為什么消耗性能:
(1)GPU屏幕渲染分為On-Screen Rendering (當前屏幕渲染) 和Off-Screen Rendering (離屏渲染),當前屏幕渲染是在當前顯示的屏幕緩沖區進行,而離屏渲染需要在當前屏幕緩沖區外開辟一塊新的緩沖區進行渲染
(2)需要多次切換上下文,使用離屏渲染要從當前屏幕切換到離屏,離屏渲染結束后再切回到當前屏幕。
69.內存泄漏和野指針
內存泄漏:程序分配的內存未釋放或者無法釋放,造成系統內存的浪費,導致程序運行緩慢或者崩潰。造成內存泄漏的原因主要是對象沒有釋放、循環引用。block循環引用、delegate循環引用、大次數循環內存暴漲等。
野指針:如果內存已經被釋放,而指針還在引用原始內存,這樣的指針就叫做野指針。野指針不是nil指針,是指向垃圾內存的指針。