(FILO)
。主要用於存儲函數、方法、指針、臨時變量,簡單類型的變量。優勢是效率高。alloc
生成的對象,或者block
拷貝的對象等。相比棧區效率較低。.bbs
區:靜態區域,系統分配和釋放,用來存儲未初始化的全局變量、靜態變量。.data
區:系統分配和釋放,用來存儲初始化的全局變量、靜態變量。.text
區:系統分配和釋放,程序加載到內存中存儲的區域。- (void)test {
// 存放放在棧區
int a = 10
// &obj存放在棧區
// obj存放在堆區
NSObject *obj = [NSObject alloc];
}
複製代碼
棧效率比堆高的緣由以下:c++
cpu
命中率更高,知足局部性原理。在函數內部聲明,做用域只在當前{}
以內。 存儲位置:自動保存在函數的每次執行的【棧幀】中,並隨着函數結束後自動釋放。程序員
全局變量是一種在函數外聲明、能夠跨文件訪問的變量。它能夠在聲明時賦值,也能夠在使用的時候賦值,若是在聲明是沒有賦值,系統會默認分配爲空值。聲明方式以下:bash
NSString *global;
NSString *global = @"AAA";
複製代碼
全局變量保存在內存的全局存儲區中,佔用靜態的存儲單元;局部變量保存在棧中,只有在所在函數被調用時才動態地爲變量分配存儲單元。數據結構
咱們能夠在block
內部直接訪問全局變量,而不須要藉助其餘修飾詞。多線程
使用全局變量不能在.h
中定義,別的文件導入當前文件的時候會報錯,duplicate symbols for architecture x86_64
。由於在編譯階段,頭文件的信息都會copy
到文件中,這樣的話會出現全部引入的頭文件的都會有這麼一個全局變量,因此會出現重名的狀況。解決這種問題,咱們能夠將其定義在.m
文件中,而後在須要使用的地方使用extern
關鍵字獲取。架構
static
static
關鍵字修飾局部變量時,只會初始化一次,在內存中只有一塊地址。- (void)staticLocalTest {
for (int i = 0; i < 5; i++) {
static int num = 0;
num += 1;
NSLog(@"==static num==%d==", num);
}
}
複製代碼
能夠看出,被static
修飾的變量,延長了生命週期,本該在for
循環中每一次循環就銷燬,實際上只會初始化一次,在for
循環結束以後才銷燬。async
static
關鍵字修飾全局變量時,若是是在.m
文件中,做用域僅限於當前文件,外部類是不能夠訪問到該全局變量的;若是是在.h
文件中,能夠被任意引入當前文件做爲頭文件的文件所使用。static 類型 變量名;
複製代碼
靜態全局變量在每一個文件中都會單獨拷貝一份地址,互不影響。好比我在文件A
中定義了一份靜態全局變量,分別在A
、B
、C
三個文件中使用,他們的賦值是互不影響的。而全局變量是隻有一份地址,這是它們的不一樣點。ide
iOS
系統根據使用場景的不一樣,提供了3種方案:函數
TaggedPointer
:對於一些小對象,如NSNumber
、NSDate
等採用此種方案NONPOINTER_ISA
:64位架構下iOS
應用程序TaggedPointer
TaggedPointer
是蘋果從64bit
開始使用的一項內存管理技術,用於優化NSNumber
、NSDate
、NSString
等小對象的存儲。在沒有使用TaggedPointer
以前,這些小對象須要動態分配內存、維護引用計數等。使用了Tagged Pointer
,指針的值再也不是地址了,而是真正的值。實際上它再也不是一個對象了,而是一個普通變量而已。因此,它的內存並不存儲在堆中,不須要malloc
申請、或者是釋放。當指針不夠存儲數據時,纔會使用動態分配內存的方式來存儲數據。佈局
如:沒有使用TaggedPointer
的時候,NSNumber
指針存儲的是堆中NSNumber
對象的地址值。使用TaggedPointer
以後,NSNumber
指針裏面存儲的數據變成了:Tag + Data
,也就是將數據直接存儲在了指針中。
經過下面例子咱們來看一下系統對TaggedPointer
的特殊處理:
- (void)taggedpointerTest {
self.queue = dispatch_queue_create("com.cooci.cn", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i<10000; i++) {
dispatch_async(self.queue, ^{
self.nameStr = [NSString stringWithFormat:@"abc"];
NSLog(@"%@",self.nameStr);
});
}
for (int i = 0; i<10000; i++) {
dispatch_async(self.queue, ^{
self.nameStr = [NSString stringWithFormat:@"carshcarshcarshcarshcarshcarshcarshcarshcarshcarsh"];
NSLog(@"%@",self.nameStr);
});
}
}
複製代碼
運行程序,第二個for
循環會崩潰,提示objc_release
異常。崩潰的緣由是能夠看到是由於多線程訪問setter/getter
,那麼爲何第一個for
循環沒有崩潰呢?從代碼看兩個for
循環的區別就是第一個nameStr
賦值短,第二個長一些。打斷點,在控制檯查看信息,能夠看到第一個循環中的nameStr
是NSTaggedPointerString *
,而第二個是NSCFString *
。查看retain/release
的源碼,能夠看到:
id objc_retain(id obj)
{
if (!obj) return obj;
if (obj->isTaggedPointer()) return obj;
return obj->retain();
}
id objc_release(id obj)
{
if (!obj) return;
if (obj->isTaggedPointer()) return;
return obj->release();
}
複製代碼
剛纔咱們說了,TaggedPointer
的存儲是類型+數據
,那麼咱們來具體看一下,它的存儲策略:
NSNumber
、NSDate
、NSString
。如,0xb
表明NSNumber
。ASCII
碼編碼值來存儲字符NONPOINTER_ISA
NONPOINTER_ISA
是MACOSX_VERSION_10_11
以後出來的對isa
內存的優化。在isa
中添加了更多的信息。
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19
複製代碼
nonpointer
:表示是否對isa
指針開啓指針優化。0表示純isa
指針,11表示不止是類對象地址,isa
中包含了類信息、對象的引用計數等。has_assoc
:關聯對象標誌位,0沒有,1存在。has_cxx_dtor
:該對象是否有c++
或者objc
的析構器,若是有析構函數,則須要作析構邏輯,若是沒有,則能夠更快的釋放對象。shiftcls
:存儲類指針的值。開啓指針優化的狀況下,在arm64
架構中有33位用來存儲類指針。magic
:用於調試器判斷當前對象是真的對象仍是沒有初始化的空間。weakly_referenced
:志對象是否被指向或者曾經指向一個ARC
的弱變量,沒有弱引用的對象能夠更快釋放。deallocating
:標誌對象是否正在釋放內存。has_sidetable_rc
:當對象引用技術大於10時,則須要借用該變量存儲進位。extra_rc
:當表示該對象的引用計數值,其實是引用計數值減1, 例如,若是對象的引用計數爲10,那麼extra_rc
爲9。若是引用計數大於10,則須要使用到has_sidetable_rc
。散列表是一張哈希結構的表,其包含了自旋鎖、引用計數表、弱引用表。其中的引用計數表,就是來對內存管理作處理的。