内存泄漏:https://my.oschina.net/llfk/blog/1031291
内存泄漏监测自动化:http://www.cocoachina.com/articles/18490
facebook三件套内存检测:https://www.jianshu.com/p/bf43f2a7290c、https://www.jianshu.com/p/4730d90e6008、https://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
内存泄漏检测工具MLeaksFinder:https://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的一个弱引用。
以下几种写法同一个意思:
|
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];