2020面試題彙總

2020不平凡的一年,無論是關於本身,還是關於生活,都不是好過的,再加上自己的人生低谷,無以復加,人道中年,真的也是雪上加霜。慢慢人生之路,不知何去何從,可我們沒法選擇我們自己的局,不管手中的牌是好是壞,只有兩種選擇,要不棄牌,要不就是盡力打好!

文章目錄

數據結構

1.數據結構的存儲一般分爲幾種?各有什麼特點

數據結構的存儲一般常用的有兩種 順序存儲結構 和 鏈式存儲結構

順序存儲結構:

比如,數組,1-2-3-4-5-6-7-8-9-10,存儲是按順序的。再比如棧和隊列等

鏈式存儲結構:

比如,數組,1-2-3-4-5-6-7-8-9-10,鏈式存儲就不一樣了 1(地址)-2(地址)-7(地址)-4(地址)-5(地址)-9(地址)-8(地址)-3(地址)-6(地址)-10(地址)。每個數字後面跟着一個地址 而且存儲形式不再是順序

2.集合結構 線性結構 樹形結構 圖形結構

  • 集合結構
    一個集合,就是一個圓圈中有很多個元素,元素與元素之間沒有任何關係 這個很簡單
  • 線性結構
    一個條線上站着很多個人。 這條線不一定是直的。也可以是彎的。也可以是值的 相當於一條線被分成了好幾段的樣子 (發揮你的想象力)。 線性結構是一對一的關係
  • 樹形結構
    做開發的肯定或多或少的知道xml 解析 樹形結構跟他非常類似。也可以想象成一個金字塔。樹形結構是一對多的關係
  • 圖形結構
    這個就比較複雜了,他呢 無窮。無邊 無向(沒有方向)圖形機構 你可以理解爲多對多 類似於我們人的交集關係

3.單向鏈表 雙向鏈表 循環鏈表

  • 單向鏈表
  • 雙向鏈表
  • 循環鏈表

4.數組和鏈表區別

  • 數組
    數組元素在內存上連續存放,可以通過下表查找元素;插入,刪除需要移動大量元素,比較適用於元素很少的情況
  • 鏈表
    鏈表中的元素在內存中不是順序存儲的,查找慢,插入、刪除只需要對元素指針重新賦值,效率高

堆、棧和隊列

  • 堆是一種經過排序的樹形數據結構,每個節點都有一個值,通常我們所說的堆的數據結構是指二叉樹。所以堆在數據結構中通常可以被看做是一棵樹的數組對象。而且堆需要滿足一下兩個性質:
    1.堆中某個節點的值總是不大於或不小於其父節點的值;
    2.堆總是一棵完全二叉樹。
  • 堆分爲兩種情況,有最大堆和最小堆。將根節點最大的堆叫做最大堆或大根堆,根節點最小的堆叫做最小堆或小根堆,在一個擺放好元素的最小堆中,父結點中的元素一定比子結點的元素要小,但對於左右結點的大小則沒有規定誰大誰小
  • 堆常用來實現優先隊列,堆的存取是隨意的,這就如同我們在圖書館的書架上取書,雖然書的擺放是有順序的,但是我們想取任意一本時不必像棧一樣,先取出前面所有的書,書架這種機制不同於箱子,我們可以直接取出我們想要的書。

  • 棧是限定僅在表尾進行插入和刪除操作的線性表。我們把允許插入和刪除的一端稱爲棧頂,另一端稱爲棧底,不含任何數據元素的棧稱爲空棧。棧的特殊之處在於它限制了這個線性表的插入和刪除位置,它始終只在棧頂進行。
  • 棧是一種具有後進先出的數據結構,又稱爲後進先出的線性表,簡稱 LIFO(Last In First Out)結構。也就是說後存放的先取,先存放的後取,這就類似於我們要在取放在箱子底部的東西(放進去比較早的物體),我們首先要移開壓在它上面的物體(放進去比較晚的物體)。
  • 堆棧中定義了一些操作。兩個最重要的是PUSH和POP。PUSH操作在堆棧的頂部加入一個元素。POP操作相反,在堆棧頂部移去一個元素,並將堆棧的大小減一。
  • 棧的應用—遞歸

隊列

  • 隊列是隻允許在一端進行插入操作、而在另一端進行刪除操作的線性表。允許插入的一端稱爲隊尾,允許刪除的一端稱爲隊頭。它是一種特殊的線性表,特殊之處在於它只允許在表的前端進行刪除操作,而在表的後端進行插入操作,和棧一樣,隊列是一種操作受限制的線性表。
  • 隊列是一種先進先出的數據結構,又稱爲先進先出的線性表,簡稱 FIFO(First In First Out)結構。也就是說先放的先取,後放的後取,就如同行李過安檢的時候,先放進去的行李在另一端總是先出來,後放入的行李會在最後面出來。

輸入一棵二叉樹的根結點,求該樹的深度?

二叉樹的節點定義如下:

struct BinaryTreeNode
{
	int m_nValue ;
	BinaryTreeNode* m_pLeft;
	BinarvTreeNode* m_pRight ;
}
  • 如果一棵樹只有一個結點,它的深度爲1。
  • 如果根結點只有左子樹而沒有右子樹,那麼樹的深度應該是其左子樹的深度加1;同樣如果根結點只有右子樹而沒有左子樹,那麼樹的深度應該是其右子樹的深度加1。
  • 如果既有右子樹又有左子樹,那該樹的深度就是其左、右子樹深度的較大值再加1。
int TreeDepth(TreeNode* pRoot)
{
    if(pRoot == nullptr)
        return 0;
    int left = TreeDepth(pRoot->left);
    int right = TreeDepth(pRoot->right);

    return (left>right) ? (left+1) : (right+1);
}

輸入一課二叉樹的根結點,判斷該樹是不是平衡二叉樹?

  • 重複遍歷結點
    先求出根結點的左右子樹的深度,然後判斷它們的深度相差不超過1,如果否,則不是一棵二叉樹;如果是,再用同樣的方法分別判斷左子樹和右子樹是否爲平衡二叉樹,如果都是,則這就是一棵平衡二叉樹
  • 遍歷一遍結點
    遍歷結點的同時記錄下該結點的深度,避免重複訪問

方法1

struct TreeNode{
    int val;
    TreeNode* left;
    TreeNode* right;
};
 
int TreeDepth(TreeNode* pRoot){
    if(pRoot==NULL)
        return 0;
    int left=TreeDepth(pRoot->left);
    int right=TreeDepth(pRoot->right);
    return left>right?(left+1):(right+1);
}
 
bool IsBalanced(TreeNode* pRoot){
    if(pRoot==NULL)
        return true;
    int left=TreeDepth(pRoot->left);
    int right=TreeDepth(pRoot->right);
    int diff=left-right;
    if(diff>1 || diff<-1)
        return false;
    return IsBalanced(pRoot->left) && IsBalanced(pRoot->right);
}

方法2

bool IsBalanced_1(TreeNode* pRoot,int& depth){
    if(pRoot==NULL){
        depth=0;
        return true;
    }
    int left,right;
    int diff;
    if(IsBalanced_1(pRoot->left,left) && IsBalanced_1(pRoot->right,right)){
        diff=left-right;
        if(diff<=1 || diff>=-1){
            depth=left>right?left+1:right+1;
            return true;
        }
    }
    return false;
}
 
bool IsBalancedTree(TreeNode* pRoot){
    int depth=0;
    return IsBalanced_1(pRoot,depth);
} 

Foundation(基礎)

1.nil NIL NSNULL區別

  • nil、NIL 可以說是等價的,都代表內存中一塊空地址。
  • NSNULL 代表一個指向 nil 的對象。

2.如何實現一個線程安全的 NSMutableArray?

NSMutableArray是線程不安全的,當有多個線程同時對數組進行操作的時候可能導致崩潰或數據錯誤

  • 線程鎖:使用線程鎖對數組讀寫時進行加鎖
  • 派發隊列:在《Effective Objective-C 2.0…》書中第41條:多用派發隊列,少用同步鎖中指出:使用“串行同步隊列”(serial synchronization queue),將讀取操作及寫入操作都安排在同一個隊列裏,即可保證數據同步。而通過併發隊列,結合GCD的柵欄塊(barrier)來不僅實現數據同步線程安全,還比串行同步隊列方式更高效。

3.atomic修飾符是絕對安全嗎,爲什麼?

不是,所謂的安全只是侷限於 Setter、Getter 的訪問器方法而言的,你對它做 Release 的操作是不會受影響的。這個時候就容易崩潰了。

4.實現 isEqual 和 hash 方法時要注意什麼?

  • hash
    對關鍵屬性的hash值進行位或運算作爲hash值
  • isEqual
    ==運算符判斷是否是同一對象, 因爲同一對象必然完全相同
    判斷是否是同一類型, 這樣不僅可以提高判等的效率, 還可以避免隱式類型轉換帶來的潛在風險
    判斷對象是否是nil, 做參數有效性檢查
    各個屬性分別使用默認判等方法進行判斷
    返回所有屬性判等的與結果

5.id 和 instanceType 有什麼區別?

  • 相同點
    instancetype 和 id 都是萬能指針,指向對象。
  • 不同點
    1.id 在編譯的時候不能判斷對象的真實類型,instancetype 在編譯的時候可以判斷對象的真實類型。
    2…id 可以用來定義變量,可以作爲返回值類型,可以作爲形參類型;instancetype 只能作爲返回值類型。

self和super的區別

  • self調用自己方法,super調用父類方法
  • self是類,super是預編譯指令
  • [self class] 和 [super class] 輸出是一樣的
  • self和super底層實現原理
    1.當使用 self 調用方法時,會從當前類的方法列表中開始找,如果沒有,就從父類中再找;
    而當使用 super 時,則從父類的方法列表中開始找,然後調用父類的這個方法sd
    2.當使用 self 調用時,會使用 objc_msgSend 函數:id objc_msgSend(id theReceiver, SEL theSelector, …)
    第一個參數是消息接收者,第二個參數是調用的具體類方法的 selector,後面是 selector 方法的可變參數。以 [self setName:] 爲例,編譯器會替換成調用 objc_msgSend 的函數調用,其中 theReceiver 是 self,theSelector 是 @selector(setName:),這個 selector 是從當前 self 的 class 的方法列表開始找的 setName,當找到後把對應的 selector 傳遞過去。
    3.當使用 super 調用時,會使用 objc_msgSendSuper 函數:id objc_msgSendSuper(struct objc_super *super, SEL op, …)
    第一個參數是個objc_super的結構體,第二個參數還是類似上面的類方法的selector
    struct objc_super {
    id receiver;
    Class superClass;
    };

