iOS開發最新:各大廠面試題(二)

一、iOS程序內存分爲幾個區

iOS內存分爲5大區域

1. 棧區:編譯器自動分配並釋放,存放函數的參數值,局部變量等。棧是系統數據結構,對應線程/進程是唯一的。
2. 堆區:由程序員分配和釋放,如果程序員不釋放,程序結束時,可能會由操作系統回收 ,比如在iOSalloc 都是存放在堆中。
3. 全局區:全局變量和靜態變量的存儲是放在一起的,初始化的全局變量和靜態變量存放在一塊區域,未初始化的全局變量和靜態變量在相鄰的另一塊區域,程序結束後由系統釋放。
4. 文字常量區:存放常量字符串,程序結束後由系統釋放程序結束釋放。
5. 代碼區:存放函數的二進制代碼

二、iOS程序內存的每個分區怎麼存儲(舉例說明)

  • 棧區:存放的局部變量、先進後出、一旦出了作用域就會被銷燬;函數跳轉地址,現場保護等,內存地址從高到低分配。
  • 堆區:堆區的地址是從低到高分配,通過程序員通過alloc手動分配。
  • 全局區:包含兩個部分,未初始化區,初始化區域。全局變量和靜態變量在一塊區域,未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域;

代碼區存放於低地址,棧區存放於高地址。區與區之間並不是連續的。堆區的內存是應用程序共享的,堆中的內存分配是系統負責的;當引用計數爲0的時候,系統會回收該內存。

三、block一般存在哪裏(分ARC和MRC)

  • MRC 下,Block 默認是分配在棧上的,除非進行顯式執行的copy方法,只要block沒有引用外部的局部變量,block放在全局區裏面
  • ARC的中,對象默認是用__strong修飾的,所以大部分情況下編譯器都會將 block從棧自動複製到堆上。有一個特殊情況,如果僅僅定義了block沒有賦值給變量的話,仍是在棧上。這種情況下隨着作用域結束,block將會銷燬回收。

四、代碼區存儲的是什麼?

代碼區存放的是程序中函數編譯後的CPU指令

五、進程和線程的理解(從資源分配進行理解)

進程和線程都是由操作系統所體會的程序運行的基本單元,系統利用該基本單元實現系統對應用的併發性

1. 一個程序至少有一個進程,一個進程至少有一個線程。
2. 線程的劃分尺度小於進程,使得多線程程序的併發性高。

線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。線程自己基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧),但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源。

六、進程線程的內存分配和管理

當程序被運行時,需要將可執行文件加載到內存,在內存中的可執行文件形成進程,一個進程(文件)可以同時存在多個進程(內存)
運行程序的時候,需要將可執行文件加載到內存中,形成進程。每個進程佔據了一塊獨立的內存區域,這塊內存區域又劃分成不同的區域,從低地址到高地址依次爲:代碼區、只讀常量區、全局區/數據區、BSS段、堆區、棧區 。

7、多線程中哪些內存是共享哪些獨佔

多線程中,線程之間有共享資源和獨佔資源。
共享資源有:

1. 進程申請的堆內存
2. 進程打開的文件描述符
3. 進程的全局數據
4. 進程id,進程組id
5. 進程目錄
6. 信號處理器

獨佔資源有:

1. 線程ID
2. 寄存器組的值:每個線程有自己不同的運行線索,當從一個線 程切換到另一個線程上 時,必須將原有的線程的寄存器集合的狀態保存,以便將來該線程在被重新切換到時能得以恢復。
3. 線程堆棧
4. 錯誤返回碼
5. 信號屏蔽碼
6. 線程的優先級

八、實現多線程同步的方式

同步方式有互斥鎖(mutex),條件變量(condition variable)和讀寫鎖(reader-writer lock)來同步資源
iOS 互斥鎖有@synchronized
條件信號量dispatch_semaphore_t
NSConditionLock
讀寫鎖 pthread_rwlock

九、兩個異步子線程輸出字符串,主線程前後也輸出一個字符串,順序如何,爲什麼是這樣的?

先執行主線程種操作,在執行一步子線程操作,子線程在分配時遵循,新建,就續,運行,阻塞,死亡這個生命週期,而主線程已經運行狀態,所以會先運行主線程的操作,操作遵循FIFO的模式進行。子線程如果沒有特殊的優先級指定,默認處於同一優先級,所以也遵循FIFO的模式運行。

十、任務A,B,C先執行A和B再執行C可以怎麼實現(group,條件鎖,barrier)

1. group 通過創建信號量訪問資源數量爲1,然後通過waitsign順序執行group內線程。

