您好,登錄后才能下訂單哦!
這篇文章主要介紹“iOS使用頻率最高的內(nèi)存管理有哪幾種”,在日常操作中,相信很多人在iOS使用頻率最高的內(nèi)存管理有哪幾種問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”iOS使用頻率最高的內(nèi)存管理有哪幾種”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
block內(nèi)存分為三種類型:
_NSConcreteGlobalBlock(全局)
_NSConcreteStackBlock(棧)
_NSConcreteMallocBlock(堆)
1)對于_NSConcreteStackBlock
和_NSConcreteGlobalBlock
類型_NSConcreteStackBlock
和_NSConcreteGlobalBlock
這兩種類型的block,我們可以手動創(chuàng)建,如下所示:
void (^globalBlock)() = ^{};int main(int argc, const char * argv[]) { @autoreleasepool { void (^stackBlock1)() = ^{ }; } return 0;}
那么我們怎么確定這兩個block,就是我們所說的兩種類型的block呢,我們可以使用clang -rewrite-objc xxx.m
(報錯可以使用詳細命令: clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk xxxxx.m
)編譯轉(zhuǎn)換成C++實現(xiàn),就可以看到轉(zhuǎn)換完的結(jié)果,如下所示:
// globalBlockstruct __globalBlock_block_impl_0 { struct __block_impl impl; struct __globalBlock_block_desc_0* Desc; __globalBlock_block_impl_0(void *fp, struct __globalBlock_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteGlobalBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};...// stackBlockstruct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};...int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; void (*stackBlock)() = (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA); } return 0;}
可以看出可以看出globalBlock是_NSConcreteGlobalBlock類型,即在全局區(qū)域創(chuàng)建,block變量存儲在全局數(shù)據(jù)存儲區(qū);stackBlock是_NSConcreteStackBlock類型,即在棧區(qū)創(chuàng)建。
2)對于_NSConcreteMallocBlock
類型NSConcreteMallocBlock
類型的內(nèi)存是通過_NSConcreteStackBlock
類型的block copy得到的,那么哪些類型會對block進行copy呢?
block作為返回值
// 如果是weak類型的block,依然不會自動進行copy// <__NSStackBlock__: 0x7fff5fbff728>__weak void (^weakBlock)() = ^{i;};// ARC情況下輸出// <__NSMallocBlock__NSLog(@"%@", [self callBack:weakBlock]);- (id)callBack:(void (^)(void))callBack{ NSLog(@"%@", callBack); return callBack;}//輸出結(jié)果<__NSStackBlock__: 0x7ffee2559838><__NSMallocBlock__: 0x600003a99ce0>
block作為屬性,使用copy修飾時(strong修飾符不會改變block內(nèi)存類型)
@property (copy, nonatomic) id myCopyBlock;@property (strong, nonatomic) id myStrongBlock;// 如果是weak類型的block,依然不會自動進行copy// <__NSStackBlock__: 0x7fff5fbff728>__weak void (^weakBlock)() = ^{i;};NSLog(@"%@", weakBlock);//會進行copy操作//<__NSMallocBlock__: 0x6000037e8db0>self.myCopyBlock = weakBlock;NSLog(@"%@", self.myCopyBlock);// 會進行strong操作// <__NSStackBlock__: 0x7fff5fbff728>self.myStrongBlock = weakBlock;NSLog(@"%@", self.myStrongBlock);//打印結(jié)果//<__NSStackBlock__: 0x7ffee8ed5838>//<__NSMallocBlock__: 0x6000037e8db0>//<__NSStackBlock__: 0x7ffee8ed5838>
block為strong類型,且捕獲了外部變量時。
int i = 10;void (^block)() = ^{i;};// 因為block為strong類型,且捕獲了外部變量,所以賦值時,自動進行了copy// <__NSMallocBlock__: 0x100206920>NSLog(@"%@", block);
對于作為參數(shù)傳遞的block,其類型是什么呢?
int i = 10;void (^block)() = ^{i;};__weak void (^weakBlock)() = ^{i;};void (^stackBlock)() = ^{};// ARC情況下// 創(chuàng)建時,都會在棧中// <__NSStackBlock__: 0x7fff5fbff730>NSLog(@"%@", ^{i;});// 因為block為strong類型,且捕獲了外部變量,所以賦值時,自動進行了copy// <__NSMallocBlock__: 0x100206920>NSLog(@"%@", block);// 如果是weak類型的block,依然不會自動進行copy// <__NSStackBlock__: 0x7fff5fbff728>NSLog(@"%@", weakBlock);// 如果block是strong類型,并且沒有捕獲外部變量,那么就會轉(zhuǎn)換成__NSGlobalBlock__// <__NSGlobalBlock__: 0x100001110>NSLog(@"%@", stackBlock);[self callBack:weakBlock];[self callBack:block];[self callBack:stackBlock];- (id)callBack:(void (^)(void))callBack{ NSLog(@"%@", callBack); return callBack;}//結(jié)果 //<__NSStackBlock__: 0x7ffee2572838>//<__NSMallocBlock__: 0x600002e881e0>// <__NSGlobalBlock__: 0x10d68c0f8>//<__NSStackBlock__: 0x7ffee2572838>//<__NSMallocBlock__: 0x600002e881e0>//<__NSGlobalBlock__: 0x10d68c0f8>
我們可以發(fā)現(xiàn)函數(shù)參數(shù)的block為什么類型,block在函數(shù)中就是什么類型。
1)enumerateObjectsUsingBlock中的對象
[NSArray array] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {//自動緩存池 }
2)__autoreleasing 修飾的對象
id obj = [NSObject new];id __autoreleasing o = obj;
3)array、dictiongnary、stringWithString等非init或者new方法生成的對象
int main(int argc, char * argv[]) {NSMutableArray *array = [NSMutableArray array];NSMutableArray *array1 = [NSMutableArray arrayWithCapacity:5];NSMutableDictionary *dict = [NSMutableDictionary dictionary];NSMutableString *str = [NSMutableString stringWithString:@"dsdsds"];
以上類型實驗結(jié)果:
int main(int argc, char * argv[]) { id obj = [NSObject new]; id __autoreleasing o = obj; id __autoreleasing o1 = obj; NSMutableArray *array = [NSMutableArray arrayWithCapacity:5]; [array addObject:@"0"]; [array addObject:@"1"]; [array addObject:@"2"]; [array addObject:@"3"]; [array addObject:@"4"]; [array addObject:@"5"]; [array addObject:@"6"]; NSMutableArray *array1 = [NSMutableArray array]; [array1 addObject:@"11"]; [array1 addObject:@"12"]; [array1 addObject:@"13"]; [array1 addObject:@"14"]; [array1 addObject:@"15"]; [array1 addObject:@"16"]; [array1 enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { id __autoreleasing o = obj; }]; NSMutableDictionary *dict = [NSMutableDictionary dictionary]; [dict setObject:@"1" forKey:@"1"]; NSMutableString *str = [NSMutableString stringWithString:@"dsdsds"];// _objc_autoreleasePoolPrint()}//在armv7上、使用_objc_autoreleasePoolPrint()調(diào)試打印結(jié)果 (lldb) po _objc_autoreleasePoolPrint()objc[96185]: ############## objc[96185]: AUTORELEASE POOLS for thread 0x20d080objc[96185]: 6 releases pending.objc[96185]: [0x7e115000] ................ PAGE (hot) (cold)objc[96185]: [0x7e115028] 0x7be71ca0 NSObject objc[96185]: [0x7e11502c] 0x7be71ca0 NSObject objc[96185]: [0x7e115030] 0x7c470560 __NSArrayM objc[96185]: [0x7e115034] 0x7be723b0 __NSArrayM objc[96185]: [0x7e115038] 0x7c170b80 __NSDictionaryM objc[96185]: [0x7e11503c] 0x7be72540 __NSCFString objc[96185]: ##############0x0a5c2500//在arm64的手機上、使用_objc_autoreleasePoolPrint()調(diào)試打印結(jié)果 (lldb) po _objc_autoreleasePoolPrint()objc[96400]: ############## objc[96400]: AUTORELEASE POOLS for thread 0x1151d75c0objc[96400]: 5 releases pending.objc[96400]: [0x7fae43000000] ................ PAGE (hot) (cold)objc[96400]: [0x7fae43000038] 0x600003a6c840 __NSArrayI//系統(tǒng)創(chuàng)建對象objc[96400]: [0x7fae43000040] 0x600000c358b0 __NSSetI//系統(tǒng)創(chuàng)建對象objc[96400]: [0x7fae43000048] 0x600002d380d0 NSObject objc[96400]: [0x7fae43000050] 0x600002d380d0 NSObject objc[96400]: [0x7fae43000058] 0x6000021649f0 __NSArrayM objc[96400]: ##############0xe0675b6edaa1003f(lldb) po 0x6000021649f0<__NSArrayM 0x600001435d70>(0,1,2,3,4,5,6)
注意:這里面的實驗結(jié)果不一樣,在arm64上、array、dictiongnary、stringWithString等方法生成的對象
,在自動緩存池中只能看見第一個對象,而armv7的機型上,可以看見所有的,不知這里是什么原因,有知道的歡迎告訴我
兩個常用的調(diào)試命令
//打印自動緩存池對象_objc_autoreleasePoolPrint()//打印引用計數(shù)_objc_rootRetainCount(obj)
1)系統(tǒng)通過runloop創(chuàng)建的autoreleasePool
runloop 可以說是iOS 系統(tǒng)的靈魂。內(nèi)存管理/UI 刷新/觸摸事件這些功能都需要 runloop 去管理和實現(xiàn)。runloop是通過線程創(chuàng)建的,和線程保持一對一的關系,其關系是保存在一個全局的 Dictionary 里。線程剛創(chuàng)建時并沒有 RunLoop,如果你不主動獲取,那它一直都不會有。RunLoop 的創(chuàng)建是發(fā)生在第一次獲取時,RunLoop 的銷毀是發(fā)生在線程結(jié)束時。你只能在一個線程的內(nèi)部獲取其 RunLoop(主線程除外)。
runloop和autoreleasePool又是什么關系呢?對象又是什么時候釋放的?
App啟動后,蘋果在主線程 RunLoop 里注冊了兩個 Observer,其回調(diào)都是 _wrapRunLoopWithAutoreleasePoolHandler()。
第一個 Observer 監(jiān)視的事件是 Entry(即將進入Loop),其回調(diào)內(nèi)會調(diào)用 _objc_autoreleasePoolPush() 創(chuàng)建自動釋放池。其 order 是-2147483647,優(yōu)先級最高,保證創(chuàng)建釋放池發(fā)生在其他所有回調(diào)之前。
第二個 Observer 監(jiān)視了兩個事件: BeforeWaiting(準備進入休眠) 時調(diào)用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 釋放舊的池并創(chuàng)建新池;Exit(即將退出Loop) 時調(diào)用 _objc_autoreleasePoolPop() 來釋放自動釋放池。
這個 Observer 的 order 是 2147483647,優(yōu)先級最低,保證其釋放池子發(fā)生在其他所有回調(diào)之后。
在主線程執(zhí)行的代碼,通常是寫在諸如事件回調(diào)、Timer回調(diào)內(nèi)的。這些回調(diào)會被 RunLoop 創(chuàng)建好的 AutoreleasePool 環(huán)繞著,所以不會出現(xiàn)內(nèi)存泄漏,開發(fā)者也不必顯示創(chuàng)建 Pool 了。
驗證結(jié)果:
int main(int argc, char * argv[]) { id obj = [NSObject new]; id __autoreleasing o = obj; id __autoreleasing o1 = obj; NSMutableArray *array = [NSMutableArray arrayWithCapacity:5]; [array addObject:@"0"]; [array addObject:@"1"]; [array addObject:@"2"]; [array addObject:@"3"]; [array addObject:@"4"]; [array addObject:@"5"]; [array addObject:@"6"];// _objc_autoreleasePoolPrint()}//_objc_autoreleasePoolPrint調(diào)試打印結(jié)果(lldb) po _objc_autoreleasePoolPrint()objc[99121]: ############## objc[99121]: AUTORELEASE POOLS for thread 0x107b0d5c0objc[99121]: 5 releases pending.objc[99121]: [0x7f93b2002000] ................ PAGE (hot) (cold)objc[99121]: [0x7f93b2002038] 0x6000000d66c0 __NSArrayI objc[99121]: [0x7f93b2002040] 0x6000036b9680 __NSSetI objc[99121]: [0x7f93b2002048] 0x600001780160 NSObject objc[99121]: [0x7f93b2002050] 0x600001780160 NSObject objc[99121]: [0x7f93b2002058] 0x600001bcd230 __NSArrayM objc[99121]: ##############0x67c4279ea7c20079(lldb) po 0x600001bcd230<__NSArrayM 0x600001bcd230>(0,1,2,3,4,5,6)(lldb) po [NSThread currentThread]<NSThread: 0x6000000953c0>{number = 1, name = main}
2)手動autoreleasePool
我們可以通過@autoreleasepool {}
方式手動創(chuàng)建autoreleasepool對象,那么這個對象什么時候釋放呢?答案是除了autoreleasepool的大括號就釋放了,我們可以看下下面的實驗結(jié)果
int main(int argc, char * argv[]) { //1\. _objc_autoreleasePoolPrint() @autoreleasepool { id obj = [NSObject new]; id __autoreleasing o = obj; id __autoreleasing o1 = obj;//2\. _objc_autoreleasePoolPrint() }//3\. _objc_autoreleasePoolPrint()} //1\. _objc_autoreleasePoolPrint() (lldb) po _objc_autoreleasePoolPrint()objc[1555]: ############## objc[1555]: AUTORELEASE POOLS for thread 0x11331a5c0objc[1555]: 2 releases pending.0x2196ee78f1e100fdobjc[1555]: [0x7fc2a9802000] ................ PAGE (hot) (cold)objc[1555]: [0x7fc2a9802038] 0x600002dbb600 __NSArrayI objc[1555]: [0x7fc2a9802040] 0x600001bd8a50 __NSSetI objc[1555]: ############## //2\. _objc_autoreleasePoolPrint() (lldb) po _objc_autoreleasePoolPrint()objc[1555]: ############## objc[1555]: AUTORELEASE POOLS for thread 0x11331a5c00x2196ee78f1e100fdobjc[1555]: 5 releases pending.objc[1555]: [0x7fc2a9802000] ................ PAGE (hot) (cold)objc[1555]: [0x7fc2a9802038] 0x600002dbb600 __NSArrayI objc[1555]: [0x7fc2a9802040] 0x600001bd8a50 __NSSetI objc[1555]: [0x7fc2a9802048] ################ POOL 0x7fc2a9802048objc[1555]: [0x7fc2a9802050] 0x600003afc030 NSObject objc[1555]: [0x7fc2a9802058] 0x600003afc030 NSObject objc[1555]: ############## //3\. _objc_autoreleasePoolPrint() (lldb) po _objc_autoreleasePoolPrint()objc[1555]: ############## objc[1555]: AUTORELEASE POOLS for thread 0x11331a5c00x2196ee78f1e100fdobjc[1555]: 2 releases pending.objc[1555]: [0x7fc2a9802000] ................ PAGE (hot) (cold)objc[1555]: [0x7fc2a9802038] 0x600002dbb600 __NSArrayI objc[1555]: [0x7fc2a9802040] 0x600001bd8a50 __NSSetI objc[1555]: ##############(lldb)
從上面1、2、3的結(jié)果可以看出,當對象出了autoreleasepool的大括號就釋放了。
3、子線程的autoreleasepool對象的管理?
線程剛創(chuàng)建時并沒有 RunLoop,如果你不主動獲取,那它一直都不會有。所以在我們創(chuàng)建子線程的時候,如果沒有獲取runloop,那么也就沒用通過runloop來創(chuàng)建autoreleasepool,那么我們的autorelease對象是怎么管理的,會不會存在內(nèi)存泄漏呢?答案是否定的,當子線程有autoreleasepool的時候,autorelease對象通過其來管理,如果沒有autoreleasepool,會通過調(diào)用 autoreleaseNoPage 方法,將對象添加到 AutoreleasePoolPage 的棧中,也就是說你不進行手動的內(nèi)存管理,也不會內(nèi)存泄漏啦!這部分我們可以看下
runtime中NSObject.mm的部分,有相關代碼。
static inline id *autoreleaseFast(id obj){ AutoreleasePoolPage *page = hotPage(); if (page && !page->full()) { return page->add(obj); } else if (page) { return autoreleaseFullPage(obj, page); } else { //調(diào)用 autoreleaseNoPage 方法管理autorelease對象。 return autoreleaseNoPage(obj); }}
作為一個開發(fā)者,有一個學習的氛圍跟一個交流圈子特別重要,這是一個我的iOS交流群: 519832104 不管你是小白還是大牛歡迎入駐,分享經(jīng)驗,討論技術,大家一起交流學習成長!
另附上一份各好友收集的大廠面試題,需要iOS開發(fā)學習資料、面試真題,可以添加iOS開發(fā)進階交流群,進群可自行下載!
1.釋放時機
在dealloc的時候,會將weak屬性的值設置為nil
2.如何實現(xiàn)
Runtime維護了一個weak表,用于存儲指向某個對象的所有weak指針,對于 weak 對象會放入一個 hash 表中,Key是所指對象的地址,Value是weak指針的地址(這個地址的值是所指對象的地址)數(shù)組。 當此對象的引用計數(shù)為0的時候會 dealloc,假如 weak 指向的對象內(nèi)存地址是a,那么就會以a為鍵, 在這個 weak 表中搜索,找到所有以a為鍵的 weak 對象,從而設置為 nil。
注:由于可能多個weak指針指向同一個對象,所以value為一個數(shù)組
weak 的實現(xiàn)原理可以概括以下三步:
1)初始化時:runtime會調(diào)用objc_initWeak函數(shù),初始化一個新的weak指針指向?qū)ο蟮牡刂贰?br/>我們以下面這行代碼為例:
代碼清單1:示例代碼
{ id __weak obj1 = obj;}
當我們初始化一個weak變量時,runtime會調(diào)用objc_initWeak函數(shù)。這個函數(shù)在Clang中的聲明如下:
id objc_initWeak(id *object, id value);
其具體實現(xiàn)如下:
id objc_initWeak(id *object, id value){ *object = 0; return objc_storeWeak(object, value);}
示例代碼輪換成編譯器的模擬代碼如下:
id obj1;objc_initWeak(&obj1, obj);
因此,這里所做的事是先將obj1初始化為0(nil),然后將obj1的地址及obj作為參數(shù)傳遞給objc_storeWeak函數(shù)。
objc_initWeak函數(shù)有一個前提條件:就是object必須是一個沒有被注冊為__weak對象的有效指針。而value則可以是null,或者指向一個有效的對象。
2)添加引用時:objc_initWeak函數(shù)會調(diào)用 objc_storeWeak() 函數(shù)。
objc_storeWeak() 的作用是更新指針指向,創(chuàng)建對應的弱引用表。
3)釋放時,調(diào)用clearDeallocating函數(shù)。
clearDeallocating函數(shù)首先根據(jù)對象地址獲取所有weak指針地址的數(shù)組,然后遍歷這個數(shù)組把其中的數(shù)據(jù)設為nil,最后把這個entry從weak表中刪除,最后清理對象的記錄。
NSString內(nèi)存分為兩種類型:
__NSCFConstantString(常量區(qū))
__NSCFString(堆區(qū))、NSTaggedPointerString(堆區(qū))
生成一個NSString類型的字符串有三種方法:
方法1.直接賦值:
NSString *str1 = @"my string";
方法2.類函數(shù)初始化生成:
NSString *str2 = [NSString stringWithString:@"my string"];
方法3.實例方法初始化生成:
NSString *str3 = [[NSString alloc] initWithString:@"my string"];NSString *str4 = [[NSString alloc]initWithFormat:@"my string"];
1)對于__NSCFConstantString
這種類型的字符串是常量字符串。該類型的字符串以字面量的方式創(chuàng)建,保存在字符串常量區(qū),是在編譯時創(chuàng)建的。
NSString *a = @"str";NSString *b = [[NSString alloc]init];NSString *c = [[NSString alloc]initWithString:@"str"];NSString *d = [NSString stringWithString:@"str"];NSLog(@"%@ : class = %@",a,NSStringFromClass([a class]));NSLog(@"%@ : class = %@",b,NSStringFromClass([b class]));NSLog(@"%@ : class = %@",c,NSStringFromClass([c class]));NSLog(@"%@ : class = %@",d,NSStringFromClass([d class]));//打印結(jié)果2019-06-23 19:23:13.240611+0800 BlockDemo[47229:789011] str : class = __NSCFConstantString2019-06-23 19:23:13.240764+0800 BlockDemo[47229:789011] : class = __NSCFConstantString2019-06-23 19:23:13.240870+0800 BlockDemo[47229:789011] str : class = __NSCFConstantString2019-06-23 19:23:13.240957+0800 BlockDemo[47229:789011] str : class = __NSCFConstantString
2)對于__NSCFString
和NSTaggedPointerString
__NSCFString 表示對象類型的字符串,在運行時創(chuàng)建,保存在堆區(qū),初始引用計數(shù)為1,其內(nèi)存管理方式就是對象的內(nèi)存管理方式。
NSTaggedPointerString是對__NSCFString類型的一種優(yōu)化,在運行創(chuàng)建字符串時,會對字符串內(nèi)容及長度作判斷,若內(nèi)容由ASCII字符構(gòu)成且長度較小(具體要多小暫時不太清楚),這時候創(chuàng)建的字符串類型就是 NSTaggedPointerString
對于不可以變NSString的測試結(jié)果:
NSString *e = [[NSString alloc]initWithFormat:@"str"];NSString *f = [NSString stringWithFormat:@"str"];NSString *g = [NSString stringWithFormat:@"123456789"];NSString *h = [NSString stringWithFormat:@"1234567890"];NSLog(@"%@ : class = %@",e,NSStringFromClass([e class]));NSLog(@"%@ : class = %@",f,NSStringFromClass([f class]));NSLog(@"%@ : class = %@",g,NSStringFromClass([g class]));NSLog(@"%@ : class = %@",h,NSStringFromClass([h class]));//打印結(jié)果2019-06-23 19:27:19.115212+0800 BlockDemo[48129:794364] str : class = NSTaggedPointerString2019-06-23 19:27:19.115286+0800 BlockDemo[48129:794364] str : class = NSTaggedPointerString2019-06-23 19:27:19.115388+0800 BlockDemo[48129:794364] 123456789 : class = NSTaggedPointerString2019-06-23 19:27:19.115476+0800 BlockDemo[48129:794364] 1234567890 : class = __NSCFString
對于可變的NSMutableString
NSMutableString *ms1 = [[NSMutableString alloc]init];NSMutableString *ms2 = [[NSMutableString alloc]initWithString:@"str"];NSMutableString *ms3 = [[NSMutableString alloc]initWithFormat:@"str"];NSMutableString *ms4 = [NSMutableString stringWithFormat:@"str"];NSMutableString *ms5 = [NSMutableString stringWithFormat:@"123456789"];NSMutableString *ms6 = [NSMutableString stringWithFormat:@"1234567890"];NSLog(@"%@ : class = %@",ms1,NSStringFromClass([ms1 class]));NSLog(@"%@ : class = %@",ms2,NSStringFromClass([ms2 class]));NSLog(@"%@ : class = %@",ms3,NSStringFromClass([ms3 class]));NSLog(@"%@ : class = %@",ms4,NSStringFromClass([ms4 class]));NSLog(@"%@ : class = %@",ms5,NSStringFromClass([ms5 class]));NSLog(@"%@ : class = %@",ms6,NSStringFromClass([ms6 class]));//打印結(jié)果2019-06-23 19:34:08.521931+0800 BlockDemo[49465:802590] : class = __NSCFString2019-06-23 19:34:08.522058+0800 BlockDemo[49465:802590] str : class = __NSCFString2019-06-23 19:34:08.522131+0800 BlockDemo[49465:802590] str : class = __NSCFString2019-06-23 19:34:08.522196+0800 BlockDemo[49465:802590] str : class = __NSCFString2019-06-23 19:34:08.522281+0800 BlockDemo[49465:802590] 123456789 : class = __NSCFString2019-06-23 19:34:08.522372+0800 BlockDemo[49465:802590] 1234567890 : class = __NSCFString
從結(jié)果我們可以看出來NSMutableString都是分配在堆區(qū),且是__NSCFString類型,NSString中Format相關方法也是都分配在堆區(qū),但是會根據(jù)字符串的長度,區(qū)分為__NSCFString和NSTaggedPointerString兩種。在分配堆區(qū)的這些變量,其實一部分是正常的對象,一部分變成autorelease對象,具體是哪些,我們可以使用_objc_autoreleasePoolPrint()打印出來,比如實例中的g、ms4、ms5、ms6。
到此,關于“iOS使用頻率最高的內(nèi)存管理有哪幾種”的學習就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關知識,請繼續(xù)關注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。