7.@synthesize和@dynamic分別有什麼作用?

  • @property有兩個對應的詞,一個是 @synthesize,一個是 @dynamic。如果 @synthesize和 @dynamic都沒寫,那麼默認的就是@syntheszie var = _var;
  • @synthesize 的語義是如果你沒有手動實現 setter 方法和 getter 方法,那麼編譯器會自動爲你加上這兩個方法。
  • @dynamic 告訴編譯器:屬性的 setter 與 getter 方法由用戶自己實現,不自動生成。(當然對於 readonly 的屬性只需提供 getter 即可)。假如一個屬性被聲明爲 @dynamic var,然後你沒有提供 @setter方法和 @getter 方法,編譯的時候沒問題,但是當程序運行到 instance.var = someVar,由於缺 setter 方法會導致程序崩潰;或者當運行到 someVar = var 時,由於缺 getter 方法同樣會導致崩潰。編譯時沒問題,運行時才執行相應的方法,這就是所謂的動態綁定。

8.typeof 和 __typeof,typeof 的區別?

  • __typeof __() 和 __typeof() 是 C語言 的編譯器特定擴展,因爲標準 C 不包含這樣的運算符。 標準 C 要求編譯器用雙下劃線前綴語言擴展(這也是爲什麼你不應該爲自己的函數,變量等做這些)
  • typeof() 與前兩者完全相同的,只不過去掉了下劃線,同時現代的編譯器也可以理解。
  • 所以這三個意思是相同的,但沒有一個是標準C,不同的編譯器會按需選擇符合標準的寫法。

10.struct和class的區別

  • 類: 引用類型(位於棧上面的指針(引用)和位於堆上的實體對象)
  • 結構:值類型(實例直接位於棧中)

UIKit

1.UIView 和 CALayer 是什麼關係?

  • UIView 繼承 UIResponder,而 UIResponder 是響應者對象,可以對iOS 中的事件響應及傳遞,CALayer 沒有繼承自 UIResponder,所以 CALayer 不具備響應處理事件的能力。CALayer 是 QuartzCore 中的類,是一個比較底層的用來繪製內容的類,用來繪製UI
  • UIView 對 CALayer 封裝屬性,對 UIView 設置 frame、center、bounds 等位置信息時,其實都是UIView 對 CALayer 進一層封裝,使得我們可以很方便地設置控件的位置;例如圓角、陰影等屬性, UIView 就沒有進一步封裝,所以我們還是需要去設置 Layer 的屬性來實現功能。
  • UIView 是 CALayer 的代理,UIView 持有一個 CALayer 的屬性,並且是該屬性的代理,用來提供一些 CALayer 行的數據,例如動畫和繪製。

Bounds 和 Frame 的區別?

  • Bounds:一般是相對於自身來說的,是控件的內部尺寸。如果你修改了 Bounds,那麼子控件的相對位置也會發生改變。
  • Frame :是相對於父控件來說的,是控件的外部尺寸。

setNeedsDisplay 和 layoutIfNeeded 兩者是什麼關係?

  • UIView的setNeedsDisplay和setNeedsLayout兩個方法都是異步執行的。而setNeedsDisplay會自動調用drawRect方法,這樣可以拿到UIGraphicsGetCurrentContext進行繪製;而setNeedsLayout會默認調用layoutSubViews,給當前的視圖做了標記;layoutIfNeeded 查找是否有標記,如果有標記及立刻刷新。
  • 只有setNeedsLayout和layoutIfNeeded這二者合起來使用,纔會起到立刻刷新的效果。

4.談談對UIResponder的理解

UIResponder類是專門用來響應用戶的操作處理各種事件的,包括觸摸事件(Touch Events)、運動事件(Motion Events)、遠程控制事件(Remote Control Events)。我們知道UIApplication、UIView、UIViewController這幾個類是直接繼承自UIResponder,所以這些類都可以響應事件。當然我們自定義的繼承自UIView的View以及自定義的繼承自UIViewController的控制器都可以響應事件。

5.loadView的作用?

loadView方法會在每次訪問UIViewController的view(比如controller.view、self.view)而且view爲nil時會被調用,此方法主要用來負責創建UIViewController的view(重寫loadView方法,並且不需要調用[super loadView])
這裏要提一下 [super loadView],[super loadView]做了下面幾件事。

  • 它會先去查找與UIViewController相關聯的xib文件,通過加載xib文件來創建UIViewController的view,如果在初始化UIViewController指定了xib文件名,就會根據傳入的xib文件名加載對應的xib文件,如果沒有明顯地傳xib文件名,就會加載跟UIViewController同名的xib文件
  • 如果沒有找到相關聯的xib文件,就會創建一個空白的UIView,然後賦值給UIViewController的view屬性
    綜上,在需要自定義UIViewController的view時,可以通過重寫loadView方法且不需要調用[super loadView]方法。

6.使用 drawRect有什麼影響?

drawRect 方法依賴 Core Graphics 框架來進行自定義的繪製 缺點:它處理 touch 事件時每次按鈕被點擊後,都會用 setNeddsDisplay 進行強制重繪;而且不止一次,每次單點事件觸發兩次執行。這樣的話從性能的角度來說,對 CPU 和內存來說都是欠佳的。特別是如果在我們的界面上有多個這樣的UIButton 實例,那就會很糟糕了。這個方法的調用機制也是非常特別. 當你調用 setNeedsDisplay 方法時, UIKit 將會把當前圖層標記爲 dirty,但還是會顯示原來的內容,直到下一次的視圖渲染週期,纔會將標記爲 dirty 的圖層重新建立 Core Graphics 上下文,然後將內存中的數據恢復出來, 再使用 CGContextRef 進行繪製

7.keyWindow 和 delegate的window有何區別

  • delegate.window 程序啓動時設置的window對象。
  • keyWindow 這個屬性保存了[windows]數組中的[UIWindow]對象,該對象最近被髮送了[makeKeyAndVisible]消息
    一般情況下 delegate.window 和 keyWindow 是同一個對象,但不能保證keyWindow就是delegate.window,因爲keyWindow會因爲makeKeyAndVisible而變化,例如,程序中添加了一個懸浮窗口,這個時候keywindow就會變化。

WebView

1.說一下 JS 和 OC 互相調用的幾種方式?

  • js調用oc的三種方式:

根據網頁重定向截取字符串通過url scheme判斷

替換方法.context[@“copyText”]

