內存管理高級

1.內存管理的黃金法則:The basic rule to apple is everything thatincreases the reference counter with alloc,[mutable]copy[WithZone:] or retain is in charge of the corresponding [auto]release. 即:如果一個對象使用了alloc,[mutable] copy,retain,那麼你必須使用相應的release或autonrelease.

2.規則:

1、Objective-C類中實現了引用計數器,對象知道自己當前被引用的次數

2、最初對象的計數器爲1

3、如果需要引用對象,可以給對象發送一個retain消息,這樣對象的計數器就加1

4、當不需要引用對象了,可以給對象發送release消息,這樣對象計數器就減1

5、當計數器減到0,自動調用對象的dealloc函數,對象就會釋放內存

6、計數器爲0的對象不能再使用release和其他方法


3.內存問題主要有:內存溢出 內存過渡釋放 野指針 三種
內存溢出(內存堆積):大量的開闢空間但並不立即釋放,通常是由於使用autorelease產生的.
內存過渡釋放:對象將內存已經還給系統,之後再次歸還給系統這時就會出現內存過渡釋放的問題.
野指針:對象指向的內存空間已經歸還給系統但此時再次調用了指向該指針空間的指針,就會產生野指針的問題.

4.內存管理的形式:MRC (manual reference count) 人工引用計數機制 內存的開闢和釋放都有手動代碼進行控制.ARC(auto reference count )自動引用計數機制 不需要人工控制系統自動進行管理.

5.利用遛狗來理解內存問題 請參考博文中的遛狗問題分析 特別注意內存處理的setter和getter方法
+alloc  加號方法 買狗 開闢空間 引用計數+1 對應於release 
-retain 不買狗只在狗的身上套繩子 前提是要有對象 引用計數+1 對應於release
-copy  按照原來狗的標準進行再買個狗 有了新的對象 開闢了新的空間相當於一個新的alloc 引用計數+1
-release 在對象原來基礎上 引用計數立即-1 對應於retain
-autorelease 將對象放到自動釋放池裏 當自動釋放池結束時會執行release操作 引用計數-1 

6.內存中的相關問題
1.只有在堆區纔會有內存管理的問題,棧區 常量區 全局區等都不涉及內存管理的問題都是有系統自己來管理.
如下例子:
 NSString *per/*(棧區) */= [[NSString alloc/*(堆區)*/] init];//0~1
        [per 
retain];//計數1~2
        [per 
retain];//計數2~3 內存沒釋放 內存泄露 此時棧區的對象沒有釋放 
        per = 
@"aa";//常量區將地址給per 地址發生變化 計數爲無窮大
        [per 
release];//常量區內存系統自動釋放 不需要人工釋放
        [per 
release];
    }
2.自動釋放池.autorelease將對象放到離它最近的釋放池中,當釋放池銷燬時,會想池中的每一個對象發送一個release消息,每一個對象計數減一.
如下例子: 特別注意輸出是順序 即:dealloc在什麼時候執行的
重寫的dealloc
- (void)dealloc{
    NSLog(@"哈哈");
    [super dealloc];
}
自動釋放池autorelease操作釋放的時機
 @autoreleasepool {
         
NSLog(@"%lu",[per retainCount]);//1
        
    [per release];//1~0立即減一
        
//autorelease作用:將對象放入離它最近的釋放池中.
        
//當自動釋放池銷燬時,會向池中的每一個對象發送release消息.
         [per autorelease];//1~0
        
/**3.
         * 1
         test
         
哈哈(重寫了dealloc方法)
         */

        
@autoreleasepool {
            [per 
autorelease]; //1~0放到小池子裏
            [per autorelease]; //過渡釋放

            
/**2.
             *  1
                哈哈(重寫了dealloc方法)              
                test
             */

        }
        
NSLog(@"test");
        [per 
autorelease]; //過渡釋放
        
/**3.
         * 1
         test
         
 哈哈(重寫了dealloc方法)
         */

    }

