底层学习
MRC
MRC :手动引用计数管理,通过调用 retain、release 和 autorelease 等方法来控制对象的生命周期。
| 对象操作 | OC方法 | 
|---|---|
| 生成并且持有对象 | alloc/new/copy/mutableCopy等方法 | 
| 持有对象 | retain方法 | 
| 释放对象 | release | 
| 废弃对象 | dealloc | 
对于内存管理考虑方式有四点:
单个对象:
- 
自己生成的对象,自己持有
 - 
非自己生成的对象,自己也可以持有
objectivecNSMutableArray* ary = [NSMutableArray array]; [ary reatin]; - 
不再需要自己持有的对象时释放
 - 
非自己持有的对象无法释放
 
多个对象内存管理思想
- 一个对象不持有另外一个对象
 
此时两个实例对象没有关联可以将各自释放掉
- 一个对象持有另外一个对象
 
持有的对象需要在set方法中引用计数加一。释放时也需要将持有的对象销毁
            
            
              objectivec
              
              
            
          
          - (void)setMyObject:(MyObject*)myObj { 
    // 引用计数器 +1
    [myObj retain];
    _myObj = myObj;
}
- (void)dealloc {
    // 人释放了, 那么房间也需要释放
    [_myObj release];
    NSLog(@"%s", __func__);
 
    [super dealloc];
}
        - 一个对象持有并释放掉一个对象后持有另外一个对象
 
此时则需要在更换原持有的对象前,释放第一个对象
            
            
              objectivec
              
              
            
          
          - (void)setMyObject:(MyObject*)myObj { 
		[_myObj release];
    // 引用计数器 +1
    [myObj retain];
    _myObj = myObj;
}
        - 一个对象持有一个对象并释放该对象后再次持有该对象
 
此种情况使用上一个getter方法不难发现,我们将那个对象赋值给该对象后该对象对那个对象的引用计数加一,再将那个对象引用计数减一,此时两个对象的引用计数相同,此时再次进行赋值会导致那个对象的引用计数先减一此时为0变成了野指针,此时再对野指针进行retain操作就会报错。
此时就需要在setter方法中判断是否重复赋值,如果是同一个实例对象,就不需要重复进行 release 和 retain。
            
            
              objectivec
              
              
            
          
          - (void)setMyObject:(MyObject*)myObj { 
   if (_myObj != myObj) {
   [_myObj release];
    // 引用计数器 +1
    [myObj retain];
    _myObj = myObj;
   }
}
        @property参数
- 在成员变量前加上 
@property,系统就会自动帮我们生成基本的setter/getter方法,但是不会生成内存管理相关的代码。 
            
            
              objectivec
              
              
            
          
          @property (nonatomic) int val;
        - 如果在 
property后边加上assign,系统也不会帮我们生成setter方法内存管理的代码,仅仅只会生成普通的getter/setter方法,默认什么都不写就是assign。 
            
            
              objectivec
              
              
            
          
          @property(nonatomic, assign) int val;
        - 如果在 
property后边加上retain,系统就会自动帮我们生成getter/setter方法内存管理的代码,但是仍需要我们自己重写dealloc方法。 
            
            
              objectivec
              
              
            
          
          @property(nonatomic, retain)MyObject *myObj;
        自动释放池
autorelease 是一种支持引用计数的内存管理方式,主要用于延迟对象的释放,只要给对象发送一条 autorelease 消息,会将对象注册到一个自动释放池中,当自动释放池被销毁时,会对池子里面的所有对象做一次 release 操作。
autorelease方法会返回对象本身,且调用完autorelease方法后,对象的计数器不变。
            
            
              objectivec
              
              
            
          
          Person *p = [Person new];
p = [p autorelease];
NSLog(@"count = %lu", [p retainCount]); // 计数还为 1
        创建
- 第一种是使用 NSAutoreleasePool 创建
 
            
            
              objectivec
              
              
            
          
          NSAutoreleasePoo *pool = [[NSAutoreleasePool alloc] init]; //创建自动释放池
[pool release];  [pool drain]; //销毁自动释放池
        - 第二种是使用 @autoreleasepool 创建
 
            
            
              objectivec
              
              
            
          
          @autoreleasepool
{ // 开始代表创建自动释放池
 
} // 结束代表销毁自动释放池
        使用
需要注意的是对象调用autorelease方法需要在创建autoreleasepool之后否则无法将其注册到autoreleasepool。
            
            
              objectivec
              
              
            
          
          NSAutoreleasePool *autoreleasePool = [[NSAutoreleasePool alloc] init];
Person *p = [[[Person alloc] init] autorelease];
[autoreleasePool drain];
@autoreleasepool
{ // 创建一个自动释放池
        Person *p = [[Person new] autorelease];
        // 将代码写到这里就放入了自动释放池
} // 销毁自动释放池(会给池子中所有对象发送一条 release 消息)
//以上两种使用方法相同
        自动释放池的嵌套
- 自动释放池是以栈的形式存在。
 - 由于栈只有一个入口,所以调用 
autorelease会将对象放到栈顶的自动释放池。 
            
            
              objectivec
              
              
            
          
          @autoreleasepool { // 栈底自动释放池
    @autoreleasepool {
        @autoreleasepool { // 栈顶自动释放池
            Person *p = [[[Person alloc] init] autorelease];
        }
        Person *p = [[[Person alloc] init] autorelease];
    }
}
        注意事项
- 自动释放池中不适宜放占用内存比较大的对象
 
因为自动释放池是延迟释放机制,只有清空池子时才会将注册到池子的对象销毁,此时如果有多个内存较大的对象就会造成短时间内内存暴涨。
- 
不要把大量循环操作放到同一个
@autoreleasepool之间,这样会造成内存峰值的上升。如果一定要循环,则这样使用objectivec// 内存暴涨 @autoreleasepool { for (int i = 0; i < 99999; ++i) { Person *p = [[[Person alloc] init] autorelease]; } } // 内存不会暴涨,在循环内部创建自动释放池 for (int i = 0; i < 99999; ++i) { @autoreleasepool { Person *p = [[[Person alloc] init] autorelease]; } } - 
不要连续调用
autorelease,也不要调用后又调用release 
            
            
              objectivec
              
              
            
          
          @autoreleassepool {
 // 会导致过度释放
    Person *p = [[[[Person alloc] init] autorelease] autorelease];
}
@autoreleasepool {
    Person *p = [[[Person alloc] init] autorelease];
    [p release]; //过度释放
}
        循环引用
两个对象互相持有对方,A对象的销毁依赖于B,B对象的销毁依赖于A
1.自循环引用
假如有一个对象,内部强持有它的成员变量obj,若此时我们给obj赋值为原对象时,就是自循环引用。
2.相互循环引用
对象A内部强持有obj,对象B内部强持有obj,若此时对象A的obj指向对象B,同时对象B中的obj指向对象A,就是相互引用。
3.多循环引用
假如类中有对象1...对象N,每个对象中都强持有一个obj,若每个对象的obj都指向下个对象,就产生了多循环引用。