注入對象:遵守協議JSExport,設置context[@

  • oc調用js代碼兩種方式

通過webVIew調用 webView stringByEvaluatingJavaScriptFromString: 調用

通過JSContext調用[context evaluateScript:];

2.在使用 WKWedView 時遇到過哪些問題?

白屏問題,Cookie 問題,在WKWebView上直接使用NSURLProtocol無法攔截請求,在WKWebView 上通過loadRequ發起的post請求body數據被丟失,截屏問題等

消息傳遞的方式

1.KVC實現原理

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-gyg1iuaD-1591231390707)(evernotecid://2CC0A8C7-733A-43F8-8428-C60C9C6C63B2/appyinxiangcom/21128449/ENResource/p395)]

  • KVC,鍵-值編碼,使用字符串直接訪問對象的屬性。
  • 底層實現,當一個對象調用setValue方法時,方法內部會做以下操作:
    1.檢查是否存在相應key的set方法,如果存在,就調用set方法
    2.如果set方法不存在,就會查找與key相同名稱並且帶下劃線的成員屬性,如果有,則直接給成員屬性賦值
    3.如果沒有找到_key,就會查找相同名稱的屬性key,如果有就直接賦值
    4.如果還沒找到,則調用valueForUndefinedKey:和setValue:forUndefinedKey:方法

2.KVO的實現原理

  • KVO-鍵值觀察機制,原理如下:
    1.當給A類添加KVO的時候,runtime動態的生成了一個子類NSKVONotifying_A,讓A類的isa指針指向NSKVONotifying_A類,重寫class方法,隱藏對象真實類信息
    2.重寫監聽屬性的setter方法,在setter方法內部調用了Foundation的_NSSetObjectValueAndNotify 函數
  1. ()_NSSetObjectValueAndNotify函數內部
    • 首先會調用 willChangeValueForKey
    • 然後給屬性賦值
    • 最後調用 didChangeValueForKey
    • 最後調用 observer 的 observeValueForKeyPath 去告訴監聽器屬性值發生了改變
      4.重寫了dealloc做一些 KVO 內存釋放

3.如何手動觸發KVO方法

  • 手動調用willChangeValueForKey和didChangeValueForKey方法
  • 鍵值觀察通知依賴於 NSObject 的兩個方法: willChangeValueForKey: 和 didChangeValueForKey。在一個被觀察屬性發生改變之前, willChangeValueForKey: 一定會被調用,這就 會記錄舊的值。而當改變發生後, didChangeValueForKey 會被調用,繼而 observeValueForKey:ofObject:change:context: 也會被調用。如果可以手動實現這些調用,就可以實現“手動觸發”了 有人可能會問只調用didChangeValueForKey方法可以觸發KVO方法,其實是不能的,因爲willChangeValueForKey: 記錄舊的值,如果不記錄舊的值,那就沒有改變一說了

4.通知和代理有什麼區別

  • 通知是觀察者模式,適合一對多的場景
  • 代理模式適合一對一的反向傳值
  • 通知耦合度低,代理的耦合度高

5.block和delegate的區別

  • delegate運行成本低,block的運行成本高
  • block出棧需要將使用的數據從棧內存拷貝到堆內存,當然對象的話就是加計數,使用完或者block置nil後才消除。delegate只是保存了一個對象指針,直接回調,沒有額外消耗。就像C的函數指針,只多做了一個查表動作。
  • delegate更適用於多個回調方法(3個以上),block則適用於1,2個回調時。

6.爲什麼Block用copy關鍵字

Block在沒有使用外部變量時,內存存在全局區,然而,當Block在使用外部變量的時候,內存是存在於棧區,當Block copy之後,是存在堆區的。存在於棧區的特點是對象隨時有可能被銷燬,一旦銷燬在調用的時候,就會造成系統的崩潰。所以Block要用copy關鍵字。

組件化

1.組件化有什麼好處?

  • 業務分層、解耦,使代碼變得可維護;
  • 有效的拆分、組織日益龐大的工程代碼,使工程目錄變得可維護;
  • 便於各業務功能拆分、抽離,實現真正的功能複用;
  • 業務隔離,跨團隊開發代碼控制和版本風險控制的實現;
  • 模塊化對代碼的封裝性、合理性都有一定的要求,提升開發同學的設計能力;
    +在維護好各級組件的情況下,隨意組合滿足不同客戶需求;(只需要將之前的多個業務組件模塊在新的主App中進行組裝即可快速迭代出下一個全新App)

2.你是如何組件化解耦的?

  • 分層
    1.基礎功能組件:按功能分庫,不涉及產品業務需求,跟庫Library類似,通過良好的接口拱上層業務組件調用;不寫入產品定製邏輯,通過擴展接口完成定製;
    2.基礎UI組件:各個業務模塊依賴使用,但需要保持好定製擴展的設計
    3.業務組件:業務功能間相對獨立,相互間沒有Model共享的依賴;業務之間的頁面調用只能通過UIBus進行跳轉;業務之間的邏輯Action調用只能通過服務提供;
  • 中間件:target-action,url-block,protocol-class

3.爲什麼CTMediator方案優於基於Router的方案?

Router的缺點:

  • 在組件化的實施過程中,註冊URL並不是充分必要條件。組件是不需要向組件管理器註冊URL的,註冊了URL之後,會造成不必要的內存常駐。註冊URL的目的其實是一個服務發現的過程,在iOS領域中,服務發現的方式是不需要通過主動註冊的,使用runtime就可以了。另外,註冊部分的代碼的維護是一個相對麻煩的事情,每一次支持新調用時,都要去維護一次註冊列表。如果有調用被棄用了,是經常會忘記刪項目的。runtime由於不存在註冊過程,那就也不會產生維護的操作,維護成本就降低了。 由於通過runtime做到了服務的自動發現,拓展調用接口的任務就僅在於各自的模塊,任何一次新接口添加,新業務添加,都不必去主工程做操作,十分透明。
  • 在iOS領域裏,一定是組件化的中間件爲openURL提供服務,而不是openURL方式爲組件化提供服務。如果在給App實施組件化方案的過程中是基於openURL的方案的話,有一個致命缺陷:非常規對象(不能被字符串化到URL中的對象,例如UIImage)無法參與本地組件間調度。 在本地調用中使用URL的方式其實是不必要的,如果業務工程師在本地間調度時需要給出URL,那麼就不可避免要提供params,在調用時要提供哪些params是業務工程師很容易懵逼的地方。
  • 爲了支持傳遞非常規參數,蘑菇街的方案採用了protocol,這個會侵入業務。由於業務中的某個對象需要被調用,因此必須要符合某個可被調用的protocol,然而這個protocol又不存在於當前業務領域,於是當前業務就不得不依賴public Protocol。這對於將來的業務遷移是有非常大的影響的。
    CTMediator的優點:
  • 調用時,區分了本地應用調用和遠程應用調用。本地應用調用爲遠程應用調用提供服務。
  • 組件僅通過Action暴露可調用接口,模塊與模塊之間的接口被固化在了Target-Action這一層,避免了實施組件化的改造過程中,對Business的侵入,同時也提高了組件化接口的可維護性。
  • 方便傳遞各種類型的參數。

4.基於CTMediator的組件化方案,有哪些核心組成?

  • CTMediator中間件:集成就可以了
  • 模塊Target_%@:模塊的實現及提供對外的方法調用Action_methodName,需要傳參數時,都統一以NSDictionary的形式傳入
  • CTMediator+%@擴展:擴展裏聲明瞭模塊業務的對外接口,參數明確,這樣外部調用者可以很容易理解如何調用接口。

內存管理

1.什麼情況使用weak關鍵字,相比assign有什麼不同?

  • 什麼情況使用 weak 關鍵字?
    在 ARC 中,在有可能出現循環引用的時候,往往要通過讓其中一端使用 weak 來解決,比如: delegate 代理屬性
    自身已經對它進行一次強引用,沒有必要再強引用一次,此時也會使用 weak,自定義 IBOutlet 控件屬性一般也使用 weak;當然,也可以使用strong。在下文也有論述:《IBOutlet連出來的視圖屬性爲什麼可以被設置成weak?》
  • 不同點:
    weak 此特質表明該屬性定義了一種“非擁有關係” (nonowning relationship)。爲這種屬性設置新值時,設置方法既不保留新值,也不釋放舊值。此特質同assign類似, 然而在屬性所指的對象遭到摧毀時,屬性值也會清空(nil out)。 而 assign 的“設置方法”只會執行鍼對“純量類型” (scalar type,例如 CGFloat 或 NSlnteger 等)的簡單賦值操作。
    assign 可以用非 OC 對象,而 weak 必須用於 OC 對象

2.如何讓自己的類用copy修飾符?如何重寫帶copy關鍵字的setter?

  • 若想令自己所寫的對象具有拷貝功能,則需實現 NSCopying 協議。如果自定義的對象分爲可變版本與不可變版本,那麼就要同時實現 NSCopying 與 NSMutableCopying 協議。
    具體步驟:
    需聲明該類遵從 NSCopying 協議
    實現 NSCopying 協議。該協議只有一個方法:
- (id)copyWithZone:(NSZone *)zone;
1

注意:一提到讓自己的類用 copy 修飾符,我們總是想覆寫copy方法,其實真正需要實現的卻是 “copyWithZone” 方法。

  • 重寫帶 copy 關鍵字的 setter,例如:
- (void)setName:(NSString *)name {
    //[_name release];
    _name = [name copy];
}

3.深拷貝與淺拷貝

淺拷貝只是對指針的拷貝,拷貝後兩個指針指向同一個內存空間,深拷貝不但對指針進行拷貝,而且對指針指向的內容進行拷貝,經深拷貝後的指針是指向兩個不同地址的指針。
當對象中存在指針成員時,除了在複製對象時需要考慮自定義拷貝構造函數,還應該考慮以下兩種情形:

  • 當函數的參數爲對象時,實參傳遞給形參的實際上是實參的一個拷貝對象,系統自動通過拷貝構造函數實現;
  • 當函數的返回值爲一個對象時,該對象實際上是函數內對象的一個拷貝,用於返回函數調用處。
    copy方法:如果是非可擴展類對象,則是淺拷貝。如果是可擴展類對象,則是深拷貝。
    mutableCopy方法:無論是可擴展類對象還是不可擴展類對象,都是深拷貝。

@property的本質是什麼?ivar、getter、setter是如何生成並添加到這個類中的

  • @property 的本質是實例變量(ivar)+存取方法(access method = getter + setter),即 @property = ivar + getter + setter;
    “屬性” (property)作爲 Objective-C 的一項特性,主要的作用就在於封裝對象中的數據。 Objective-C 對象通常會把其所需要的數據保存爲各種實例變量。實例變量一般通過“存取方法”(access method)來訪問。其中,“獲取方法” (getter)用於讀取變量值,而“設置方法” (setter)用於寫入變量值。
  • ivar、getter、setter 是自動合成這個類中的
    完成屬性定義後,編譯器會自動編寫訪問這些屬性所需的方法,此過程叫做“自動合成”(autosynthesis)。需要強調的是,這個過程由編譯 器在編譯期執行,所以編輯器裏看不到這些“合成方法”(synthesized method)的源代碼。除了生成方法代碼 getter、setter 之外,編譯器還要自動向類中添加適當類型的實例變量,並且在屬性名前面加下劃線,以此作爲實例變量的名字。在前例中,會生成兩個實例變量,其名稱分別爲 _firstName 與 _lastName。也可以在類的實現代碼裏通過 @synthesize 語法來指定實例變量的名字.

@protocol和category中如何使用@property

  • 在 protocol 中使用 property 只會生成 setter 和 getter 方法聲明,我們使用屬性的目的,是希望遵守我協議的對象能實現該屬性
  • category 使用 @property 也是隻會生成 setter 和 getter 方法的聲明,如果我們真的需要給 category 增加屬性的實現,需要藉助於運行時的兩個函數:objc_setAssociatedObject和objc_getAssociatedObject

6.簡要說一下@autoreleasePool的數據結構??

簡單說是雙向鏈表,每張鏈表頭尾相接,有 parent、child指針
每創建一個池子,會在首部創建一個 哨兵 對象,作爲標記
最外層池子的頂端會有一個next指針。當鏈表容量滿了,就會在鏈表的頂端,並指向下一張表。

BAD_ACCESS在什麼情況下出現?

訪問了懸垂指針,比如對一個已經釋放的對象執行了release、訪問已經釋放對象的成員變量或者發消息。 死循環

8.使用CADisplayLink、NSTimer有什麼注意點?

CADisplayLink、NSTimer會造成循環引用,可以使用YYWeakProxy或者爲CADisplayLink、NSTimer添加block方法解決循環引用

9.iOS內存分區情況

  • 棧區(Stack)
    由編譯器自動分配釋放,存放函數的參數,局部變量的值等
    棧是向低地址擴展的數據結構,是一塊連續的內存區域
  • 堆區(Heap)
    由程序員分配釋放
    是向高地址擴展的數據結構,是不連續的內存區域
  • 全局區
    全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域,未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域
    程序結束後由系統釋放
  • 常量區
    常量字符串就是放在這裏的
    程序結束後由系統釋放
    代碼區
    存放函數體的二進制代碼
  • 注:
    在 iOS 中,堆區的內存是應用程序共享的,堆中的內存分配是系統負責的
    系統使用一個鏈表來維護所有已經分配的內存空間(系統僅僅記錄,並不管理具體的內容)
    變量使用結束後,需要釋放內存,OC 中是判斷引用計數是否爲 0,如果是就說明沒有任何變量使用該空間,那麼系統將其回收
    當一個 app 啓動後,代碼區、常量區、全局區大小就已經固定,因此指向這些區的指針不會產生崩潰性的錯誤。而堆區和棧區是時時刻刻變化的(堆的創建銷燬,棧的彈入彈出),所以當使用一個指針指向這個區裏面的內存時,一定要注意內存是否已經被釋放,否則會產生程序崩潰(也即是野指針報錯)

10.循環引用

循環引用的實質:多個對象相互之間有強引用,不能釋放讓系統回收。
如何解決循環引用?

  • 1.避免產生循環引用,通常是將 strong 引用改爲 weak 引用。 比如在修飾屬性時用weak 在block內調用對象方法時,使用其弱引用,這裏可以使用兩個宏
    #define WS(weakSelf) __weak __typeof(&*self)weakSelf = self; // 弱引用
    #define ST(strongSelf) __strong __typeof(&*self)strongSelf = weakSelf; //使用這個要先聲明weakSelf 還可以使用__block來修飾變量 在MRC下,__block不會增加其引用計數,避免了循環引用 在ARC下,__block修飾對象會被強引用,無法避免循環引用,需要手動解除。

  • 2.在合適時機去手動斷開循環引用。 通常我們使用第一種。

  • 代理(delegate)循環引用屬於相互循環引用
    delegate 是iOS中開發中比較常遇到的循環引用,一般在聲明delegate的時候都要使用弱引用 weak,或者assign,當然怎麼選擇使用assign還是weak,MRC的話只能用assign,在ARC的情況下最好使用weak,因爲weak修飾的變量在釋放後自動指向nil,防止野指針存在

  • NSTimer循環引用屬於相互循環使用
    在控制器內,創建NSTimer作爲其屬性,由於定時器創建後也會強引用該控制器對象,那麼該對象和定時器就相互循環引用了。 如何解決呢? 這裏我們可以使用手動斷開循環引用: 如果是不重複定時器,在回調方法裏將定時器invalidate並置爲nil即可。 如果是重複定時器,在合適的位置將其invalidate並置爲nil即可

  • 3.block循環引用
    一個簡單的例子:

@property (copy, nonatomic) dispatch_block_t myBlock;
@property (copy, nonatomic) NSString *blockString;

- (void)testBlock {
    self.myBlock = ^() {
        NSLog(@"%@",self.blockString);
    };
}

由於block會對block中的對象進行持有操作,就相當於持有了其中的對象,而如果此時block中的對象又持有了該block,則會造成循環引用。 解決方案就是使用__weak修飾self即可

__weak typeof(self) weakSelf = self;

self.myBlock = ^() {
        NSLog(@"%@",weakSelf.blockString);
 };

並不是所有block都會造成循環引用。 只有被強引用了的block纔會產生循環引用 而比如dispatch_async(dispatch_get_main_queue(), ^{}),[UIView animateWithDuration:1 animations:^{}]這些系統方法等 或者block並不是其屬性而是臨時變量,即棧block

[self testWithBlock:^{
    NSLog(@"%@",self);
}];

- (void)testWithBlock:(dispatch_block_t)block {
    block();
}

還有一種場景,在block執行開始時self對象還未被釋放,而執行過程中,self被釋放了,由於是用weak修飾的,那麼weakSelf也被釋放了,此時在block裏訪問weakSelf時,就可能會發生錯誤(向nil對象發消息並不會崩潰,但也沒任何效果)。 對於這種場景,應該在block中對 對象使用__strong修飾,使得在block期間對 對象持有,block執行結束後,解除其持有。

__weak typeof(self) weakSelf = self;

self.myBlock = ^() {

        __strong __typeof(self) strongSelf = weakSelf;

        [strongSelf test];
 };
<<<<<<< HEAD

數據存儲

1.iOS 開發中數據持久性有哪幾種?

iOS本地數據保存有多種方式,比如NSUserDefaults、歸檔、文件保存、數據庫、CoreData、KeyChain(鑰匙串)等多種方式。其中KeyChain(鑰匙串)是保存到沙盒範圍以外的地方,也就是與沙盒無關。

2.FMDB數據結構變化升級

  • 使用columnExists:inTableWithName方法判斷數據表中是否存在字段
  • 如果不存在,則添加, 如:向bbb表中添加aaa字段 -> ALTER TABLE bbb ADD ‘aaa’ TEXT

多線程

1.進程與線程

  • 進程:
    1.進程是一個具有一定獨立功能的程序關於某次數據集合的一次運行活動,它是操作系統分配資源的基本單元.
    2.進程是指在系統中正在運行的一個應用程序,就是一段程序的執行過程,我們可以理解爲手機上的一個app.
    3.每個進程之間是獨立的,每個進程均運行在其專用且受保護的內存空間內,擁有獨立運行所需的全部資源

  • 線程
    1.程序執行流的最小單元,線程是進程中的一個實體.
    2.一個進程要想執行任務,必須至少有一條線程.應用程序啓動的時候,系統會默認開啓一條線程,也就是主線程

  • 進程和線程的關係
    1.線程是進程的執行單元,進程的所有任務都在線程中執行
    2.線程是 CPU 分配資源和調度的最小單位
    3.一個程序可以對應多個進程(多進程),一個進程中可有多個線程,但至少要有一條線程
    4.同一個進程內的線程共享進程資源

2.什麼是多線程?

  • 多線程的實現原理:事實上,同一時間內單核的CPU只能執行一個線程,多線程是CPU快速的在多個線程之間進行切換(調度),造成了多個線程同時執行的假象。
  • 如果是多核CPU就真的可以同時處理多個線程了。
  • 多線程的目的是爲了同步完成多項任務,通過提高系統的資源利用率來提高系統的效率。

3.多線程的優點和缺點

  • 優點:
    能適當提高程序的執行效率
    能適當提高資源利用率(CPU、內存利用率)

  • 缺點:
    開啓線程需要佔用一定的內存空間(默認情況下,主線程佔用1M,子線程佔用512KB),如果開啓大量的線程,會佔用大量的內存空間,降低程序的性能
    線程越多,CPU在調度線程上的開銷就越大
    程序設計更加複雜:比如線程之間的通信、多線程的數據共享

4.多線程的 並行 和 併發 有什麼區別?

  • 並行:充分利用計算機的多核,在多個線程上同步進行
  • 併發:在一條線程上通過快速切換,讓人感覺在同步進行

5.iOS中實現多線程的幾種方案,各自有什麼特點?

  • NSThread 面向對象的,需要程序員手動創建線程,但不需要手動銷燬。子線程間通信很難。
  • GCD c語言,充分利用了設備的多核,自動管理線程生命週期。比NSOperation效率更高。
  • NSOperation 基於gcd封裝,更加面向對象,比gcd多了一些功能。

6.多個網絡請求完成後執行下一步

  • 使用GCD的dispatch_group_t
    創建一個dispatch_group_t
    每次網絡請求前先dispatch_group_enter,請求回調後再dispatch_group_leave,enter和leave必須配合使用,有幾次enter就要有幾次leave,否則group會一直存在。
    當所有enter的block都leave後,會執行dispatch_group_notify的block。
NSString *str = @"http://xxxx.com/";
NSURL *url = [NSURL URLWithString:str];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];