例2:有沒有內存問題,如果有請修改? 
@autoreleasepool {
        for (int i = 0; i < 10000000; i++) {          
                
Person *per = [[Person allocinit];
                [per 
autorelease];
        }
}
有:內存堆積 只開闢空間 不釋放 要理解autorelease的處理機制
1.處理方式一 autorelease是在銷燬釋放池是將引用計數減一
@autoreleasepool {
        for (int i = 0; i < 10000000; i++) {
            
@autoreleasepool {
                
Person *per = [[Person allocinit];
                [per 
autorelease];
            }
        }
}
2.處理方式二 release是立即將引用計數減1
@autoreleasepool {
        for (int i = 0; i < 10000000; i++) {            
                
Person *per = [[Person allocinit];
                [per 
release];
        }
}


7.對於野指針問題最好的解決方案是:將對象賦值=nil .

8.重寫dealloc方法
當該類型的對象引用計數爲零時 系統會自動調用dealloc方法來回收空間 系統自動調用 不需要手動回收.
驗證對象空間有沒有回收,只要查看該類的dealloc方法有沒有執行即可,回收執行,不回收就不調用.
- (
void)dealloc{
    
NSLog(@"哈哈");
    [
super dealloc];
}

9.內存管理高級
1.屬性的內部實現原理和管理內存機制.
1.當實例變量屬性聲明爲retain時,它的setter和getter方法及getter方法的內部實現是
 @property (nonatomic,retainTeacher1 *teach;
//setter以及getter方法內部是對實例變量進行賦值和取值操作,所以方法內部要操作實例變量
//if判斷是判斷原有對象是同一個,如果是同一個人就沒與必要重新賦值,否則就顯release,release之後空間系統被回收 此時如果retain就成爲野指針問題.
- (void)setTeach:(Teacher *)teach{
    
if (_teach != teach) {//如果是不同的空間
        
//setter是給實例變量賦值來用
        [_teach release];
//先將原來的保有的所有權釋放計數減解決內存泄露問題
        
//讓實例變量保有新的所有權 解決野指針問題
        _teach = [teach retain];
        
//對於將來要做的一些對象,可以持有一份 將引用計數加解決野指針問題
        
//    self.teach = teach;死循環了 不能調用自己
    }
}

-(Teacher1 *)teach{
    
return _teach;
}
對應的重寫dealloc方法
- (void)dealloc{
    
//當該類對象的引用計數爲零會自動調用該類的dealloc方法
    
//當調用dealloc方法時本類對象的空間將會被回收 在空間回收之前,將保有的其他對象的所有權給釋放掉.
    [_teach release];
//釋放retain的所有權 引用計數-1操作
    [_teach1 release];
//只要有retain就要釋放
    NSLog(
@"hahahha");
    [
super dealloc];//繼承父類的dealloc方法.
}

2.當實例變量屬性聲明爲copy時,它的setter和getter方法及getter方法的內部實現是
@property (nonatomic,copyTeacher1 *teach1;
//copy的內部實現形式
-(
void)setTeach1:(Teacher1 *)teach1{
    
if (_teach1 != teach1) {
        [
_teach1 release];
//如果相對一個對象copy操作,對象的類必須符合一個NSCopying協議,並且實現協議中的方法
//解決方案:實現協議操作
        
_teach1 = [teach1 copy];
    }
}
-(
Teacher1 *)teach{
    
return _teach;
}
如果要聲明屬性爲copy時就要使用copy所要滿足的協議
@interface Teacher1 : NSObject<NSCopying>{
    
//姓名
    
NSString *_name;
    
//性別
    
NSString *_gender;
}
@property (nonatomic,retainNSString *name;
@property (nonatomic,retainNSString *gender;
- (
id)initWithName:(NSString *)name gender:(NSString *)gender;
+ (
id)teacherWithName:(NSString *)name gender:(NSString *)gender;
- (
void)dealloc;
@end
@implementation Teacher1
//實現協議
- (
id)copyWithZone:(NSZone *)zone{
    
Teacher1 *newTeacher = [[Teacher1 allocWithZone:zone]init];//開闢空間 alloc操作引用計數加對象發生變化
    newTeacher.
name = self.name;//賦值操作
    newTeacher.
gender = self.gender;
    
return newTeacher;//返回對象
}
- (
id)initWithName:(NSString *)name gender:(NSString *)gender{
    
self = [super init];
    
if (self) {
        
//    _name = name;可能會出現野指針問題 相當於賦值操作 引用計數不會加1
        
self.name = name;//內部調用setter方法添加了retain操作 保有了對象的所有權 引用計數+1
        
self.gender = gender;
    }
    
return self;
}
+ (
id)teacherWithName:(NSString *)name gender:(NSString *)gender{
    
return [[[Teacher1 alloc]initWithName:name gender:gender ] autorelease];
}
- (
void)dealloc{
    
NSLog(@"%@空間被回收了",self.name);
    [
_gender release];
    [
_name release];
    [
super dealloc];
}
@end

2.便利構造器方法的內部實現及內存管理 內部採用的是autorelease所以會造成內存堆積
        Teacher1 *tea = [Teacher1 teacherWithName:
@"Frank" gender:@"man"];0 ~ 1
        NSLog(
@"%lu",[tea retainCount]);
        
下面的是內存堆積的問題 因爲便利構造器內部用的是autorelease
        
for (int i = 0; i < 100000000; i++) {
            NSString *str = [NSString stringWithFormat:
@"aaaaa"];
        }
便利構造器的內部實現
內部對應的有alloc 就對應的有autorelease 所以當銷燬自動釋放池時就將引用計數減一 對象所佔的內存回收
+ (id)teacherWithName:(NSString *)name gender:(NSString *)gender{    
    
return [[[Teacher1 alloc]initWithName:name gender:gender ] autorelease];
}

3.collection的內存管理 自主管理內存 
1.當一個對象加入集合(數組 字典 集合)時,對象的引用計數會retain操作
2.當一個對象移除集合(數組 字典 集合)時,對象的引用計數會release操作
3.當集合collection空間回收時,他們會向空間中每一個對象發送一個release消息(對應添加元素時的retain操作)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章