iOS循环引用问题集合、内存泄漏、僵尸对象、代码静态分析

 

内存泄漏:https://my.oschina.net/llfk/blog/1031291

内存泄漏监测自动化:http://www.cocoachina.com/articles/18490

facebook三件套内存检测:https://www.jianshu.com/p/bf43f2a7290chttps://www.jianshu.com/p/4730d90e6008https://www.jianshu.com/p/bf43f2a7290c

******iOS几种常见内存泄漏:https://blog.csdn.net/heqiang2015/article/details/84575047

  **【view  removeFromSuperview】后,view的内存并不一定释放,最好在合适的时候设置view=nil;

  **UITextfield获得焦点后会内存高涨,因为弹出了键盘,不用的时候要取消焦点。

 

AFN内存泄漏问题:https://juejin.im/post/5a20ec24f265da432240feb8

// AFURLSessionManager.m
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    // ... 初始化代码,省略

    // 导致循环引用的方法
    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];

    // ... 其它初始化代码,省略
    return self;
}
大致原因就是AFURLSessionManager引用NSURLSession,同时设置NSURLSession的delegate为自己,NSURLSession会强引用delegate,于是产生了循环引用。
Leaked memory 和 Abandoned memory 都属于应该释放而没释放的内存,
都是内存泄露,而 Leaks 工具只负责检测 Leaked memory,
而不管 Abandoned memory。
对于 Abandoned memory,可以用 Instrument 的 Allocations 检测出来。

__weak和__strog配合使用原因:https://www.jianshu.com/p/ae4f84e289b9

__weak对象释放后自动变为nil解释:https://blog.csdn.net/weixin_30352645/article/details/98951975

引用计数原理:https://www.cnblogs.com/azuo/p/6066868.html

打印引用计数:

方法一:使用KVC
[obj valueForKey:@"retainCount"]
方法二:使用CFGetRetainCount
CFGetRetainCount((__bridge CFTypeRef)(obj))
方法三:使用私有API
OBJC_EXTERN int _objc_rootRetainCount(id);
_objc_rootRetainCount(obj)

引用计数会增加的情况:alloc,new,strong,copy,set方法或者点语法,block内的对象,storyboard拖拽出来的控件,addsubview添加子控件。当对象的引用计数减为0时候,会出发dealloc的调用;当前对象销毁,属性所持有的对象也跟着销毁。

 

******引用计数举例:

#define WS(weakSelf) __weak __typeof(&*self)weakSelf = self;

1.****
@property(nonatomic,strong)TestTwovc *twovc;
 self.twovc=[TestTwovc new];
   NSLog(@"jishu---%ld",CFGetRetainCount((__bridge CFTypeRef)(self.twovc)));//self.twovc引用计数2.


 

2*****
 TestTwovc*twovc=[TestTwovc new];
    self.twovc=twovc;
   NSLog(@"jishu---%ld",CFGetRetainCount((__bridge CFTypeRef)(twovc)));//twovc的引用计数是2
@property(nonatomic,weak)TestTwovc *twovc;
 TestTwovc*twovc=[TestTwovc new];
    self.twovc=twovc;
   NSLog(@"jishu---%ld",CFGetRetainCount((__bridge CFTypeRef)(twovc)));//twovc的引用计数是1

 

 3.*****
TestTwovc*twovc=[TestTwovc new];
    self.twovc=twovc;
   NSLog(@"jishu---%ld",CFGetRetainCount((__bridge CFTypeRef)(self.twovc)));//self.twovc的引用计数是3