dispatch_group_t downloadGroup = dispatch_group_create();
for (int i=0; i<10; i++) {
    dispatch_group_enter(downloadGroup);
    
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"%d---%d",i,i);
        dispatch_group_leave(downloadGroup);
    }];
    [task resume];
}

dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{
    NSLog(@"end");
});
  • 使用GCD的信號量dispatch_semaphore_t
    dispatch_semaphore信號量爲基於計數器的一種多線程同步機制。如果semaphore計數大於等於1,計數-1,返回,程序繼續運行。如果計數爲0,則等待。dispatch_semaphore_signal(semaphore)爲計數+1操作,dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER)爲設置等待時間,這裏設置的等待時間是一直等待。
    創建semaphore爲0,等待,等10個網絡請求都完成了,dispatch_semaphore_signal(semaphore)爲計數+1,然後計數-1返回
NSString *str = @"http://xxxx.com/";
NSURL *url = [NSURL URLWithString:str];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];

dispatch_semaphore_t sem = dispatch_semaphore_create(0);
for (int i=0; i<10; i++) {
    
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"%d---%d",i,i);
        count++;
        if (count==10) {
            dispatch_semaphore_signal(sem);
            count = 0;
        }
    }];
    [task resume];
}
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);

dispatch_async(dispatch_get_main_queue(), ^{
    NSLog(@"end");
});

7.多個網絡請求順序執行後執行下一步

  • 使用信號量semaphore
    每一次遍歷,都讓其dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER),這個時候線程會等待,阻塞當前線程,直到dispatch_semaphore_signal(sem)調用之後
NSString *str = @"http://www.xxx.com";
NSURL *url = [NSURL URLWithString:str];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];

dispatch_semaphore_t sem = dispatch_semaphore_create(0);
for (int i=0; i<10; i++) {
    
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
        NSLog(@"%d---%d",i,i);
        dispatch_semaphore_signal(sem);
    }];
    
    [task resume];
    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
}

dispatch_async(dispatch_get_main_queue(), ^{
    NSLog(@"end");
});

8.異步操作兩組數據時, 執行完第一組之後, 才能執行第二組

  • 這裏使用dispatch_barrier_async柵欄方法即可實現
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{
    NSLog(@"第一次任務的主線程爲: %@", [NSThread currentThread]);
});

dispatch_async(queue, ^{
    NSLog(@"第二次任務的主線程爲: %@", [NSThread currentThread]);
});

dispatch_barrier_async(queue, ^{
    NSLog(@"第一次任務, 第二次任務執行完畢, 繼續執行");
});

dispatch_async(queue, ^{
    NSLog(@"第三次任務的主線程爲: %@", [NSThread currentThread]);
});

dispatch_async(queue, ^{
    NSLog(@"第四次任務的主線程爲: %@", [NSThread currentThread]);
});

9.多線程中的死鎖?

死鎖是由於多個線程(進程)在執行過程中,因爲爭奪資源而造成的互相等待現象,你可以理解爲卡主了。產生死鎖的必要條件有四個:

  • 互斥條件 : 指進程對所分配到的資源進行排它性使用,即在一段時間內某資源只由一個進程佔用。如果此時還有其它進程請求資源,則請求者只能等待,直至佔有資源的進程用畢釋放。
  • 請求和保持條件 : 指進程已經保持至少一個資源,但又提出了新的資源請求,而該資源已被其它進程佔有,此時請求進程阻塞,但又對自己已獲得的其它資源保持不放。
  • 不可剝奪條件 : 指進程已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時由自己釋放。
  • 環路等待條件 : 指在發生死鎖時,必然存在一個進程——資源的環形鏈,即進程集合{P0,P1,P2,···,Pn}中的P0正在等待一個P1佔用的資源;P1正在等待P2佔用的資源,……,Pn正在等待已被P0佔用的資源。
    最常見的就是 同步函數 + 主隊列 的組合,本質是隊列阻塞。
dispatch_sync(dispatch_get_main_queue(), ^{
    NSLog(@"2");
});

NSLog(@"1");
// 什麼也不會打印,直接報錯

10.GCD執行原理?

  • GCD有一個底層線程池,這個池中存放的是一個個的線程。之所以稱爲“池”,很容易理解出這個“池”中的線程是可以重用的,當一段時間後這個線程沒有被調用胡話,這個線程就會被銷燬。注意:開多少條線程是由底層線程池決定的(線程建議控制再3~5條),池是系統自動來維護,不需要我們程序員來維護(看到這句話是不是很開心?) 而我們程序員需要關心的是什麼呢?我們只關心的是向隊列中添加任務,隊列調度即可。
  • 如果隊列中存放的是同步任務,則任務出隊後,底層線程池中會提供一條線程供這個任務執行,任務執行完畢後這條線程再回到線程池。這樣隊列中的任務反覆調度,因爲是同步的,所以當我們用currentThread打印的時候,就是同一條線程。
  • 如果隊列中存放的是異步的任務,(注意異步可以開線程),當任務出隊後,底層線程池會提供一個線程供任務執行,因爲是異步執行,隊列中的任務不需等待當前任務執行完畢就可以調度下一個任務,這時底層線程池中會再次提供一個線程供第二個任務執行,執行完畢後再回到底層線程池中。
  • 這樣就對線程完成一個複用,而不需要每一個任務執行都開啓新的線程,也就從而節約的系統的開銷,提高了效率。在iOS7.0的時候,使用GCD系統通常只能開58條線程,iOS8.0以後,系統可以開啓很多條線程,但是實在開發應用中,建議開啓線程條數:35條最爲合理。