2. NSConditionLock,通過控制創建條件和解鎖條件,順序執行線程。

let lock = NSConditionLock.init(condition: 3)
        DispatchQueue.global().async {
            lock.lock(whenCondition: 3)
            print("A")
            lock.unlock(withCondition: 2)
        }
        DispatchQueue.global().async {
                   lock.lock(whenCondition: 2)
                   print("B")
                   lock.unlock(withCondition: 1)
               }
        DispatchQueue.global().async {
                   lock.lock(whenCondition: 1)
                   print("C")
                   lock.unlock()
               }

3. barrier 允許在一個併發隊列中創建一個同步點。當在併發隊列中遇到一個barrier, 他會延遲執行barrierblock,等待所有在barrier之前提交的blocks執行結束。 這時,barrier block自己開始執行。

十一、屬性的修飾關鍵詞有哪些

見上一篇 面試題集

十二、atomicnonatomic的區別,如果是你覺得該怎麼實現atomic一樣的效果

主要區別在於atomic保證 getset 操作的完整性。
可以堆getset操作加鎖,實現atomic一樣的功能。

十三、atomic 一定是線程安全的嗎?什麼情況下是不安全的?

不一定是線程安全的,只保證了getset操作安全,但是不保證資源線程的安全。

如果一個線程正在getter 或者 setter時,有另外一個線程同時對該屬性進行release操作,如果release先完成,會造成crash

十四、copy常用來修飾什麼,爲什麼?

常常用來修飾NSString,使用copy修飾之後,即使屬性拷貝來自可變字符串,也會被深拷貝成不可變字符串,也就是源字符串修改之後不會影響到屬性字符串,增強了代碼的健壯性。

十五、weakassign 的區別

見上一篇 面試題集

十六、delegate你一般用什麼修飾(回答weak,爲什麼?可以用assign嗎)

見上一篇 面試題集

十七、循環引用(weak,用assign修飾block可以嗎)

見上一篇 面試題集

十八、KVO的實現原理(runtime)或者你要實現KVO你會怎麼做

KVO運用了一個isa-swizzling技術,isa-swizzling就是混合指針機制,將2個對象的isa指針互相調換。
當某個類的屬性對象第一次被觀察時,系統就會在運行期動態地創建該類的一個派生類,在這個派生類中重寫基類中任何被觀察屬性的 setter 方法。派生類在被重寫的setter方法內實現真正的通知機制。
每個類對象中都有一個isa指針指向當前類,當一個類對象的第一次被觀察,那麼系統會偷偷將isa指針指向動態生成的派生類,從而在給被監控屬性賦值時執行的是派生類的setter方法
鍵值觀察通知依賴於NSObject的兩個方法: willChangeValueForKey:didChangevlueForKey:
在一個被觀察屬性發生改變之前, willChangeValueForKey:一定會被調用,這就 會記錄舊的值。而當改變發生後,didChangeValueForKey:會被調用,繼而 observeValueForKey:ofObject:change:context:也會被調用。

十九、旋轉數組找最小數(算法)

思路:

  1. 從頭到尾遍歷數組一次,就能找出最小的元素,時間複雜度顯然是O(n)
  2. 通過二分查找的方式,時間複雜度爲O(logn)觀察一下數組的特性,首先遞增(稱爲遞增a),然後突然下降到最小值,然後再遞增(稱爲遞增b)。當然還有一種特殊情況,就是數組遞增,中間沒有下降,即旋轉元素個數爲0。對於一般的情況,假設A爲輸入數組,leftright 爲數組左右邊界的座標,考察中間位置的值A[mid],如果A[mid] <= A[right],表明處於遞增b,調整右邊界right = mid;如果A[mid] >= A[left],表明處於遞增a,因此調整左邊界left = mid。當左右邊界相鄰時,較小的一個就是數組的最小值。其實,對於一般情況,右邊界所指的元素爲最小值。對於特殊情況,即旋轉個數爲0。按照上述算法,右邊界會不斷減少,直到與左邊界相鄰。這時左邊界所指的元素爲最小值。
//# Swift 實現

func findMin( pArray:[Int]) -> Int{
        let len = pArray.count
        if len <= 0 { return 0 };
        var left:Int = 0
        var right:Int = len - 1
        var mid:Int = 0
        while(right - left != 1)
        {
            mid = left + ((right - left)>>1);
            if pArray[right] >= pArray[mid] {
                right = mid;
            } else if pArray[left] <= pArray[mid] {
                     left = mid
            }
        }
        return pArray[right] > pArray[left] ? pArray[left] : pArray[right]
    }

推薦文集

收錄:原文地址

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