4.****
@property(nonatomic,strong)Testoneview *oneview;
  Testoneview *oneview=[[Testoneview alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];//计数+1
    oneview.backgroundColor=[UIColor redColor];
    self.oneview =oneview;//计数+1
    [self.view addSubview:oneview];//计数+1
     NSLog(@"oneview---%ld",CFGetRetainCount((__bridge CFTypeRef)(oneview)));//one view的引用计数+3

 

 6。*****
testob *testobs=[testob sharedCommonManager];//testob是单利
    NSLog(@"单利---%ld",CFGetRetainCount((__bridge CFTypeRef)(testobs)));//testobs的引用计数是2,在单利内部有一次实例话的过程。
7.*****
 NSLog(@"self---%ld",CFGetRetainCount((__bridge CFTypeRef)(self)));
//在控制器vc中什么才做不做,self本身就会有6个引用计数。

 ****UIVIew animation动画、AFN、dispatch_async(dispatch_after中使用self会循环引用,必须用weakSelf或者StrongSelf)中等block中使用self不会造成循环引用(block中没有其他的block嵌套)。

***** QMWNSTRONG在block不会造成循环引用
@property(nonatomic,copy)void (^blc)(void);

QMWNWEAKSELF;
  
    self.blc = ^{
         QMWNSTRONG;在里面
        Testoneview *oneview=[[Testoneview alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];//计数+1
        oneview.backgroundColor=[UIColor redColor];
        strongS.oneview =oneview;//计数+1
        [strongS.view addSubview:strongS.oneview];//计数+1
        NSLog(@"oneview---%ld",CFGetRetainCount((__bridge CFTypeRef)(oneview)));
        
    };


*********** QMWNSTRONG在block外面会造成循环引用,strongSelf是防止block中weakself提前释放造成崩溃,一般简单的操作用weakSelf也可以。
@property(nonatomic,copy)void (^blc)(void);

QMWNWEAKSELF;
  QMWNSTRONG;在外面
    self.blc = ^{
         
        Testoneview *oneview=[[Testoneview alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];//计数+1
        oneview.backgroundColor=[UIColor redColor];
        strongS.oneview =oneview;//计数+1
        [strongS.view addSubview:strongS.oneview];//计数+1
        NSLog(@"oneview---%ld",CFGetRetainCount((__bridge CFTypeRef)(oneview)));
        
    };


******直接用weakSelf不会造成循环引用
 QMWNWEAKSELF;
  
    self.blc = ^{
        Testoneview *oneview=[[Testoneview alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];//计数+1
        oneview.backgroundColor=[UIColor redColor];
        weakSelf.oneview =oneview;//计数+1
        [weakSelf.view addSubview:weakSelf.oneview];//计数+1
        NSLog(@"oneview---%ld",CFGetRetainCount((__bridge CFTypeRef)(oneview)));
        
    };

********方法体中用self不会造成循环引用
 QMWNWEAKSELF;
    self.blc = ^{
        QMWNSTRONG;
           [strongS testfun];//在这里使用了weakSelf或者strongSelf方法体中就不需要在使用weakSelf或者strongSelf了。
        
    };

}
-(void)testfun{
    Testoneview *oneview=[[Testoneview alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];//计数+1
    oneview.backgroundColor=[UIColor redColor];
    self.oneview =oneview;//计数+1
    [self.view addSubview:self.oneview];//计数+1
    NSLog(@"oneview---%ld",CFGetRetainCount((__bridge CFTypeRef)(oneview)));
   
}



**********UIView的block 方法不会造成循环引用
 [UIView animateWithDuration:0.2 animations:^{
             [self testfun];//用self 不会造成循环引用
    
        } completion:^(BOOL finished) {
            
        }];

-(void)testfun{
    Testoneview *oneview=[[Testoneview alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];//计数+1
    oneview.backgroundColor=[UIColor redColor];
    self.oneview =oneview;//计数+1
    [self.view addSubview:self.oneview];//计数+1
    NSLog(@"oneview---%ld",CFGetRetainCount((__bridge CFTypeRef)(oneview)));
   
}


*****UIVIew的block外还有一层不block
 
    QMWNWEAKSELF;
    self.blc = ^{
        QMWNSTRONG;

        [UIView animateWithDuration:0.2 animations:^{
//             [self testfun];//这里如果self会循环引用,如果UIView  animate方法外层没有block,用self 不会造成循环引用
            [strongS testfun];//不会造成循环引用
        } completion:^(BOOL finished) {
            
        }];
    };

}
-(void)testfun{
    Testoneview *oneview=[[Testoneview alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];//计数+1
    oneview.backgroundColor=[UIColor redColor];
    self.oneview =oneview;//计数+1
    [self.view addSubview:self.oneview];//计数+1
    NSLog(@"oneview---%ld",CFGetRetainCount((__bridge CFTypeRef)(oneview)));
   
}

 ARC内存管理(mj):https://www.cnblogs.com/huanying2000/p/6165440.html

weak避免强引用objc对象:https://www.jianshu.com/p/14f494b1f566

内存管理:https://www.jianshu.com/p/ac86dc80cca5

ARC内存管理/strong weak copy assign 的用法与区别:https://blog.csdn.net/u011146511/article/details/51344070


a). 将控件声明成strong
@property(nonatomic,strong) UIButton *btn;
那么你在实现这个控件时只需这样:
_btn = [[UIButton alloc]init];
[self.view addSubview:_btn]

b). 将控件声明成weak
@property(nonatomic,weak) UIButton *btn;
那么你在实现这个控件时需要这样:
UIButton *button = [[UIButton alloc]init];
_btn = button;
[self.view addSubview:_btn];


assign:适用于基本数据类型,其set方法只会进行简单的赋值操作NSInteger,CGFloat...
/** 基本数据类型*/
@property (assign, nonatomic) NSInteger age;

strong:强引用类型,被定义了该类型的属性会被其所指对象持有,为这种属性设置新值的时候,set方法先保留新值,并释放掉旧值,然后再把新值赋值上去
/** 适用于对象类型,并且被持有*/
@property (strong, nonatomic) UIView *view;

weak:爱恨交织的weak!弱引用类型,被定义了该类型的属性不会被持有,在set的时候,
既不保留新值也不会释放旧值,和assign类似,不同的是在该属性所指对象被销毁之后,
属性值也会被置为nil
/** 弱引用类型,对象释放后置为nil*/
@property (weak, nonatomic) id<SSDelegate> delegate;
unsafe_unretained:这个跟assign就更像了,但不同的是它适用于对象类型.同样的不会被持有,但与weak不同是,所指对象被销毁之后不会把属性置为nil

/** 弱引用类型,对象释放后不会置为nil*/
@property (unsafe_unretained, nonatomic) UIView *view;
copy:它与所指对象是持有关系,与strong类似,然而set方法并不会保留新值,而是将其拷贝一份.
所以,当你声明一个非可变集合类型(collection type)的时候应该使用这个修饰符,不单单是NSString,
同样的NSArray,NSDictionary,NSSet也应当使用它来修饰,
copy可以保护非可变集合类型(collection type)的封装性,
因为在其传递过程中有可能会指向一个可变类型(mutable type),
此时如果不是copy,那么设置完属性之后,其值可能会在对象不知情的情况下被更改了.
所以使用copy修饰符,保证set的时候copy一份不可变(immutable)类型的值,确保前后的一致性.

/** 适用于集合类型(collection type)*/
@property (copy, nonatomic) NSString *name;

***代码静态分析:OCLint

***僵尸对象分析:在Xcode的scheme页面中设置NSZombieEnabled环境变量。点击Product——>Edit Scheme打开该页面,然后勾选Enable Zombie Objects 复选框。

内存泄漏参考:https://www.cnblogs.com/mukekeheart/p/8144742.html

内存泄漏检测工具MLeaksFinderhttps://github.com/Tencent/MLeaksFinder

 

instrument 使用:https://blog.csdn.net/u011146511/article/details/51500628

*****Analyze (shift+command+b)检测内存泄露product----Analyze

Analyze 分析内存 检测出现 User-facing text should use localized string macro 警告问题

Analyze也可以逻辑错误监测:

Analyze是一个编译和分析工具,可以发现编译中的warning,内存泄露隐患,有时还可以查处逻辑上的问题。

内存泄露隐患提示:

Potential Leak of an object allocated on line ……

数据赋值隐患提示:

The left operand of …… is a garbage value;

对象引用隐患提示:

Reference-Counted object is used after it is released;

Analyze发现的问题值得我们注意,但它只是提出隐患,并不一定就存在问题。

 

 

MLeaksFinder原理:

MLeaksFinder 一开始从 UIViewController 入手。我们知道,当一个 UIViewController 被 pop 或 dismiss 后,该 UIViewController 包括它的 view,view 的 subviews 等等将很快被释放(除非你把它设计成单例,或者持有它的强引用,但一般很少这样做)。于是,我们只需在一个 ViewController 被 pop 或 dismiss 一小段时间后,看看该 UIViewController,它的 view,view 的 subviews 等等是否还存在。
具体的方法是,为基类 NSObject 添加一个方法 -willDealloc 方法,该方法的作用是,先用一个弱指针指向 self,并在一小段时间(3秒)后,通过这个弱指针调用 -assertNotDealloc,而 -assertNotDealloc 主要作用是直接中断言。这样,当我们认为某个对象应该要被释放了,在释放前调用这个方法,如果3秒后它被释放成功,weakSelf 就指向 nil,不会调用到 -assertNotDealloc 方法,也就不会中断言,如果它没被释放(泄露了),-assertNotDealloc 就会被调用中断言。这样,当一个 UIViewController 被 pop 或 dismiss 时(我们认为它应该要被释放了),我们遍历该 UIViewController 上的所有 view,依次调 -willDealloc,若3秒后没被释放,就会中断言。
总结起来一句话就是,当一个对象3秒之后还没释放,那么指向它的 weak 指针还是存在的,所以可以调用其 runtime 绑定的方法 willDealloc 从而提示内存泄漏。


**内存泄漏检测框架:FBRetainCycleDetector

NStimer循环引用解决:https://www.jianshu.com/p/bb691938fb2f

循环引用会导致内存泄露,对象释放不掉,会占用内存,如果循环引用多的话,占用内存太大,会导致程序崩溃.

参考:http://blog.csdn.net/lvxiangan/article/details/50202673

 

****检测循环引用:

Leaks 工具只负责检测 Leaked memory,而不管 Abandoned memory。
在 MRC 时代 Leaked memory 很常见,因为很容易忘了调用 release,但在 ARC 时代更常见的内存泄露是循环引用导致的 Abandoned memory,

对于 Abandoned memory,可以用 Instrument 的 Allocations 检测出来。

检测方法:每次点击 Mark Generation 时,Allocations 会生成当前 App 的内存快照,而且 Allocations 会记录从上回内存快照到这次内存快照这个时间段内,新分配的内存信息。重复操作看内存有没有增长;

二.选择Product----->Profile------->工程的模拟器instrument会开始运行,

三,在弹出的对话框中选择Leaks---->Choose.

四,选择Choose后,会弹出如下界面。默认是选择Allocations的。

 五,选择Leaks.

六,选择左上角的红色的圆圈,红色的圆圈变成黒色的方型。此时,开始运行工程,检测循环引用。

七,会出现如下的界面。

八,点击黑色的圆圈,此时又变为红色圆圈,暂时停止循环引用的检测。

 九,此时循环检测完成。在Leaks中出现了一条红色的线条,此线条表示一次内存泄漏的产生。

=============================================

block中多次调用self的问题解决:在block中使用__strong:https://www.jianshu.com/p/d2f551370890

__weak:https://blog.csdn.net/lixuezhi86/article/details/81839906

objc_autorelease:将对象注册到autoreleasepool中,因为对象为弱引用在访问的过程中随时都有可能释放,如果那它加入到autoreleasepool中,那么在autoreleasepool未释放之前对象可以放心使用了。

 

block中的循环引用是这样的:某个对象有一个copy或者strong的block成员变量或者属性,这时block内部直接引用了成员变量或者self,这样就产生了self持有block成员,block成员持有self,就会导致循环引用。因为self本身就是一个strong类型的变量。苹果官方的建议是:传进block之前,把self转换成weak automatic的变量,这样在block中就不会出现对self的强引用。如果在block执行完成之前,self被释放,weakSelf也会置为nil。weak类型相对比较安全,因为可以在释放后自动置为nil,不会引起野指针。那么如何来声明呢?

 1.IOS5.0推出的weak

__weak typeof(self) weakSelf = self;

 

 

 

这句话的意思是声明了一个self类型的weak指针,名字叫做weakSelf.  typeof是用来求参数类型的,这里也就是来求self的类型。这样定义出的weakSelf就是和self是一个类型,并且是原self的一个弱引用。

 

 

以下几种写法同一个意思:

 

 

 

__weak typeof(&*self)weakSelf = self;

// 我之前一直这么写的
__weak typeof(self) weakSelf = self;
// 或者这么写
__weak XxxViewController *weakSelf = self;
// 或者这么写
__weak id weakSelf = self;

2. iOS4.0推出的

    __unsafe_unretained typeof(self) weakSelf = self;

 

 

 

 

 

==================

 

NSTImer的循环引用问题

使用NSTimer可能会碰到循环引用的问题。特别是当类具有NSTimer类型的成员变量,并且需要反复执行计时任务时。例如

_timer = [NSTimer scheduledTimerWithTimeInterval:5.0
                                          target:self
                                        selector:@selector(startCounting) userInfo:nil
                                         repeats:YES];

类有一个成员变量_timer,给_timer设置的target为这个类本身。这样类保留_timer_timer又保留了这个类,就会出现循环引用的问题,最后导致类无法正确释放。

解决这个问题的方式也很简单,当类的使用者能够确定不需要使用这个计时器时,就调用

[_timer invalidate];
_timer = nil;

这样就打破了保留环,类也可以正确释放。但是,这种依赖于开发者手动调用方法,才能让内存正确释放的方式不是一个非常好的处理方式。所以需要另外一种解决方案。如下所示:

@interface NSTimer (JQUsingBlock)
+ (NSTimer *)jq_scheduledTimerWithTimeInterval:(NSTimeInterval)ti
                                     block:(void(^)())block
                                   repeats:(BOOL)repeats;
@end

@implementation NSTimer (JQUsingBlock)

+ (NSTimer *)jq_scheduledTimerWithTimeInterval:(NSTimeInterval)ti
                                     block:(void(^)())block
                                   repeats:(BOOL)repeats{

    return [self scheduledTimerWithTimeInterval:ti
                                     target:self
                                   selector:@selector(jq_blockInvoke:)
                                   userInfo:[block copy]
                                    repeats:repeats];
}

+ (void)jq_blockInvoke:(NSTimer *)timer{

    void(^block)() = timer.userInfo;
    if (block) {
        block();
    }
}

@end

定义一个NSTimer的类别,在类别中定义一个类方法。类方法有一个类型为块的参数(定义的块位于栈上,为了防止块被释放,需要调用copy方法,将块移到堆上)。使用这个类别的方式如下:

__weak ViewController *weakSelf = self;
_timer = [NSTimer jq_scheduledTimerWithTimeInterval:5.0
                                              block:^{
                                                  __strong ViewController *strongSelf = weakSelf;
                                                  [strongSelf startCounting];
                                              }
                                            repeats:YES];

使用这种方案就可以防止NSTimer对类的保留,从而打破了循环引用的产生。__strong ViewController *strongSelf = weakSelf主要是为了防止执行块的代码时,类被释放了。在类的dealloc方法中,记得调用[_timer invalidate]

 

===================

使用Delegate也可能遇到循环引用问题,要用weak修饰Delegate

 

=======================

使用__block 时的循环应用,只要调用以下block就可以解除循环应用;

 

利用__block解决循环的做法。例子4:

#import "ViewController.h"
#import "Student.h"

@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    Student *student = [[Student alloc]init];
    
    __block Student *stu = student;
    student.name = @"Hello World";
    student.study = ^{
        NSLog(@"my name is = %@",stu.name);
        stu = nil;
    };
}
#import "Student.h"

@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    Student *student = [[Student alloc]init];
    
    __block Student *stu = student;
    student.name = @"Hello World";
    student.study = ^{
        NSLog(@"my name is = %@",stu.name);
        stu = nil;
    };
}

这样写会循环么?看上去应该不会。但是实际上却是会的。

由于没有执行study这个block,现在student持有该block,block持有__block变量,__block变量又持有student对象。3者形成了环,导致了循环引用了。 想打破环就需要破坏掉其中一个引用。__block不持有student即可。

只需要执行一下block即可。例子5:

#import "ViewController.h"
#import "Student.h"

@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    Student *student = [[Student alloc]init];
    student.name = @"Hello World";
    __block Student *stu = student;

    student.study = ^{
        NSLog(@"my name is = %@",stu.name);
        stu = nil;
    };

    student.study();
} student.study();
}