動畫

1.UIView動畫與核心動畫的區別?

  • 核心動畫只作用在layer.
  • 核心動畫修改的值都是假像.它的真實位置沒有發生變化.
  • 當需要與用戶進行交互時用UIView動畫,不需要與用戶進行交互時兩個都可以.

2.當我們要做一些基於 CALayer 的動畫時,有時需要設置 layer 的錨點來配合動畫,這時候我們需要注意什麼?

  • 需要注意的是設置錨點會引起原來 position 的變化,可能會發生不符合預期的行爲,所以要做一下轉化,示例代碼如下:
// 爲 layer 的動畫設置不同的 anchor point,但是又不想改變 view 原來的 position,則需要做一些轉換。
- (void)setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)view {
    // 分別計算原來錨點和將更新的錨點對應的座標點,這些座標點是相對該 view 內部座標系的。
    CGPoint oldPoint = CGPointMake(view.bounds.size.width * view.layer.anchorPoint.x,
                                   view.bounds.size.height * view.layer.anchorPoint.y);
    CGPoint newPoint = CGPointMake(view.bounds.size.width * anchorPoint.x,
                                   view.bounds.size.height * anchorPoint.y);
    
    // 如果當前 view 有做過 transform,這裏要同步計算。
    oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform);
    newPoint = CGPointApplyAffineTransform(newPoint, view.transform);
    
    // position 是當前 view 的 anchor point 在其父 view 的位置。
    CGPoint position = view.layer.position;
    // anchor point 的改變會造成 position 的改變,從而影響 view 在其父 view 的位置,這裏把這個位移給計算回來。
    position.x = position.x + newPoint.x - oldPoint.x;
    position.y = position.y + newPoint.y - oldPoint.y;
    
    view.translatesAutoresizingMaskIntoConstraints = YES;
    view.layer.anchorPoint = anchorPoint; // 設置了新的 anchor point 會改變位置。
    view.layer.position = position; // 通過在 position 上做逆向偏移,把位置給移回來。
}

圖像處理

1.圖像的壓縮方式

壓縮圖片質量

  • 一般情況下使用UIImageJPEGRepresentation或UIImagePNGRepresentation方法實現。
  • 壓縮圖片尺寸
  • 一般通過指定壓縮的大小對圖像進行重繪

2.如何計算圖片加載內存中所佔的大小

  • 圖片內存大小的計算公式 寬度 * 高度 * bytesPerPixel/8。
  • bytesPerPixel : 每個像素所佔的字節數。
  • RGBA顏色空間下 每個顏色分量由32位組成
  • 所以一般圖片的計算公式是 wxhx4

Runtime

RunLoop

網絡

算法

項目框架

1.MVC、MVP、MVVM模式

MVC(Model、View、Controller)

MVC是比較直觀的架構模式,最核心的就是通過Controller層來進行調控,首先看一下官方提供的MVC示意圖:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-5md7GAWy-1591231390709)(evernotecid://2CC0A8C7-733A-43F8-8428-C60C9C6C63B2/appyinxiangcom/21128449/ENResource/p375)]

  • Model和View永遠不能相互通信,只能通過Controller傳遞
  • Controller可以直接與Model對話(讀寫調用Model),Model通過NOtification和KVO機制與Controller間接通信

Controller可以直接與View對話,通過IBoutlet直接操作View,IBoutlet直接對應View的控件(例如創建一個Button:需聲明一個 IBOutlet UIButton * btn),View通過action向Controller報告時間的發生(用戶點擊了按鈕)。Controller是View的直接數據源

  • 優點:對於混亂的項目組織方式,有了一個明確的組織方式。通過Controller來掌控全局,同時將View展示和Model的變化分開
  • 缺點:愈發笨重的Controller,隨着業務邏輯的增加,大量的代碼放進Controller,導致
    Controller越來越臃腫,堆積成千上萬行代碼,後期維護起來費時費力

MVP(Model、View、Presenter)

MVP模式是MVC模式的一個演化版本,其中Model與MVC模式中Model層沒有太大區別,主要提供數據存儲功能,一般都是用來封裝網絡獲取的json數據;View與MVC中的View層有一些差別,MVP中的View層可以是viewController、view等控件;Presenter層則是作爲Model和View的中介,從Model層獲取數據之後傳給View。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-9i9r5dOi-1591231390710)(evernotecid://2CC0A8C7-733A-43F8-8428-C60C9C6C63B2/appyinxiangcom/21128449/ENResource/p373)]
從上圖可以看出,從MVC模式中增加了Presenter層,將UIViewController中複雜的業務邏輯、網絡請求等剝離出來。

  • 優點 模型和視圖完全分離,可以做到修改視圖而不影響模型;更高效的使用模型,View不依賴Model,可以說VIew能做到對業務邏輯完全分離
  • 缺點 Presenter中除了處理業務邏輯以外,還要處理View-Model兩層的協調,也會導致Presenter層的臃腫

MVVM(Model、Controller/View、ViewModel)

在MVVM中,view和ViewCOntroller聯繫在一起,我們把它們視爲一個組件,view和ViewController都不能直接引用model,而是引用是視圖模型即ViewModel。 viewModel是一個用來放置用戶輸入驗證邏輯、視圖顯示邏輯、網絡請求等業務邏輯的地方,這樣的設計模式,會輕微增加代碼量,但是會減少代碼的複雜性
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ZRJIh1Gx-1591231390711)(evernotecid://2CC0A8C7-733A-43F8-8428-C60C9C6C63B2/appyinxiangcom/21128449/ENResource/p374)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-PDmihaTY-1591231390712)(evernotecid://2CC0A8C7-733A-43F8-8428-C60C9C6C63B2/appyinxiangcom/21128449/ENResource/p372)]

  • 優點 VIew可以獨立於Model的變化和修改,一個ViewModel可以綁定到不同的View上,降低耦合,增加重用
  • 缺點 過於簡單的項目不適用、大型的項目視圖狀態較多時構建和維護成本太大
    合理的運用架構模式有利於項目、團隊開發工作,但是到底選擇哪個設計模式,哪種設計模式更好,就像本文開頭所說,不同的設計模式,只是讓不同的場景有了更多的選擇方案。根據項目場景和開發需求,選擇最合適的解決方案。

2.關於RAC你有怎樣運用到解決不同API依賴關係

信號的依賴:使用場景是當信號A執行完纔會執行信號B,和請求的依賴很類似,例如請求A請求完畢才執行請求B,我們需要注意信號A必須要執行發送完成信號,否則信號B無法執行

//這相當於網絡請求中的依賴,必須先執行完信號A纔會執行信號B
//經常用作一個請求執行完畢後,纔會執行另一個請求
//注意信號A必須要執行發送完成信號,否則信號B無法執行
RACSignal * concatSignal = [self.signalA concat:self.signalB]
//這裏我們是對這個拼接信號進行訂閱
[concatSignal subscribeNext:^(id x) {
	NSLog(@"%@",x);
}];
1

3.微服務架構設想。

微服務架構具有以下優勢:
1.靈活和獨立的可擴展性
靈活擴展是微服務架構的主要優勢之一。與單片架構不同,每個模塊都可以水平擴展並獨立於其他模塊。因此,微服務架構非常適合大型項目。
2.獨立技術堆棧
在微服務架構中,軟件工程師有機會使用各種工具和技術構建APP。代碼可以用不同的編程語言編寫,這爲APP開發過程增加了更多的靈活性。
3.更好的故障隔離
如果一個服務失敗,它不會影響其他服務的功能。與其他體系結構樣式相比,在微服務中,系統繼續工作,單片模式下的問題會影響整個APP。
4.易於部署和集成
雖然即使是小代碼更改的情況下,開發人員也必須再次部署APP,但在微服務架構中,部署變得更快更輕鬆。
由於所有服務都是圍繞單一業務流程構建的,因此程序員不必修改和重新部署整個APP,只需要您需要的區域。因此,改進產品也比較簡單。
微服務可以通過全自動部署機制獨立部署。此外,通過使用開源持續集成工具,開發人員大大簡化了與第三方服務的集成。
5.容易理解
微服務體系結構的另一個優點是很容易理解系統是如何工作的以及它是如何開發的。當一個新的團隊成員來到這個項目並且必須快速鑽研它時,這特別有用。
那麼在iOS中如何借鑑這種思想去構建我們的App呢?是需要我們開發者自己去不斷探索的

設計模式

1.iOS有哪些常見的設計模式?

  • 單例模式:單例保證了應用程序的生命週期內僅有一個該類的實例對象,而且易於外界訪問.在ios sdk中,UIApplication, NSBundle, NSNotificationCenter, NSFileManager, NSUserDefault, NSURLCache等都是單例.
  • 委託模式:委託Delegate是協議的一種,通過@protocol方式實現,常見的有tableView,textField等。
  • 觀察者模式:觀察者模式定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。在iOS中,觀察者模式的具體實現有兩種: 通知機制(notification)和KVO機制(Key-value Observing)

2.單例會有什麼弊端?

  • 主要優點:
    1、提供了對唯一實例的受控訪問。
    2、由於在系統內存中只存在一個對象,因此可以節約系統資源,對於一些需要頻繁創建和銷燬的對象單例模式無疑可以提高系統的性能。
    3、允許可變數目的實例。
  • 主要缺點:
    1、由於單利模式中沒有抽象層,因此單例類的擴展有很大的困難。
    2、單例類的職責過重,在一定程度上違背了“單一職責原則”。
    3、濫用單例將帶來一些負面問題,如爲了節省資源將數據庫連接池對象設計爲的單例類,可能會導致共享連接池對象的程序過多而出現連接池溢出;如果實例化的對象長時間不被利用,系統會認爲是垃圾而被回收,這將導致對象狀態的丟失。
#import "InstanceManager.h"
@interface InstanceManager() <NSCopying,NSMutableCopying>
@end
static InstanceManager *_instance = nil;
@implementation InstanceManager
    
+ (instancetype)sharedInstance {
            return [[self alloc] init];
        }
        
+ (id)allocWithZone:(struct _NSZone *)zone {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
        if (!_instance) {
            _instance = [super allocWithZone:zone];
            }
        });
        return _instance;
    }
    
- (nonnull id)copyWithZone:(nullable NSZone *)zone {
            return _instance;
        }
        
- (nonnull id)mutableCopyWithZone:(nullable NSZone *)zone {
            return _instance;
        }
        
@end

3.編程中的六大設計原則?

