堆和棧都是一種數據項按序排列的數據結構,只能在一端(稱爲棧頂(top))對數據項進行插入和刪除。程序員
堆,隊列優先,先進先出(FIFO—first in first out);面試
棧,先進後出(FILO—First-In/Last-Out)。算法
通常狀況下,若是有人把堆棧合起來講,那它的意思是棧,而不是堆。編程
1.棧區(stack):由編譯器自動分配釋放,存放函數的參數值,局部變量等值。其操做方式相似於數據結構中的棧。swift
2.堆區(heap):通常由程序員分配釋放,若程序員不釋放,則可能會引發內存泄漏。其相似於鏈表。api
iOS 中應用程序使用的計算機內存不是統一分配空間,運行代碼使用的空間在三個不一樣的內存區域,分紅三個段:「text segment 「,「stack segment 」,「heap segment 」。緩存
代碼區(text segment ):安全
是應用程序運行時應用程序代碼存在的內存段,運行前就已經肯定(編譯時肯定),一般爲只讀的。代碼區的指令中包括操做碼和要操做的對象(或對象地址引用),代碼區指令根據程序設計流程依次執行,每個指令,每個單個函數、過程、方法和執行代碼都存在這個內存段中直到應用程序退出。通常使用中不多涉及。數據結構
棧(Stack):多線程
當咱們建立一個值類型,如結構體,系統將其存儲在一個被稱爲棧的內存區域中,是由CPU直接管理和優化的。當一個函數聲明一個變量,變量將存儲在棧中,當函數調用完畢後棧會自動釋放該變量。所以棧是很是易於管理的、有效的,因爲是CPU直接控制,速度很是快。
堆(Heap):
當咱們建立了一個引用類型,如類,系統將把類實例存儲在一個被稱爲堆的內存區域中。系統使用堆來存儲其餘對象引用的數據。
堆是一個大的內存池,系統能夠從該池中請求並動態分配內存塊。堆不會像棧同樣自動釋放對象,須要額外的工做來完成。這使得在堆中建立和刪除數據比棧慢。
棧使用的是一級緩存, 他們一般都是被調用時處於存儲空間中,調用完畢當即釋放。
堆則是存放在二級緩存中,生命週期由虛擬機的垃圾回收算法來決定(並非一旦成爲孤兒對象就能被回收)。因此調用這些對象的速度要相對來得低一些。
stack 中的一個指針僅僅是一個整型變量,保存了heap(堆)中特定內存地址的數據。簡而言之,操做系統使用stack 段中的指針值訪問heap 段中的對象。若是stack 對象的指針沒有了,則heap 中的對象就不能訪問。這也是內存泄露的緣由。
在iOS 操做系統的stack 段和heap 段中,你均可以建立數據對象。
stack 對象的優勢主要有兩點,一是建立速度快,二是管理簡單,它有嚴格的生命週期。stack 對象的缺點是它不靈活。建立時長度是多大就一直是多 大,建立時是哪一個函數建立的,它的owner 就一直是它。不像heap 對象那樣有多個owner ,其實多個owner 等同於引用計數。只有 heap 對象纔是採用「引用計數」方法管理它。
做爲一個開發者,有一個學習的氛圍跟一個交流圈子特別重要,這是一個個人iOS交流羣:519832104 無論你是小白仍是大牛歡迎入駐,分享經驗,討論技術,你們一塊兒交流學習成長!
另附上一份各好友收集的大廠面試題,須要iOS開發學習資料、面試真題,能夠添加iOS開發進階交流羣,進羣可自行下載!
堆(數據結構):堆能夠被當作是一棵樹,如:堆排序。
棧(數據結構):一種先進後出的數據結構。
堆和棧究竟有什麼區別? 主要的區別由如下幾點:
一、管理方式不一樣;
管理方式:對於棧來說,是由編譯器自動管理,無需咱們手工控制;對於堆來講,釋放工做由程序員控制,容易產生memory leak。
二、空間大小不一樣;
空間大小:棧是一塊空間較小,可是運行速度很快的內存區域。棧上的內存分配遵循後進先出的原則,經過移動棧的尾指針實現 push(入棧)和 pop(出棧)操做。咱們的程序是由一個個方法組成的,CPU 會負責調度並執行這些方法。當咱們的程序執行到某個方法的時候,須要在棧上爲方法須要的內存開闢空間,此時把棧的尾指針向棧底移動。當方法執行完畢後須要釋放掉這些空間,此時會把棧的尾指針移向棧頂,這就完成了一次棧上的內存分配。只要棧的剩餘空間大於stack 對象申請建立的空間,操做系統就會爲程序提供這段內存空間,不然將報異常提示棧溢出。
堆是內存中的另外一塊區域,空間比棧大的多,可是運行速度要比棧上的運行速度慢。堆能夠在運行時動態的分配內存,補充棧上內存分配的不足。通常來說在32位系統下,堆內存能夠達到4G的空間,從這個角度來看堆內存幾乎是沒有什麼限制的。
操做系統對於內存heap 段是採用鏈表進行管理的。操做系統有一個記錄空閒內存地址的鏈表,當收到程序的申請時,會遍歷鏈表,尋找第一個空間大於所申請的heap 節點,而後將該節點從空閒節點鏈表中刪除,並將該節點的空間分配給程序。iOS使用了名爲 ARC(自動引用計數)的技術。在多線程環境中,多個線程會共享堆上的內存,爲了確保線程安全,不得不在堆上進行加鎖操做,可是加鎖操做是很耗費性能的,你在堆上所獲的的數據安全性其實是在犧牲性能的代價下得來的。
三、可否產生碎片不一樣;
碎 片問題:對於堆來說,頻繁的new/delete勢必會形成內存空間的不連續,從而形成大量的碎片,使程序效率下降。對於棧來說,則不會存在這個問題,因 爲棧是先進後出的隊列,他們是如此的一一對應,以致於永遠都不可能有一個內存塊從棧中間彈出,在他彈出以前,在他上面的後進的棧內容已經被彈出。
四、生長方向不一樣;
生長方向:對於堆來說,生長方向是向上的,也就是向着內存地址增長的方向;對於棧來說,它的生長方向是向下的,是向着內存地址減少的方向增加。
五、分配方式不一樣;
分配方式:堆都是動態分配的,沒有靜態分配的堆。棧有2種分配方式:靜態分配和動態分配。靜態分配是編譯器完成的,好比局部變量的分配。動態分配由alloca函數進行分配,可是棧的動態分配和堆是不一樣的,他的動態分配是由編譯器進行釋放,無需咱們手工實現。
六、分配效率不一樣;
分 配效率:棧是機器系統提供的數據結構,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執行,這就決定了棧的效率比較 高。堆則是C/C++函數庫提供的,它的機制是很複雜的,例如爲了分配一塊內存,庫函數會按照必定的算法(具體的算法能夠參考數據結構/操做系統)在堆內 存中搜索可用的足夠大小的空間,若是沒有足夠大小的空間(多是因爲內存碎片太多),就有可能調用系統功能去增長程序數據段的內存空間,這樣就有機會分到 足夠大小的內存,而後進行返回。顯然,堆的效率比棧要低得多。
從這裏咱們能夠看到,堆和棧相比,因爲大量new/delete的使用,容 易形成大量的內存碎片;因爲沒有專門的系統支持,效率很低;因爲可能引起用戶態和核心態的切換,內存的申請,代價變得更加昂貴。
因此棧在程序中是應用最廣 泛的,就算是函數的調用也利用棧去完成,函數調用過程當中的參數,返回地址,局部變量都採用棧的方式存放。
但缺點是,存在棧中的數據大小與生存期必須是肯定的,缺少靈活性。另外,棧數據在多個線程或者多個棧之間是不能夠共享的,可是在棧內部多個值相等的變量是能夠指向一個地址的。和堆相比不是那麼靈活,有時候分配大量的內存空間,仍是用堆好一些。不管是堆仍是 棧,都要防止越界現象的發生(除非你是故意使其越界),由於越界的結果要麼是程序崩潰,要麼是摧毀程序的堆、棧結構,產生以想不到的結果,就算是在你的程 序運行過程當中,沒有發生上面的問題,你仍是要當心,說不定何時就崩掉了。
Swift 中的數據類型分爲引用類型(類)和值類型(枚舉、結構體)。引用類型存儲在 「堆」 上,值類型存儲在 「棧」 上。Swift 管理引用類型採用自動引用計數(ARC)的管理方法。值類型是由處理器來管理的,不須要程序員來管理。
在 Swift 中,典型的有 struct,enum,以及 tuple 都是值類型。而平時使用的Int,Double,Float,String,Array,Dictionary,Set 其實都是用結構體實現的,也是值類型。Swift 中,值類型的賦值爲深拷貝(Deep Copy),值語義(Value Semantics)即新對象和源對象是獨立的,當改變新對象的屬性,源對象不會受到影響,反之同理。
在 Swift 中,class 和閉包是引用類型。引用類型的賦值是淺拷貝(Shallow Copy),引用語義(Reference Semantics)即新對象和源對象的變量名不一樣,但其引用(指向的內存空間)是同樣的,所以當使用新對象操做其內部數據時,源對象的內部數據也會受到影響。
值類型做爲參數傳入時,函數體內部不能修改其值。引用類型做爲參數傳入時,函數體內部不能修改其指向的內存地址,可是能夠修改其內部的變量值。
值類型的優勢是:不變性,值類型的變量是嚴格的被一個全部者控制的;獨立性,引用類型是相互依賴的,是一種隱式的依賴;還有可交換性。
對於面向對象編程,因爲實例對象是可變的,致使對象的另外一個享有者在合適的時候會去改變這個對象的屬性。swift支持類的單繼承,致使從多個class繼承到更多地功能,增長了複雜度,而且會致使class緊耦合的問題。在多線程狀況下,能夠同時改變同一個引用。
選擇值類型而不是引用類型的一個主要緣由是能讓你的代碼變得更加簡單。Swift的核心是面向協議,引用類型有許多的享有者。值類型被賦給一個變量或者常量,傳給函數作參數時是它的值被拷貝的。這就讓值類型在任什麼時候候只有一個享有者,從而下降複雜度。你在任何狀況下用一個值類型,都可以假設你的其餘代碼不會使它改變,這一般在多線程環境中頗有用,若是一個線程中使用的數據被另外一個線程給意外的修改了,這一般會產生很是嚴重的Bug,且至關難以調試。Class = 高複雜度,值 = 低複雜度。並且,swift對值類型的操做上進行了一些優化,所以纔有了swift大量使用值類型代替引用類型的說法。
因爲只有當你須要修改數據時二者的區別纔會獲得體現,因此當你的實例不會對數據進行修改的時候,值類型和引用類型看起來是徹底相同的。你也許會想,寫一個徹底不可變的類,經過使用不可變的存儲屬性,以及避免暴露修改數據的接口,從而在Swift裏實現一個不可變的類。事實上,大多數的Cocoa類,好比NSURL等,都被設計爲不可變的類,然而,Swift當前並無提供任何語言機制去強制申明一個類不可改變(好比子類化就能修改一個類的實現),只有結構體和枚舉纔是強制不可變的。
在Swift裏,Array、String和Dictionary都是值類型,他們的行爲和C語言中的int相似,每一個實例都有本身的數據,你不須要額外作任何事情,好比作一個顯式的copy,防止其餘代碼在你不知情的狀況下修改等,更重要的是,你能安全地在線程間傳遞它,而不須要使用同步技術。在提升安全性的精神下,這個模型將幫助你在Swift中寫出更多可預知的代碼。
除此以外,Swift和OC還有其餘的類型對應,對應關係以下:
可是,須要關注的是,對於原來OC中的數據的引用類型,swift中並無真正徹底的實現一套數據存儲邏輯。只是內部保存了對oc對象的引用,使得swift api訪問時行爲邏輯和值類型一致