这样就不会循环引用了。

=====================以下会出现循环应用

 

#import <Foundation/Foundation.h>
typedef void(^Study)();  
@interface Student : NSObject
@property (copy , nonatomic) NSString *name;
@property (copy , nonatomic) Study study;
@end
typedef void(^Study)();  
@interface Student : NSObject
@property (copy , nonatomic) NSString *name;
@property (copy , nonatomic) Study study;
@end
#import "ViewController.h"
#import "Student.h"

@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
  
    Student *student = [[Student alloc]init];
    student.name = @"Hello World";

    student.study = ^{
        NSLog(@"my name is = %@",student.name);
    };
}
#import "Student.h"

@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
  
    Student *student = [[Student alloc]init];
    student.name = @"Hello World";

    student.study = ^{
        NSLog(@"my name is = %@",student.name);
    };
}

 

到这里,大家应该看出来了,这里肯定出现了循环引用了。student的study的Block里面强引用了student自身。根据上篇文章的分析,可以知道,_NSConcreteMallocBlock捕获了外部的对象,会在内部持有它。retainCount值会加一。 

这里形成环的原因block里面持有student本身,student本身又持有block;

block内部使用了外面强引用的student对象,block代码块的内部会自动产生一个强引用,引用着student对象!所以上面的student对象不会被销毁,造成循环引用!

 