1.單一職責原則
通俗地講就是一個類只做一件事
CALayer:動畫和視圖的顯示。
UIView:只負責事件傳遞、事件響應。
2.開閉原則
對修改關閉,對擴展開放。 要考慮到後續的擴展性,而不是在原有的基礎上來回修改
3.接口隔離原則
使用多個專門的協議、而不是一個龐大臃腫的協議,如 UITableviewDelegate + UITableViewDataSource
4.依賴倒置原則
抽象不應該依賴於具體實現、具體實現可以依賴於抽象。 調用接口感覺不到內部是如何操作的
5.里氏替換原則
父類可以被子類無縫替換,且原有的功能不受任何影響 如:KVO
6.迪米特法則
一個對象應當對其他對象儘可能少的瞭解,實現高聚合、低耦合

數據安全及加密

1.對稱加密和非對稱加密的區別?

1、對稱加密又稱公開密鑰加密,加密和解密都會用到同一個密鑰,如果密鑰被攻擊者獲得,此時加密就失去了意義。常見的對稱加密算法有DES、3DES、AES、Blowfish、IDEA、RC5、RC6。
2、非對稱加密又稱共享密鑰加密,使用一對非對稱的密鑰,一把叫做私有密鑰,另一把叫做公有密鑰;公鑰加密只能用私鑰來解密,私鑰加密只能用公鑰來解密。常見的公鑰加密算法有:RSA、ElGamal、揹包算法、Rabin(RSA的特例)、迪菲-赫爾曼密鑰交換協議中的公鑰加密算法、橢圓曲線加密算法)

2.簡述 SSL 加密的過程用了哪些加密方法,爲何這麼作?

SSL 加密,在過程中實際使用了 對稱加密 和 非對稱加密 的結合。主要的考慮是先使用 非對稱加密 進行連接,這樣做是爲了避免中間人攻擊祕鑰被劫持,但是 非對稱加密 的效率比較低。所以一旦建立了安全的連接之後,就可以使用輕量的 對稱加密。

3.iOS的簽名機制是怎麼樣的

  • 簽名機制:
    先將應用內容通過摘要算法,得到摘要
    再用私鑰對摘要進行加密得到密文
    將源文本、密文、和私鑰對應的公鑰一併發佈
  • 驗證流程:
    查看公鑰是否是私鑰方的
    然後用公鑰對密文進行解密得到摘要
    將APP用同樣的摘要算法得到摘要,兩個摘要進行比對,如果相等那麼一切正常

性能優化

1.造成tableView卡頓的原因有哪些?

  • 1.最常用的就是cell的重用, 註冊重用標識符
    如果不重用cell時,每當一個cell顯示到屏幕上時,就會重新創建一個新的cell
    如果有很多數據的時候,就會堆積很多cell。
    如果重用cell,爲cell創建一個ID,每當需要顯示cell 的時候,都會先去緩衝池中尋找可循環利用的cell,如果沒有再重新創建cell
  • 2.避免cell的重新佈局
    cell的佈局填充等操作 比較耗時,一般創建時就佈局好
    如可以將cell單獨放到一個自定義類,初始化時就佈局好
  • 3.提前計算並緩存cell的屬性及內容
    當我們創建cell的數據源方法時,編譯器並不是先創建cell 再定cell的高度
    而是先根據內容一次確定每一個cell的高度,高度確定後,再創建要顯示的cell,滾動時,每當cell進入憑虛都會計算高度,提前估算高度告訴編譯器,編譯器知道高度後,緊接着就會創建cell,這時再調用高度的具體計算方法,這樣可以方式浪費時間去計算顯示以外的cell
  • 4.減少cell中控件的數量
    儘量使cell得佈局大致相同,不同風格的cell可以使用不用的重用標識符,初始化時添加控件,
    不適用的可以先隱藏
  • 5.不要使用ClearColor,無背景色,透明度也不要設置爲0
    渲染耗時比較長
  • 6.使用局部更新
    如果只是更新某組的話,使用reloadSection進行局部更
  • 7.加載網絡數據,下載圖片,使用異步加載,並緩存
  • 8.少使用addView 給cell動態添加view
  • 9.按需加載cell,cell滾動很快時,只加載範圍內的cell
  • 10.不要實現無用的代理方法,tableView只遵守兩個協議
  • 11.緩存行高:estimatedHeightForRow不能和HeightForRow裏面的layoutIfNeed同時存在,這兩者同時存在纔會出現“竄動”的bug。所以我的建議是:只要是固定行高就寫預估行高來減少行高調用次數提升性能。如果是動態行高就不要寫預估方法了,用一個行高的緩存字典來減少代碼的調用次數即可
  • 12.不要做多餘的繪製工作。在實現drawRect:的時候,它的rect參數就是需要繪製的區域,這個區域之外的不需要進行繪製。例如上例中,就可以用CGRectIntersectsRect、CGRectIntersection或CGRectContainsRect判斷是否需要繪製image和text,然後再調用繪製方法。
  • 13.預渲染圖像。當新的圖像出現時,仍然會有短暫的停頓現象。解決的辦法就是在bitmap context裏先將其畫一遍,導出成UIImage對象,然後再繪製到屏幕;
  • 14.使用正確的數據結構來存儲數據。

2.如何提升 tableview 的流暢度?

  • 本質上是降低 CPU、GPU 的工作,從這兩個大的方面去提升性能。
    CPU:對象的創建和銷燬、對象屬性的調整、佈局計算、文本的計算和排版、圖片的格式轉換和解碼、圖像的繪製
    GPU:紋理的渲染
  • 卡頓優化在 CPU 層面
    儘量用輕量級的對象,比如用不到事件處理的地方,可以考慮使用 CALayer 取代 UIView
    不要頻繁地調用 UIView 的相關屬性,比如 frame、bounds、transform 等屬性,儘量減少不必要的修改
    儘量提前計算好佈局,在有需要時一次性調整對應的屬性,不要多次修改屬性
    Autolayout 會比直接設置 frame 消耗更多的 CPU 資源
    圖片的 size 最好剛好跟 UIImageView 的 size 保持一致
    控制一下線程的最大併發數量
    儘量把耗時的操作放到子線程
    文本處理(尺寸計算、繪製)
    圖片處理(解碼、繪製)
    卡頓優化在 GPU層面
  • 儘量避免短時間內大量圖片的顯示,儘可能將多張圖片合成一張進行顯示
    GPU能處理的最大紋理尺寸是 4096x4096,一旦超過這個尺寸,就會佔用 CPU 資源進行處理,所以紋理儘量不要超過這個尺寸
    儘量減少視圖數量和層次
    減少透明的視圖(alpha<1),不透明的就設置 opaque 爲 YES
    儘量避免出現離屏渲染
  • iOS 保持界面流暢的技巧
    1.預排版,提前計算
    在接收到服務端返回的數據後,儘量將 CoreText 排版的結果、單個控件的高度、cell 整體的高度提前計算好,將其存儲在模型的屬性中。需要使用時,直接從模型中往外取,避免了計算的過程。
    儘量少用 UILabel,可以使用 CALayer 。避免使用 AutoLayout 的自動佈局技術,採取純代碼的方式
    2.預渲染,提前繪製
    例如圓形的圖標可以提前在,在接收到網絡返回數據時,在後臺線程進行處理,直接存儲在模型數據裏,回到主線程後直接調用就可以了
    避免使用 CALayer 的 Border、corner、shadow、mask 等技術,這些都會觸發離屏渲染。
    3.異步繪製
    4.全局併發線程
    5.高效的圖片異步加載

3.APP啓動時間應從哪些方面優化?

App啓動時間可以通過xcode提供的工具來度量,在Xcode的Product->Scheme–>Edit Scheme->Run->Auguments中,將環境變量DYLD_PRINT_STATISTICS設爲YES,優化需以下方面入手
dylib loading time

  • 核心思想是減少dylibs的引用
    合併現有的dylibs(最好是6個以內)
    使用靜態庫
  • rebase/binding time
    核心思想是減少DATA塊內的指針
    減少Object C元數據量,減少Objc類數量,減少實例變量和函數(與面向對象設計思想衝突)
    減少c++虛函數
    多使用Swift結構體(推薦使用swift)
  • ObjC setup time
    核心思想同上,這部分內容基本上在上一階段優化過後就不會太過耗時
    initializer time
  • 使用initialize替代load方法
    減少使用c/c++的attribute((constructor));推薦使用dispatch_once() pthread_once() std:once()等方法
    推薦使用swift
    不要在初始化中調用dlopen()方法,因爲加載過程是單線程,無鎖,如果調用dlopen則會變成多線程,會開啓鎖的消耗,同時有可能死鎖
    不要在初始化中創建線程

4.如何降低APP包的大小

降低包大小需要從兩方面着手

  • 可執行文件
    編譯器優化:Strip Linked Product、Make Strings Read-Only、Symbols Hidden by Default 設置爲 YES,去掉異常支持,Enable C++ Exceptions、Enable Objective-C Exceptions 設置爲 NO, Other C Flags 添加 -fno-exceptions 利用 AppCode 檢測未使用的代碼:菜單欄 -> Code -> Inspect Code
    編寫LLVM插件檢測出重複代碼、未被調用的代碼
  • 資源(圖片、音頻、視頻 等)
    優化的方式可以對資源進行無損的壓縮
    去除沒有用到的資源: https://github.com/tinymind/LSUnusedResources

5.如何檢測離屏渲染與優化

檢測,通過勾選Xcode的Debug->View Debugging–>Rendering->Run->Color Offscreen-Rendered Yellow項。
優化,如陰影,在繪製時添加陰影的路徑

6.怎麼檢測圖層混合

1、模擬器debug中color blended layers紅色區域表示圖層發生了混合

2、Instrument-選中Core Animation-勾選Color Blended Layers

避免圖層混合:

  • 確保控件的opaque屬性設置爲true,確保backgroundColor和父視圖顏色一致且不透明
    如無特殊需要,不要設置低於1的alpha值
  • 確保UIImage沒有alpha通道
    UILabel圖層混合解決方法:
    iOS8以後設置背景色爲非透明色並且設置label.layer.masksToBounds=YES讓label只會渲染她的實際size區域,就能解決UILabel的圖層混合問題
    iOS8 之前只要設置背景色爲非透明的就行
    爲什麼設置了背景色但是在iOS8上仍然出現了圖層混合呢?
    UILabel在iOS8前後的變化,在iOS8以前,UILabel使用的是CALayer作爲底圖層,而在iOS8開始,UILabel的底圖層變成了_UILabelLayer,繪製文本也有所改變。在背景色的四周多了一圈透明的邊,而這一圈透明的邊明顯超出了圖層的矩形區域,設置圖層的masksToBounds爲YES時,圖層將會沿着Bounds進行裁剪 圖層混合問題解決了

