iOS内存管理总结

一、详述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方法加锁解锁来保证线程安全,但是也降低了系统的性能。


iOS内存管理总结_第1张图片
属性关键字.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一旦不进行使用后,永远不会使用了,就不会产生野指针!


iOS内存管理总结_第2张图片
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,打断循环引用。
iOS内存管理总结_第3张图片
调用completionBlock.png

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


iOS内存管理总结_第4张图片
setCompletionBlock置nil.png

你可能感兴趣的:(iOS内存管理总结)