//===========以下这种情况不会出现循环应用

 

 

#import "ViewController.h"
#import "Student.h"

@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    Student *student = [[Student alloc]init];
    student.name = @"Hello World";

    student.study = ^(NSString * name){
        NSLog(@"my name is = %@",name);
    };
    student.study(student.name);
}
#import "Student.h"

@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    Student *student = [[Student alloc]init];
    student.name = @"Hello World";

    student.study = ^(NSString * name){
        NSLog(@"my name is = %@",name);
    };
    student.study(student.name);
}

 

我把block新传入一个参数,传入的是student.name。这个时候会引起循环引用么?

答案肯定是不会。

并不会出现内存泄露。原因是因为,student是作为形参传递进block的,block并不会捕获形参到block内部进行持有。所以肯定不会造成循环引用

 

=====================以下不会出现循环应用

 

 

#import "ViewController.h"
#import "Student.h"

@interface ViewController ()
@property (copy,nonatomic) NSString *name;
@property (strong, nonatomic) Student *stu;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    Student *student = [[Student alloc]init];
    
    self.name = @"halfrost";
    self.stu = student;
    
    student.study = ^{
        NSLog(@"my name is = %@",self.name);
    };
    
    student.study();
}
#import "Student.h"

@interface ViewController ()
@property (copy,nonatomic) NSString *name;
@property (strong, nonatomic) Student *stu;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    Student *student = [[Student alloc]init];
    
    self.name = @"halfrost";
    self.stu = student;
    
    student.study = ^{
        NSLog(@"my name is = %@",self.name);
    };
    
    student.study();
}