7.日常如何檢查內存泄露?

  • 目前我知道的方式有以下幾種
    Memory Leaks
    Alloctions
    Analyse
    Debug Memory Graph
    MLeaksFinder
  • 泄露的內存主要有以下兩種:
    Laek Memory 這種是忘記 Release 操作所泄露的內存。
    Abandon Memory 這種是循環引用,無法釋放掉的內存。

調試技巧

1.LLDB常用的調試命令?

  • po:print object的縮寫,表示顯示對象的文本描述,如果對象不存在則打印nil。
  • p:可以用來打印基本數據類型。
  • call:執行一段代碼 如:call NSLog(@"%@", @“yang”)
  • expr:動態執行指定表達式
  • bt:打印當前線程堆棧信息 (bt all 打印所有線程堆棧信息)
  • image:常用來尋找棧地址對應代碼位置 如:image lookup --address 0xxxx

2.斷點調試

  • 條件斷點
    打上斷點之後,對斷點進行編輯,設置相應過濾條件。下面簡單的介紹一下條件設置:
    Condition:返回一個布爾值,當布爾值爲真觸發斷點,一般裏面我們可以寫一個表達式。
    Ignore:忽略前N次斷點,到N+1次再觸發斷點。
    Action:斷點觸發事件,分爲六種:
    • AppleScript:執行腳本。
    • Capture GPU Frame:用於OpenGL ES調試,捕獲斷點處GPU當前繪製幀。
    • Debugger Command:和控制檯中輸入LLDB調試命令一致。
    • Log Message:輸出自定義格式信息至控制檯。
    • Shell Command:接收命令文件及相應參數列表,Shell Command是異步執行的,只有勾選“Wait until done”纔會等待Shell命令執行完在執行調試。
    • Sound:斷點觸發時播放聲音。
    • Options(Automatically continue after evaluating actions選項):選中後,表示斷點不會終止程序的運行。
  • 異常斷點
    異常斷點可以快速定位不滿足特定條件的異常,比如常見的數組越界,這時候很難通過異常信息定位到錯誤所在位置。這個時候異常斷點就可以發揮作用了。
    Exception:可以選擇拋出異常對象類型:OC或C++。
    Break:選擇斷點接收的拋出異常來源是Throw還是Catch語句。
  • 符號斷點
    符號斷點的創建方式和異常斷點一樣一樣的,在符號斷點中可以指定要中斷執行的方法:
    Symbol:[類名 方法名]可以執行到指定類的指定方法中開始斷點

3.iOS 常見的崩潰類型有哪些?

  • unrecognized selector crash
  • KVO crash
  • NSNotification crash
  • NSTimer crash
  • Container crash
  • NSString crash
  • Bad Access crash (野指針)
  • UI not on Main Thread Crash

iOS App 穩定性指標及監測

穩定性指標及監測

源碼理解

1…AFNetworking 底層原理分析

AFNetworking是封裝的NSURLSession的網絡請求,由五個模塊組成:分別由NSURLSession,Security,Reachability,Serialization,UIKit五部分組成

  • NSURLSession:網絡通信模塊(核心模塊) 對應 AFNetworking中的 AFURLSessionManager和對HTTP協議進行特化處理的AFHTTPSessionManager,AFHTTPSessionManager是繼承於AFURLSessionmanager的
  • Security:網絡通訊安全策略模塊 對應 AFSecurityPolicy
  • Reachability:網絡狀態監聽模塊 對應AFNetworkReachabilityManager
  • Seriaalization:網絡通信信息序列化、反序列化模塊 對應AFURLResponseSerialization
  • UIKit:對於iOS UIKit的擴展庫

2.SDWebImage加載圖片過程

0、首先顯示佔位圖
1、在webimagecache中尋找圖片對應的緩存,它是以url爲數據索引先在內存中查找是否有緩存;
2、如果沒有緩存,就通過md5處理過的key來在磁盤中查找對應的數據,如果找到就會把磁盤中的數據加到內存中,並顯示出來;
3、如果內存和磁盤中都沒有找到,就會向遠程服務器發送請求,開始下載圖片;
4、下載完的圖片加入緩存中,並寫入到磁盤中;
5、整個獲取圖片的過程是在子線程中進行,在主線程中顯示。

3.YYKit

YYKit 是一組龐大、功能豐富的 iOS 組件。
我們需要從組件架構,源碼,設計思路,解決問題的方案策略等多方面去學習。

4.Masonry

代碼管理

1.SVN與Git優缺點比較

SVN優缺點

  • 優點:
    1、管理方便,邏輯明確,符合一般人思維習慣。
    2、易於管理,集中式服務器更能保證安全性。
    3、代碼一致性非常高。
    4、 適合開發人數不多的項目開發。
  • 缺點:
    1、服務器壓力太大,數據庫容量暴增。
    2、如果不能連接到服務器上,基本上不可以工作,看上面第二步,如果服務器不能連接上,就不能提交,還原,對比等等。
    3、不適合開源開發(開發人數非常非常多,但是Google app engine就是用svn的)。但是一般集中式管理的有非常明確的權限管理機制(例如分支訪問限制),可以實現分層管理,從而很好的解決開發人數衆多的問題。

Git的優缺點

  • 優點
    1、適合分佈式開發,強調個體。
    2、公共服務器壓力和數據量都不會太大。
    3、速度快、靈活。
    4、任意兩個開發者之間可以很容易的解決衝突。
    5、離線工作。
  • 缺點
    1、學習週期相對而言比較長。
    2、不符合常規思維。
    3、代碼保密性差,一旦開發者把整個庫克隆下來就可以完全公開所有代碼和版本信息。

2.Git與SVN的區別

  • 1.Git是分佈式的,而SVN不是分佈式的
  • 2.Git把內容按元數據方式存儲,而SVN是按文件
  • 3、Git沒有一個全局版本號,SVN有,目前爲止這是SVN相比Git缺少的最大的一個特徵
  • 4、Git的內容的完整性要優於SVN: GIT的內容存儲使用的是SHA-1哈希算法。這能確保代碼內容的完整性,確保在遇到磁盤故障和網絡問題時降低對版本庫的破壞
  • 5、Git下載下來後,在OffLine狀態下可以看到所有的Log,SVN不可以
  • 6、SVN必須先Update才能Commit,忘記了合併時就會出現一些錯誤,git還是比較少的出現這種情況
  • 7、克隆一份全新的目錄以同樣擁有五個分支來說,SVN是同時復製5個版本的文件,也就是說重複五次同樣的動作。而Git只是獲取文件的每個版本的 元素,然後只載入主要的分支(master)在我的經驗,克隆一個擁有將近一萬個提交(commit),五個分支,每個分支有大約1500個文件的 SVN,耗了將近一個小時!而Git只用了區區的1分鐘
  • 8、版本庫(repository):SVN只能有一個指定中央版本庫。當這個中央版本庫有問題時,所有工作成員都一起癱瘓直到版本庫維修完畢或者新的版本庫設立完成。而 Git可以有無限個版本庫。或者,更正確的說法,每一個Git都是一個版本庫,區別是它們是否擁有活躍目錄(Git Working Tree)。如果主要版本庫(例如:置於GitHub的版本庫)發生了什麼事,工作成員仍然可以在自己的本地版本庫(local repository)提交,等待主要版本庫恢復即可。工作成員也可以提交到其他的版本庫
  • 9、分支(Branch)在SVN,分支是一個完整的目錄。且這個目錄擁有完整的實際文件。如果工作成員想要開啟新的分支,那將會影響“全世界”!每個人都會擁有和你一樣的分支。如果你的分支是用來進行破壞工作(安檢測試),那將會像傳染病一樣,你改一個分支,還得讓其他人重新切分支重新下載,十分狗血。而 Git,每個工作成員可以任意在自己的本地版本庫開啟無限個分支。舉例:當我想嘗試破壞自己的程序(安檢測試),並且想保留這些被修改的文件供日後使用, 我可以開一個分支,做我喜歡的事。完全不需擔心妨礙其他工作成員。只要我不合並及提交到主要版本庫,沒有一個工作成員會被影響。等到我不需要這個分支時, 我只要把它從我的本地版本庫刪除即可。無痛無癢。
    Git的分支名是可以使用不同名字的。例如:我的本地分支名爲OK,而在主要版本庫的名字其實是master。
    最值得一提,我可以在Git的任意一個提交點(commit point)開啓分支!(其中一個方法是使用gitk –all 可觀察整個提交記錄,然後在任意點開啟分支。)
  • 10、提交(Commit)在SVN,當你提交你的完成品時,它將直接記錄到中央版本庫。當你發現你的完成品存在嚴重問題時,你已經無法阻止事情的發生了。如果網路中斷,你根本沒辦法提交!而Git的提交完全屬於本地版本庫的活動。而你只需“推”(git push)到主要版本庫即可。Git的“推”其實是在執行“同步”(Sync)

持續集成

1.你在項目中使用過什麼持續集成方式?

Fastlane:一套用Ruby寫的自動化工具集,可用於iOS和Android的打包、發佈,節省了大量時間。Fastlane配置比較簡單,主要編寫集成的lane,然後在命令行操作即可

Jenkins:Jenkins比較受歡迎,插件衆多,但對新手來說配置可能稍微麻煩點。

2.jenkins怎麼備份恢復

只需要拷貝主home下面的 .jenkins打個包,下次要恢復就用這個覆蓋,所有的東西就都一模一樣了。其實就是配置的東西都在這裏面,插件的話有個Plugin的文件夾下面就是所有的插件的東西。

3.jenkins你都用了哪些插件?

  • Keychains and Provisioning Profiles Management:管理本地的keychain和iOS證書的插件
  • Xcode integration:用於xcode構建
  • GIT plugin/SVN:代碼管理插件

逆向(竊取密碼,防護破解,算法加密)

你想證書的大建行卡號大 商會大廈是大科技哈卡仕達刷卡機換手機號 炬華科技和空間和
的撒後即可好看哈哈哈建行卡號是的撒薩科技哈克時間和大聲道數據庫和空間和空間和可靠進口好借好還健康科技師大會盡快口水都d’s’k’j’h’j’k’j’k’h’j’k’j’j’j’h

Objective-C語言特性

KVO

  • KVO-鍵值觀察機制,原理如下:
    1.當給A類添加KVO的時候,runtime動態的生成了一個子類NSKVONotifying_A,讓A類的isa指針指向NSKVONotifying_A類,重寫class方法,隱藏對象真實類信息
    2.重寫監聽屬性的setter方法,在setter方法內部調用了Foundation的_NSSetObjectValueAndNotify 函數
  1. ()_NSSetObjectValueAndNotify函數內部
    • 首先會調用 willChangeValueForKey
    • 然後給屬性賦值
    • 最後調用 didChangeValueForKey
    • 最後調用 observer 的 observeValueForKeyPath 去告訴監聽器屬性值發生了改變
      4.重寫了dealloc做一些 KVO 內存釋放
