美文网首页
iOS内存管理总结

iOS内存管理总结

作者: woniu | 来源:发表于2018-03-21 16:18 被阅读26次

    一、详述OC的内存管理机制。

    OC使用引用计数(retainCount)的机制来管理对象。自己生成的对象,自己持有。非自己生成的对象自己也能处理。不在需要自己持有的对象时,释放掉。非自己持有的对象无法释放。

    a、在MRC中,retain与release配对使用,retain引用计数+1,release引用计数-1。
    b、与alloc配对使用的方法是dealloc,alloc是开辟内存空间,dealloc是销毁开辟的内存空间。
    c、readwrite、readonly读写控制。

    readwrite即声明getter方法又声明setter方法。
    readonly告诉编译器之生命getter方法。
    默认属性为readwrite。

    d、nonatomic,atomic为原子性控制

    nonatomic非原子性控制,此时setter、getter方法不会做多线程处理,提高系统性能。
    atomtic为原子性,此时setter、getter方法会做多线程处理,要不断的对setter、getter方法加锁解锁来保证线程安全,但是也降低了系统的性能。


    属性关键字.png

    二、delegate为什么用assign或者weak?详述!

    a、delegate之所以使用weak来修饰

    为了防止循环引用,weak属性的变量是不为其所属对象所持有,并且在该对象被销毁后,此weak变量的值会自动被赋值为nil。而assign属性一般是对C基本数据类型成员变量的声明,当然也可以用在对象成员变量上,只是其代表的意义只是单纯的拷贝所赋值变量的值。
    场景:对某成员变量B赋值某对象A的指针,则B只是简单地保存此指针的值,并不持有对象A,那么如果A被销毁,B就会指向一个已经被销毁的对象,如果再对其发送消息会引起崩溃。

    b、扩展空指针和野指针定义

    空指针:没有存储任何内存地址的指针就称为空指针。如:NSString *str= nil/NULL;
    野指针:指针指向一个已经被销毁的内存。
    总结:使用野指针是非常危险的,容易引发崩溃。而使用空指针发消息是没有任何问题的。

    c、总结

    assign是指针赋值,不对引用计数操作,使用之后如果没有置为nil,可能就会产生野指针;而weak一旦不进行使用后,永远不会使用了,就不会产生野指针!


    weak&assgin.png

    三、Block的使用

    a:为什么使用weakSelf?

    因为Block是一个结构体,它会将一个全局变量保存为一个属性(__strong),而self强引用了Blcok这会造成循环引用,所以使用__weak修饰weakSelf。

    b:为什么在Block里面使用strongSelf?

    为了保证block在执行完毕之前self不会被释放,而strongSelf是为了保证Block内部执行的时候不会被释放,但是存在执行前就已经额比释放的情况,导致strongSelf=nil。注意判空处理,防止出现崩溃。typeof是编译时确定变量类型,所以这里写self 不会被循环引用。

    c:总结:

    外部使用了weakSelf,里面使用strongSelf却不会造成循环,究其原因就是因为weakSelf是block截获的属性,而strongSelf是一个局部变量会在“函数”执行完释放。

    d:扩展:

    __block可以让block修改局部变量,而__weak不能。
    MRC中__block是不会引起retain;但在ARC中__block则会引起retain,因为,block也是一个强引用,引起循环引用,会引起循环引用。所以ARC中应该使用__weak。

        //1、无传参无返回值
        void (^PrintBlock)(void) = ^(){
            NSLog(@"-------没有传参也没有返回值");
        };
        PrintBlock();
        
        //2、有传参有返回值
        int mutiplier = 7;
        int(^backBlock)(int) = ^(int num){
            
            NSLog(@"有传参有返回值,传参为:%d",num);
            return mutiplier*num;
        };
        NSLog(@"调用传参有返回值的方法,返回值:%d",backBlock(2));
    
        //3、有传参没有返回值
        void(^numberBlcok)(int) = ^(int num){
            NSLog(@"传参为:%d",num);
            
        };
        numberBlcok(555);
        
        //4、修改外部变量
        __block int x = 110;
        void(^sumNumberBlock)(int) = ^(int y){
            x += y;
            NSLog(@"输出值为:%d",x);
        };
        sumNumberBlock(1);
    
    ####1、用SecondViewController的Block传参改变ViewController的值
      SecondViewController *vc = [[SecondViewController alloc] init];
        __weak typeof(self) weakSelf = self;
        vc.textViewBlock = ^(NSString *text){
            __strong typeof(weakSelf) strongSelf = weakSelf;
            
            strongSelf.label.text = text;
        };
    
    ####2、在SecondViewController的.h中声明,在.m文件中实现。
    //声明Block的名字,确定参数类型 我们也可以使用strong来声明,最后内部处理还是为copy类型。
    @property (nonatomic,copy)void (^textViewBlock)(NSString *text);
      self.textViewBlock(@"泰山旅游");
    
    d、再扩展

    那么我们可以思考下,在什么时候在Block中不需要使用weakSelf呢?下面是找的资料给出的解释,我在这里贴出来供大家参考。

    当block本身不被self 持有,而被别的对象持有,同时不产生循环引用的时候,就不需要使用weakself了。最常见的代码就是UIView的动画代码,我们在使用UIView animateWithDuration:animations方法 做动画的时候,并不需要使用weakself,因为引用持有关系是:
    UIView 的某个负责动画的对象持有block,block 持有了self因为 self 并不持有 block,所以就没有循环引用产生,因为就不需要使用 weak self 了。

    [UIView animateWithDuration:0.2 animations:^{
        self.alpha = 1;
    }];
    

    当动画结束时,UIView会结束持有这个 block,如果没有别的对象持有block的话,block 对象就会释放掉,从而 block会释放掉对于 self 的持有。整个内存引用关系被解除。

    拓荒者:
    1.在block里使用strongSelf是防止在block执行过程中self被释放。 2.可以通过在执行完block代码后手动把block置为nil来打破引用循环,AFNetworking就是这样处理的,避免使用者不了解引用循环造成内存泄露。实际业务中暂时没遇到这种需求,请巧哥指点什么情况下会有这种需求。

    陈祥龙:
    strongSelf 一般是在为了避免 block 回调时 weak Self变成了nil ,异步执行一些操作时可能会出现这种情况,不知道我说得对不对。因业务需要不能使用weakSelf 这种情况还真没遇到过

    e、为什么系统的block,AFN网络请求的block内使用self不会造成循环引用?
    • 解释一:
      其实只要抓住循环引用的本质,就不难理解。所谓循环引用,是因为当前控制器在引用着block,而block又引用着self即当前控制器,这样就造成了循环引用。系统的block或者AFN等block的调用并不在当前控制器中调用,那么这个self就不代表当前控制器,那自然也就没有循环引用的问题。以上引用均指强引用。
    • 解释二:
      UIView的动画block不会造成循环引用的原因就是,这是个类方法,当前控制器不可能强引用一个类,所以循环无法形成。而AFN无循环是因为绝大部分情况下,你的网络类对象是不会被当前控制器引用的,这时就不会形成引用环。当然我不知道AFN是否做了别的处理,按照这样来说的话,如果你的控制器强引用了这个网络类的对象,而且在block里面引用了当前控制器,也是会发生循环引用的。
      AFNetworking是因为人家大神自己封装了一个completionBlock,不管你传进来是啥,都会将Block值nill从而给你把循环引用打破。
      资料来源:https://www.zhihu.com/question/36358590

    总结:通用情况 : 在block本身不被self持有,而被别的对象持有,同时不产生循环引用的时候,就不需要使用weakself了。

    下面展示AFNetWorking中的completionBlock打断循环引用。

    1、在AFHTTPRequestOperation.m中的SetCompletionBlock有提示说明:

    // completionBlock is manually nilled out in AFURLConnectionOperation to break the retain cycle.
    翻译大致意思就是,在AFURLConnectionOperation中置nil,打断循环引用。
    
    调用completionBlock.png

    2、在AFURLConnectionOperation.m中,我们看到completionBlock置nil的处理。


    setCompletionBlock置nil.png

    相关文章

      网友评论

          本文标题:iOS内存管理总结

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