ViewController虽然强引用着student,但是student里面的blcok强引用的是viewController的name属性,并没有形成环。如果把上述的self.name改成self,也依旧不会产生循环引用。因为他们都没有强引用这个block。

 

 

 

===================

 

Apple 官方的建议是,传进 Block 之前,把 ‘self’ 转换成 weak automatic 的变量,这样在 Block 中就不会出现对 self 的强引用。如果在 Block 执行完成之前,self 被释放了,weakSelf 也会变为 nil。

示例代码:

__weak __typeof__(self) weakSelf = self;    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

[weakSelf doSomething];

});

clang 的文档表示,在 doSomething 内,weakSelf 不会被释放。但,下面的情况除外:

__weak __typeof__(self) weakSelf = self;  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

[weakSelf doSomething];

[weakSelf doOtherThing];

});

在 doSomething 中,weakSelf 不会变成 nil,不过在 doSomething 执行完成,调用第二个方法 doOtherThing 的时候,weakSelf 有可能被释放,于是,strongSelf 就派上用场了:

__weak __typeof__(self) weakSelf = self;

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

__strong __typeof(self) strongSelf = weakSelf;

[strongSelf doSomething];

[strongSelf doOtherThing];

});

__strong 确保在 Block 内,strongSelf 不会被释放。

