[objective-c] 08 - 内存管理
?
OC語言中的內存管理機制為ARC(Automatic Reference Counting,自動引用計數)。于2011年中旬推出,替換陳舊且低效的手動內存管理,關于手動內存管理的內容,本章教程不再講授。本章主要從以下幾個方面對內存管理進行展開講解。
- 內存管理原則
- 對象引用類型
- 屬性引用類型
- 強引用循環
- AUTO類型與釋放池
1.內存管理原則
核心原則:沒有被對象指針使用(指向)的內存立即釋放。這個原則決定一般情況下,不會有內存泄露的情況存在,但存在特殊情況,也是本章最后一個專題要闡述的問題。
內存泄露是指,一塊沒有被指針引用的內存,但由于一些原因,無法釋放,造成內存浪費的情況。
管理原則
- 強引用對象指針使用中的內存絕對不會釋放。
- 歸零弱引用對象指針使用中的內存,釋放情況由其他對象指針決定,本身不影響內存釋放與否,但其指向的內存一旦釋放,本身立即置nil,保證不出現野指針。
- 弱引用對象指針使用中的內存,釋放情況由其他對象指針決定,本身不影響內存釋放與否,但其指向的內存一旦釋放,本身值不會發生改變,會出現野指針的情況。
- AUTO類型對象指針使用過或使用中的內存,出釋放池才會釋放。通過AUTO類型與釋放池配合使用,可以精確調節內存時間,提前或延后。
2.對象引用類型
對象引用類型有如下四種
- 強引用:__strong修飾的對象指針或無修飾的對象指針
- 歸零弱引用:__weak修飾的對象指針
- 弱引用:__unsafe__unretain修飾的對象指針
- AUTO類型:__autoreleasing修飾的對象指針
首先分析強引用對象指針的使用情況。在分析內存釋放情況時,我們需要一個測試類進行釋放測試。當一個對象釋放時,它的dealloc方法會被調用。所以我們在dealloc方法中進行相關輸出,便能精確看到,該對象何時釋放。
@interface Test : NSObject@end @implementation Test - (void)dealloc { NSLog(@"該對象釋放"); } @end情況1
int main(int argc, const char * argv[]) { { Test * t = [[Test alloc]init]; } //代碼運行至此,t的作用域結束,t指向的內存并無其他對象指針使用,所以,該內存在此釋放。 return 0; }情況2
int main(int argc, const char * argv[]) { { Test * t1; { Test * t = [[Test alloc]init]; t1 = t; } //代碼運行至此,t作用域結束,但t指向的內存仍有t1對象指針使用,所以在此該內存不會釋放。 } //代碼運行至此,t1作用域結束,t1指向的內存再無其他對象指針使用,所以在此內存釋放。 return 0; }情況3
int main(int argc, const char * argv[]) { { Test * t = [[Test alloc]init]; t = nil;//代碼運行至此,t不再指向之前分配的內存,之前的內存無對象指針使用,釋放。 } return 0; }以上是強引用的常用情況,其對象指針并無任何修飾符進行修飾,但已經OC語法規定,對象指針無修飾時,為強引用類型。
我們繼續討論歸零弱引用類型的對象指針對內存的影響。
情況1
int main(int argc, const char * argv[]) { { __weak Test * t1; { Test * t = [[Test alloc]init]; t1 = t; } //代碼運行至此,t作用域結束,t指向的內存仍有t1對象指針使用,但t1為歸零弱引用,不會影響對象釋放情況,所以在此內存釋放,且t1本身值變為nil } return 0; }情況2
int main(int argc, const char * argv[]) { __weak Test * t1 = [[Test alloc]init];//在此語句運行時,分配的內存并無除弱引用對象指針以外的對象指針使用,所以,該內存立即釋放。 return 0; }以上是歸零弱引用的情況,不常用。歸零弱引用只有一種情況下使用:
- 代碼塊回調接口中的自身代碼塊引用自身對象
例如:
@interface Room : NSObject @property (strong, nonatomic) Light *lightA; @property (strong, nonatomic) SwitchB *s; @end @implementation Room - (instancetype)init { self = [super init]; if (self) { self.lightA = [[Light alloc] init]; self.s = [[SwitchB alloc] init]; __weak __block Room * copy_self = self;//打破強引用循環,強引用循環的概念下文會講解。 self.s.changeStateBlockHandle = ^(SwitchState state) { if (state == SwitchStateOff) { [self.lightA turnOff]; } else { [self.lightA turnOn]; } }; } return self; } @end弱引用和歸零弱引用管理內存的釋放時間相同。弱引用是OC為兼容之前特殊情況下的內存管理而做的一個不常用類型。所以之后,我們不會使用弱引用,所有需要弱引用的地方全部以歸零弱引用代替。
3.屬性引用類型
屬性的內存控制符,有四種情況。
- strong
- weak
- copy
- assgin
對于對象來說,可選的只有前三種。第四種,assgin為無內存管理,主要針對基礎數據類型設計。例如
@property (assgin, nonatomic) int a;如果一個屬性是對象,那么其屬性內存控制符必然是前三種之一。
strong:默認情況下使用,除特殊情況。
weak:在遇到強引用循環時,使用。
copy:在進行數值對象賦值時,使用。
例如
@property (copy, nonatomic) NSString * name; @property (copy, nonatomic) NSNumber * age;但也可以strong代替,所以,copy使用場景也不多見。
4.強引用循環
在內存管理中,強引用循環是最嚴重的失誤,會造成大量內存泄露。通過一個例子來說明為什么會產生泄露。
首先用實際生活中的一個場景來具體的說明一下,問題產生的原因。
例如,現在在一個教室,有學生有老師。老師對自己的要求是,學生不離開教室,我就不離開教室。而學生對自己的要求是,老師不離開教室,我就不離開教室。這樣一直持續下去的結果就是,雙方誰都不會離開教室。
然后我們再看一下代碼中何時會產生這種情況。
@interface Student : NSObject@property (strong, nonatomic) Teacher *tea; @end @interface Teacher : NSObject@property (strong, nonatomic) Student *stu; @end main() {{Teacher * tea = [[Teacher alloc] init];Student * stu = [[Student alloc] init];tea.stu = stu;stu.tea = tea;}}上述代碼中,可以發現,Student類有一個strong類型的屬性tea,通過管理原則我們可以知道,stu對象存在其強引用屬性tea一定存在,不會釋放。同樣Teacher有一個strong屬性stu,tea對象存在意味著stu對象也絕對不會釋放。這樣當兩個對象指針作用域消失時,其使用的內存無法釋放,出現內存泄露。
這種問題便是內存管理中會遇到的強引用循環,也是目前能夠造成內存泄露的唯一原因。需要對這樣的情況在設計層面進行避免。互相包含對方類型的屬性的結構中,必須有一方為歸零弱引用。
目前存在雙向包含的場景只有在回調中會用到
- 目標動作回調中,儲存target對象的屬性為weak
- 委托回調中,儲存delegate委托人的屬性為weak
除上述兩種情況外,其他地方默認使用strong修飾屬性即可。
5.AUTO類型與釋放池
在內存管理中有一種較為特殊的類型叫AUTO類型,雖然名字和自動相關,但其釋放仍需要手動配置釋放池來調整。
__autoreleasing:被此種修飾符修飾的對象指針,其使用過和使用中的內存在出釋放池時才會釋放。所以可以通過手動配置自動釋放池的位置來調節釋放時間。
延遲釋放的例子:
@autoreleasepool {{__autoreleasing Student * stu = [[Student alloc] init];}//在此,stu的作用域雖然已經結束,但stu為AUTO類型,所以等代碼運行到釋放池結束才會釋放 } //在此位置,內存釋放提前釋放的例子:
__autoreleasing Student * stu; @autoreleasepool {stu = [[Student alloc] init]; } //在此位置,內存釋放,雖然stu的作用域沒有結束使用AUTO的類型有兩種情況
情況1為對象的便利構造器方法,需要延遲釋放
+(id)student {__autoreleasing Student * stu = [[Student alloc] init]; return stu; }OC語言規定,方法返回的內存必須為AUTO類型。
情況2為在一個封閉循環內,用便利構造器創建對象
for(int i = 0;i<10000;i++) { @autoreleasepool { __autoreleasing Student * stu = [Student student]; } }因便利構造器返回的對象為AUTO類型,所以該對象指針使用的內存只有在出釋放池時才會釋放。但for循環中無釋放池,這會造成,大量無用的對象無法立即釋放。
添加釋放池之后,內存便可以在使用結束之后立即釋放。
?
[代碼展示]
?
1.
======Test類的聲明======
#import <Foundation/Foundation.h>
?
@interface Test : NSObject
?
@end
?======Test類的實現======
?
#import "Test.h"
?
@implementation Test
//對象被釋放之前要調用的方法
-(void)dealloc
{
? ? NSLog(@"Test被釋放。");
}
@end
?
?======main======
#import <Foundation/Foundation.h>
#import "Test.h"
int main(int argc, const char * argv[]) {
? ? //__strong 強引用,它會造成對象的引用計數器的變化(+1)
? ? @autoreleasepool {
?? ? ? ?
?? ? ? __strong Test *t2;
? ? ? ? {
? ? ? ? ? ? Test *t = [[Test alloc]init];
? ? ? ? ? ? t2 = t;
? ? ? ? ? ? t=nil;
? ? ? ? ? ? NSLog(@"2");
? ? ? ? }
? ? ? ? NSLog(@"1");
? ? }
? ? NSLog(@"3");
?? ?
? ?
?
? ? return 0;
}
======運行結果======
2
1
Test被釋放。
3
?
2.
======Test類的聲明======
#import <Foundation/Foundation.h>
?
@interface Test : NSObject
?
@end
======Test類的實現======
#import "Test.h"
?
@implementation Test
-(void)dealloc
{
? ? NSLog(@"Test被釋放");
}
@end
======main======
#import <Foundation/Foundation.h>
#import "Test.h"
int main(int argc, const char * argv[]) {
//? ? @autoreleasepool {
//? ? ? ? //__weak 弱引用,不會造成引用計數器的變化,同時也不能阻止對象的釋放,對象被釋放后自動指向了nil
//? ? ? ? __weak Test *t2;
//? ? ? ? {
//? ? ? ? ? ? __strong Test *t = [[Test alloc]init];
//? ? ? ? ? ? t2 = t;
//? ? ? ? ? ? NSLog(@"%p",t2);
//? ? ? ? ? ? NSLog(@"1");
//? ? ? ? }
//? ? ? ? NSLog(@"%p",t2);
//? ? ? ? NSLog(@"2");
//? ? }
?? ?
? ? @autoreleasepool {
? ? ? ? //__unsafe_unretained 與__weak相同,在對象被釋放后不會指向nil
? ? ? ? __unsafe_unretained Test *t2;
? ? ? ? {
? ? ? ? ? ? __strong Test *t = [[Test alloc]init];
? ? ? ? ? ? t2 = t;
? ? ? ? ? ? NSLog(@"%p",t2);
? ? ? ? ? ? NSLog(@"1");
? ? ? ? }
? ? ? ? NSLog(@"%p",t2);
? ? ? ? NSLog(@"2");
? ? }
? ? return 0;
}
======運行結果======
0x100300220
1
Test被釋放
0x100300220
2
?
轉載于:https://www.cnblogs.com/lqios/p/4288271.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的[objective-c] 08 - 内存管理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: huya虎牙小程序------真心话大冒
- 下一篇: 汉字转换拼音及首字母