3.如何手動觸發KVO方法
  • 手動調用willChangeValueForKey和didChangeValueForKey方法
  • 鍵值觀察通知依賴於 NSObject 的兩個方法: willChangeValueForKey: 和 didChangeValueForKey。在一個被觀察屬性發生改變之前, willChangeValueForKey: 一定會被調用,這就 會記錄舊的值。而當改變發生後, didChangeValueForKey 會被調用,繼而 observeValueForKey:ofObject:change:context: 也會被調用。如果可以手動實現這些調用,就可以實現“手動觸發”了 有人可能會問只調用didChangeValueForKey方法可以觸發KVO方法,其實是不能的,因爲willChangeValueForKey: 記錄舊的值,如果不記錄舊的值,那就沒有改變一說了

KVC

通知

屬性關鍵字

代理

代理模式是iOS中非常重要的一個模式,iOS SDK中的系統控件幾乎都用到了代理模式。代理模式用來處理事件監聽、參數傳遞功能。

屬性delegate需定義成weak,不可以是strong,兩者之間必須爲“非擁有關係“。委託對象(assistant類)會持有本對象(也就是上面例子的”Boss類“),而定義boss類的delegate屬性,如果delegate是strong定義的,則會被boss類所擁有。這樣就變成相互擁有了,造就著名的”保留環“

函數

協議

分類

1.分類和擴展有什麼區別?可以分別用來做什麼?分類有哪些侷限性?分類的結構體裏面有哪些成員?

Category

  • 可以減少單個文件的體積
  • 把不同功能組織到不同的Category裏
  • 可以由多個開發者共同完成一個類
  • 可以按需加載想要的category
  • 聲明私有方法
  • 模擬多繼承(一定要搞懂多繼承概念)
  • 把framework的私有方法公開(先了解framework)

Extension

  • Extension可以添加屬性和方法,且方法必須實現
  • 隱藏私有信息

區別

  • extension在編譯器決議,屬於類的一部分,category在運行時決議
  • extension必須在有一個類的源碼時才能添加,category不需要有類的源碼
  • extension可以添加實例變量,category不可以

Category的實現原理,以及Category爲什麼只能加方法不能加屬性?

分類的實現原理是將category中的方法,屬性,協議數據放在category_t結構體中,然後將結構體內的方法列表拷貝到類對象的方法列表中。 Category可以添加屬性,但是並不會自動生成成員變量及set/get方法。因爲category_t結構體中並不存在成員變量。通過之前對對象的分析我們知道成員變量是存放在實例對象中的,並且編譯的那一刻就已經決定好了。而分類是在運行時纔去加載的。那麼我們就無法再程序運行時將分類的成員變量中添加到實例對象的結構體中。因此分類中不可以添加成員變量。

Category中有load方法嗎?load方法是什麼時候調用的?load 方法能繼承嗎?

Category中有load方法,load方法在程序啓動裝載類信息的時候就會調用。load方法可以繼承。調用子類的load方法之前,會先調用父類的load方法

load、initialize的區別,以及它們在category重寫的時候的調用的次序。

  • 區別在於調用方式和調用時刻 調用方式:load是根據函數地址直接調用,initialize是通過objc_msgSend調用 調用時刻:load是runtime加載類、分類的時候調用(只會調用1次),initialize是類第一次接收到消息的時候調用,每一個類只會initialize一次(父類的initialize方法可能會被調用多次)
  • 調用順序:先調用類的load方法,先編譯那個類,就先調用load。在調用load之前會先調用父類的load方法。分類中load方法不會覆蓋本類的load方法,先編譯的分類優先調用load方法。initialize先初始化父類,之後再初始化子類。如果子類沒有實現+initialize,會調用父類的+initialize(所以父類的+initialize可能會被調用多次),如果分類實現了+initialize,就覆蓋類本身的+initialize調用。

@protocol和category中如何使用@property

  • 在 protocol 中使用 property 只會生成 setter 和 getter 方法聲明,我們使用屬性的目的,是希望遵守我協議的對象能實現該屬性
  • category 使用 @property 也是隻會生成 setter 和 getter 方法的聲明,如果我們真的需要給 category 增加屬性的實現,需要藉助於運行時的兩個函數:objc_setAssociatedObject和objc_getAssociatedObject

Category 結構體 category_t

struct category_t {
    const char *name;
    classref_t cls;
    struct method_list_t *instanceMethods; // 對象方法
    struct method_list_t *classMethods; // 類方法
    struct protocol_list_t *protocols; // 協議
    struct property_list_t *instanceProperties; // 屬性
    // Fields below this point are not always present on disk.
    struct property_list_t *_classProperties;

    method_list_t *methodsForMeta(bool isMeta) {
        if (isMeta) return classMethods;
        else return instanceMethods;
    }

    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};

擴展

面向對象三大特性(繼承,封裝,多態)

單例模式

實例方法和類方法

實例變量

屬性和方法

屬性和變量

類方法

通知

屬性關鍵字

UI視圖相關

UITableView相關

事件傳遞&視圖相應

圖像顯示原理

離屏渲染

繪製原理&異步繪製

卡頓&掉幀

內存管理

引用係數

弱引用

自動釋放池

循環引用

內存佈局

內存管理方案

數據結構

ARC&MRC

多線程

NSThread

GCD

NSOPeration

多線程和鎖

Runloop

概念

事件循環機制

RunLoop與NSTimer

RunLoop與多線程

數據結構

CFRunLoop

網絡相關

HTTP協議 概念 超文本傳輸協議

請求/相應報文
鏈接創立流程
  • 三次握手
  • 四次握手
HTTP的特點
  • 無連接
  • 無狀態

TCP/UDP傳輸層協議

TCP傳輸控制協議特點
UDP用戶數據報協議

DNS解析

瞭解DNS解析嗎?
DNS解析查詢方式
DNS的解析存在哪兒些常見的問題

怎麼解決DNS劫持

  • httpDNS
    使用HTTP協議向DNS服務器的80端口進行請求
  • 長鏈接

Session/Cookie

Session

Session也是用來記錄用戶狀態,區分用戶,狀態保存在服務器端

Cookie
  • Cookie主要用來記錄用戶狀態,區分用戶,狀態保存在客戶端
  • 如何修改Cookie
  • 怎麼保證Cookie的安全

設計模式(OC編程之道iOS設計模式解析)

  • 你的項目中用到了哪些設計模式,如何使用
  • 知道常用設計模式的優缺點
  • 能畫出常用設計模式的UML圖

01代理

02觀察者

03MVC

04單例(Singleton)

05策略

工廠

MVVM

原型(Prototype)

工廠方法(Factory Method)

抽象工廠(Abstract Factory)

生成器(Builder)

接口適配

適配器(Adapter)
橋接(Bridge)
外觀(Facade)

對象去耦

中介者(Mediator)
觀察者(Observer)

抽象集合

組合(Composite)
迭代器(Iterator)

行爲擴展

訪問者(Visitor)
裝飾(Decorator)
責任鏈(Chain of Responsibility)

算法封裝

模塊方法(Template Method)
策略(Strategy)
命令(Command)

性能與對象訪問

享元(Flyweight)
代理(Proxy)

對象狀態

備忘錄(Memento)

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-UqjK6Bue-1591231390713)(evernotecid://2CC0A8C7-733A-43F8-8428-C60C9C6C63B2/appyinxiangcom/21128449/ENResource/p391)]

框架-架構

模塊化
分層
解耦
降低代碼重合度

圖片緩存

如何設計一個圖片緩存框架Manager
  • 圖片來源
  • Code Manager
圖片通過什麼方式進行讀寫,過程是怎麼樣的

以圖片的URL的單向Hash值作爲key

內存設計上需要考慮哪些問題
  • 存儲的Size
  • 淘汰策略
    1.列隊
    2.LRU
磁盤設計需要考慮哪些問題
  • 存儲方式
  • 大小限制
  • 淘汰策略:如某一個圖片的存儲時間距離今天已經超過了7天
網絡設計部分需要考慮哪些問題
  • 圖片請求的最大併發量
  • 請求超時策略
  • 請求的優先級
對於不同格式的圖片,解碼採用什麼方式來做

應用策略模式對不同的圖片格式進行解碼

在哪個階段做圖片解碼處理
  • 磁盤讀取後
  • 網絡請求返回後
線程處理

閱讀時長統計

怎麼樣設計一個時長統計框架 記錄器
  • 記錄管理者
  • 頁面式
  • 流式
  • 自定義式
爲何有不同類型的記錄器,你的考慮是什麼

基於不同場景提供的關於記錄的封裝,適配

記錄的數據會由於某種原因丟失,你怎麼處理的
  • 定時寫磁盤
  • 限制內存緩存的條數(10條),超過該條數,寫入磁盤
關於延時上傳的具體場景有哪些
  • 前後臺切換
  • 有無網到有網的變化
  • 通過輕量接口捎帶
上傳時機是怎麼把控的
  • 立刻上傳
  • 延時上傳
  • 定時上傳

複雜頁面架構

MVVM框架思想
  • View 視圖層/ViewController/View
  • ViewModel 業務邏輯層
  • Model 數據層
ReactNative的數據流思想 -多叉樹
系統UIView更新機制的思想
FaceBook的開源框架ASyncDisplayKit關於預排版的設計思想

客戶端整體架構

概念
  • 業務A 業務B
  • 通用業務層
  • 獨立App的通用層
  • 中間層
業務之間的解耦通訊方式 OpenUR 依賴注入

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-MXp3oPlx-1591231390714)(evernotecid://2CC0A8C7-733A-43F8-8428-C60C9C6C63B2/appyinxiangcom/21128449/ENResource/p392)]

第三方庫

AFNetworking

SDWebIMage

ReactiveCocoa

Masonry

JSPath

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-RVnPfrIy-1591231390715)(evernotecid://2CC0A8C7-733A-43F8-8428-C60C9C6C63B2/appyinxiangcom/21128449/ENResource/p393)]

Runtime

數據結構

類對象與元類對象

消息轉發

動態添加方法

動態方法的解析

Method-Swizzling

Block

Block介紹

截獲變量

__block 修飾符

Block的內存管理

Block的循環引用

安全方案

本地數據存儲安全(Keychain)

授權和身份驗證

傳輸安全(對稱, 非對稱, SSL)

App代碼安全

函數式編程

RxSwift

ReactiveCocoa

工程相關

無論是企業開發人員還是獨立開發者,在需求、交互、視覺、開發一系列的步驟後,都會面對APP的測試、優化、上線、版本更新的問題,一個優秀的iOS開發者在面對這方面問題的時候,就需要有工程相關的知識,保證我們開發的APP能夠長期運行,高效優化。這部分我們必須瞭解的內容有以下幾部分:

版本管理工具和常用工作流

第三方庫管理工具

debug技能

性能調優

單元測試

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章