美文网首页
iOS内存管理-整理中

iOS内存管理-整理中

作者: okerivy | 来源:发表于2016-11-30 20:14 被阅读25次

    一, iOS的内存管理规则

    1 基本原则

    移动设备的内存极其有限,每个app所能占用的内存是有限制的

    当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不需要再使用的内存空间。比如回收一些不需要使用的对象、变量等

    管理范围:任何继承了NSObject的对象,对其他基本数据类型(int、char、float、double、struct、enum等)无效

    QQ堂开房间原理:只要房间还有人在用,就不会解散

    只要还有人在用某个对象,那么这个对象就不会被回收
    只要你想用这个对象,就让对象的计数器+1
    当你不再使用这个对象时,就让对象的计数器-1

    • 1, 你想使用(占用)某个对象,就应该让对象的计数器+1(让对象做一次retain操作)
    • 2, 你不想再使用(占用)某个对象,就应该让对象的计数器-1(让对象做一次release)
    • 3, 谁alloc,谁release

    如果你通过alloc、new或[mutable]copy来创建一个对象,那么你必须调用release或autorelease

    换句话说,不是你创建的,就不用你去[auto]release

    • 4, 谁retain,谁release

    只要你调用了retain,无论这个对象是如何生成的,你都要调用release

    • 5, 总结

    有始有终,有加就有减
    曾经让对象的计数器+1,就必须在最后让对象计数器-1

    所以,一般需要在三个地方进行release

    • 在main或其他函数创建对象后.
    • 在dealloc方法内
    • 在set方法内,进行if判断

    2 ARC

    ARC是编译器(时)特性,而不是运行时特性,更不是垃圾回收器

    2.1、 自动释放池

    autorelease 其实并不是自动释放,而是把对release的调用延迟了

    autorelease 返回的是对象本身.
    // autorelease方法会返回对象本身        
    // 调用完autorelease方法后,对象的计数器不变        
    // autorelease会将对象放到一个自动释放池中        
    // 当自动释放池被销毁时,会对池子里面的所有对象做一次release操作
    
    1、 autorelease的基本用法
    • 给某个对象发送一条autorelease消息时,就会将这个对象加到一个* 自动释放池中
    • 当自动释放池销毁时,会给池子里面的所有对象发送一条release消息
    • 调用autorelease方法时并不会改变对象的计数器,
    • 调用autorelease方法并且会 会返回对象本身autorelease实际上只是把对release的调用延迟了,对于每一次autorelease,系统只是把该对象放入了当前的autorelease pool中,当该pool被释放时,该pool中的所有对象会被调用Release
    2、autorelease的好处

    1> 不用再关心对象释放的时间
    2> 不用再关心什么时候调用release

    3、autorelease的使用注意

    1> 占用内存较大的对象不要随便使用 autorelease
    2> 占用内存较小的对象使用autorelease,没有太大影响

    4、autorelease 应用的场景
    1,开发中经常会提供一些类方法,快速创建一个已经autorelease过的对象

    一般可以为类添加一个快速创建对象的类方法
    创建一个类方法,直接返回一个扔进释放池里面的对象

    声明:
    + (id)person; 一般是和类同名的小写.

    实现:

    + (id)person{
         创建对象时不要直接用类名,一般用self 
         //因为这个self代表类 解决了子类调用父类的问题,满足子类需求 ,避免了重写
        return [[[self alloc] init] autorelease];
    }
    

    调用: 创建对象

    外界调用[Person person]时,根本不用考虑在什么时候释放返回的Person对象

    Person *p2 = [Person person];
    以前的是:  Person *p2 = [[[Person alloc] init] autorelease];
    

    也可以扩充方法

    + (id)personWithAge:(int)age{    
        Person *p = [self person];    
        p.age = age;    
        return p;
    }
    
    2、规律

    系统自带的方法里面没有包含alloc、new、copy,说明返回的对象都是autorelease的
    一般来说,除了alloc、new或copy之外的方法创建的对象都被声明了autorelease

    比如下面的对象都已经是autorelease的,不需要再release

    NSNumber *n = [NSNumber numberWithInt:100];        
    NSString *s = [NSString stringWithFormat:@"jack"];        
    NSString *s2 = @"rose”;
    

    2.2. ARC的修饰符

    ARC提供四种修饰符,分别是strong, weak, autoreleasing, unsafe_unretained
    __strong:强引用,持有所指向对象的所有权,无修饰符情况下的默认值。如需强制释放,可置nil。

    比如我们常用的定时器
    NSTimer * timer = [NSTimer timerWith...];
    
    相当于
    NSTimer * __strong timer = [NSTimer timerWith...];
    
    当不需要使用时,强制销毁定时器
    [timer invalidate];  
    timer = nil;
    

    __weak:弱引用,不持有所指向对象的所有权,引用指向的对象内存被回收之后,引用本身会置nil,避免野指针。

    比如避免循环引用的弱引用声明:
    __weak __typeof(self) weakSelf = self;
    

    __autoreleasing:自动释放对象的引用,一般用于传递参数

    比如一个读取数据的方法
    - (void)loadData:(NSError **)error;
    
    当你调用时会发现这样的提示
    NSError * error; [dataTool loadData:(NSError *__autoreleasing *)]
    
    这是编译器自动帮我们插入以下代码
    NSError * error; 
    NSError * __autoreleasing tmpErr = error;
    [dataTool loadData:&tmpErr];
    

    __unsafe_unretained:为兼容iOS5以下版本的产物,可以理解成MRC下的weak,现在基本用不到,这里不作描述。

    2.3. ARC的开启与关闭

    设置方法:
    Targets -->Build Phases -->Compile Sources
    如果你的工程是开启ARC的, 那就需要对某些文件禁用ARC, (-fno-objc-arc)
    如果你的工程是关闭ARC的, 那就需要对某些文件开启ARC.(-fobjc-arc)

    3. @property属性的内存管理

    1、控制 set方法内存管理相关的参数

    • retain : release旧值,retain新值(适用于OC对象类型)
    • assign : 直接赋值(默认,适用于非OC对象类型)
    • copy : release旧值,copy新值(一般用于 NSString * )

    2、控制 是否要生成set方法

    • readwrite : 同时生成setter和getter的声明、实现(默认)
    • readonly : 只会生成getter的声明、实现

    3、控制 多线程管理

    • nonatomic : 性能高 (一般就用这个) 不加锁
    • atomic : 性能低(默认) 加锁

    4、控制 setter和getter方法的名称

    • setter : 决定了set方法的名称,一定要有个冒号 :
    • getter : 决定了get方法的名称(一般用在BOOL类型)
    // 返回BOOL类型的方法名一般以is开头  
    @property (getter = isRich, setter = setIsRich:) BOOL rich;   
    @property (nonatomic, assign, readwrite) int weight;
    

    4. block的内存管理

    Block的类型与内存管理

    根据Block在内存中的位置分为三种类型NSGlobalBlock,NSStackBlock, NSMallocBlock。

    NSGlobalBlock:类似函数,位于text段;
    NSStackBlock:位于栈内存,函数返回后Block将无效;
    NSMallocBlock:位于堆内存。

    • 无论当前环境是ARC还是MRC,只要block没有访问外部变量,block始终在全局区
    • MRC情况下
      1.block如果访问外部变量,block在栈里
      2.不能对block使用retain,否则不能保存在堆里
      3.只有使用copy,才能放到堆里
    • ARC情况下
      1.block如果访问外部变量,block在堆里
      2.block可以使用copy和strong,并且block是一个对象

    block的循环引用
    如果要在block中直接使用外部强指针会发生错误,使用以下代码在block外部实现可以解决

    __weak typeof(self) weakSelf = self;
    

    但是如果在block内部使用延时操作还使用弱指针的话会取不到该弱指针,需要在block内部再将弱指针强引用一下
    __strong typeof(self) strongSelf = weakSelf;
    iOS中使用block必须自己管理内存,错误的内存管理将导致循环引用等内存泄漏问题,这里主要说明在ARC下block声明和使用的时候需要注意的两点:  1)如果你使用@property去声明一个block的时候,一般使用copy来进行修饰(当然也可以不写,编译器自动进行copy操作),尽量不要使用retain。
    @property (nonatomic, copy) void(^block)(NSData * data);

    2)block会对内部使用的对象进行强引用,因此在使用的时候应该确定不会引起循环引用,当然保险的做法就是添加弱引用标记。
    __weak typeof(self) weakSelf = self;

    有兴趣的读者可以深入了解:
      1、block的内部实现原理是什么?  2、从内存位置来看block有几种类型?它们的内存管理方式各是怎样的?  3、对于不同类型的外部变量,block的内存管理都是怎样的?

    5. 内存管理代码规范

    • 1, 函数或方法 里面只要调用了alloc,必须有release(autorelease)
      对象不是通过alloc产生的,就不需要release
    NSString *s = @"Jack";      
    stu.name = s;
    
    • 2, set方法的代码规范

    1> 基本数据类型:直接复制

     - (void)setAge:(int)age {    
        _age = age; 
    }  
    

    2> OC对象类型

     - (void)setCar:(Car *)car {    
        // 1.先判断是不是新传进来对象    
        if ( car != _car )    {        
            // 2.对旧对象做一次release        
            [_car release];         
            // 3.对新对象做一次retain        
            _car = [carretain];    
        } 
    } 
    
    • 3, dealloc方法的代码规范 不要直接调用dealloc
      1> 一定要[super dealloc],而且放到最后面
      2> 对self(当前)所拥有的其他对象做一次release
     - (void)dealloc      {
        [_car release];         
        [super dealloc];      
    }
    

    </br>


    二, 经典内存泄漏

    1 僵尸对象和野指针

    </br>

    2 循环引用

    </br>

    3 循环中对象占用内存大

    </br>

    4 无限循环

    </br>


    3, 项目中的注意点

    • 1, 代理 nil
    • 2, 通知 移除
    • 3, kvo 移除
    • 4, block 循环引用 __weak mrc __block
    • 5, 对象的循环引用 一端 assign 一端 strong

    参考链接
    http://www.jianshu.com/p/8b1ed04b3ba9

    相关文章

      网友评论

          本文标题:iOS内存管理-整理中

          本文链接:https://www.haomeiwen.com/subject/ykoytttx.html