总结

1 在 Block 内如果需要访问 self 的方法、变量,建议使用 weakSelf。

2 如果在 Block 内需要多次 访问 self,则需要使用 strongSelf。

 

AFN框架中的例子:

__weak __typeof(self)weakSelf = self;

AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {    

__strong __typeof(weakSelf)strongSelf = weakSelf;   

 strongSelf.networkReachabilityStatus = status;   

 if (strongSelf.networkReachabilityStatusBlock) {      

 strongSelf.networkReachabilityStatusBlock(status);   

 }

};

 

***********僵尸对象:

参考:https://www.jianshu.com/p/f6e6b4f21ca2

程序崩在了一个方法里,并且报错 “Thread 1:EXC_BAD_ACCESS(code=1,address=0x4000)”,这种错误通常是内存管理的问题,一般是访问了已经释放的对象导致的,可以开启僵尸对象(Zombie Objects)来定位问题。

第一步:还是打开Xcode 选择屏幕左上角Xcode-> PReferencese,不过我们这次是要设置一下输出信息,调试的时候输出更多的信息;

第二步:再对环境变量进行设置:菜单Product > Scheme > Edit Scheme;Guard malloc、zombie object、malloc stack前打勾

 

************成员变量在block中循环引用解决:

strongSelf修饰的self 必须为 __weak 修饰过以后的self。

__weak typeof(self) weakSelf = self;

__strong typeof(weakSelf) strongSelf = weakSelf;

 BlockView *view = [BlockView new];
    __weak typeof (self)weak = self;
    view.myBlock = ^{
        // 会循环引用
        _money = @"10元";
        // @property修饰的用下面的不会循环引用
        weak.name = @"xiaobing";

        // 私有成员变量避免循环引用方法
        __strong typeof(weak) sself = weak;
        sself->_text = @"循环引用";
    };

    [self.view addSubview:view];
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值