We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
//ClassA: @protocol ClssADelegate - (void)fuck; @end @interface ClassA : UIViewController @property (nonatomic, strong) id delegate; @end //ClassB: @interface ClassB () @property (nonatomic, strong) ClassA *classA; @end @implementation ClassB - (void)viewDidLoad { [super viewDidLoad]; self.classA = [[ClassA alloc] init]; self.classA.delegate = self; }
ClassB --> classA(ClassA) --> delegate --> self (ClassB)
解决方案将delegate声明为weak类型,这个是最常见的。
@interface ClassA () @property (nonatomic, copy) dispatch_block_t block; @property (nonatomic, assign) NSInteger tem; @end @implementation ClassA - (void)viewDidLoad { [super viewDidLoad]; self.block = ^{ self.tem = 1; }; }
解决方案,用weak指针指向self。然后在block中使用weak指针访问self对象。
@interface ClassA () @property (nonatomic, copy) dispatch_block_t block; @property (nonatomic, assign) NSInteger tem; @end @implementation ClassA - (void)viewDidLoad { [super viewDidLoad]; __weak typeof(self) weakSelf = self self.block = ^{ weakSelf.tem = 1; }; }
方案2. 通过将对象在block中设置为nil,但是这种需要注意的是block一定要被执行。
使用Strong-Weak Dance 来避免执行block过程中self被释放。
__weak MyViewController *wself = self; self.completionHandler = ^(NSInteger result) { [wself.property removeObserver: wself forKeyPath:@"pathName"]; };
假设 block 被放在子线程中执行,而且执行过程中 self 在主线程被释放了。由于 wself 是一个弱引用,因此会自动变为 nil。而在 KVO 中,这会导致崩溃。 如果在 block 内部不使用强引用,而是通过 if 判空貌似可以规避这个问题,但是在多线程的情况下有可能会在判断非空的时候self还没释放,但是在判断语句过后被置为空,同样也会崩溃。而Strong-Weak Dance的本质是在执行block的时候将self给hold在Block 局部变量中,所以在这个Block中会强持有self,保证在执行block的时候self不会被释放掉。但是它又回在Block结束的时候释放掉,所以不会导致循环引用。如果在 block 执行以前,self 就释放了,那么 block 的引用计数降为 0,所以自己就会被释放。这样它根本就不会被执行。
self.timmer = [NSTimer timerWithTimeInterval:3 target:self selector:@selector(tick) userInfo:nil repeats:YES]; [self.timmer fire];
- (void)dealloc { [self.timer invalidate]; self.timer = nil; }
运行上面代码你会发现,由于上面发生了死循环所以dealloc不会执行,导致定时器不会停止。从而导致内存泄漏。
这里明显有循环引用的存在,Object 持有timmer,timmer持有Object,(Object 作为timmer的target)。 如果将timmer作为弱引用可以解决吗?
答案是否定的,因为在定时器分派之后,当前线程所在的Runloop会持有timmer,进而间接持有Object。
这种情况有哪些方式可以解除循环引用?
如果是单次的定时器比较简单,只要在block中将定时器停止就可以了,下面是针对循环定时器而言的
- (void)stopTimmer { [self.timer invalidate]; self.timer = nil; }
这也是BlocksKit中所采用的一种方法。
@interface NSTimer (BlocksKitPrivate) + (void)bk_executeBlockFromTimer:(NSTimer *)aTimer; @end @implementation NSTimer (BlocksKit) + (id)bk_scheduledTimerWithTimeInterval:(NSTimeInterval)inTimeInterval block:(void (^)(NSTimer *timer))block repeats:(BOOL)inRepeats { NSParameterAssert(block != nil); return [self scheduledTimerWithTimeInterval:inTimeInterval target:self selector:@selector(bk_executeBlockFromTimer:) userInfo:[block copy] repeats:inRepeats]; } + (id)bk_timerWithTimeInterval:(NSTimeInterval)inTimeInterval block:(void (^)(NSTimer *timer))block repeats:(BOOL)inRepeats { NSParameterAssert(block != nil); return [self timerWithTimeInterval:inTimeInterval target:self selector:@selector(bk_executeBlockFromTimer:) userInfo:[block copy] repeats:inRepeats]; } + (void)bk_executeBlockFromTimer:(NSTimer *)aTimer { void (^block)(NSTimer *) = [aTimer userInfo]; if (block) block(aTimer); }
这种就可以在Object的dealloc中调用如下方法释放timmer了:
@interface IDLWeakTimmerObject : NSObject @property(nonatomic, weak, readwrite) id target; @property(nonatomic, weak, readwrite) NSTimer *timmer; @property(nonatomic, assign, readwrite) SEL selector; - (void)fire; @end @implementation IDLWeakTimmerObject - (void)fire { if(self.target) { if([self.target respondsToSelector:self.selector]) { [self.target performSelector:self.selector]; } } else { [self.timmer invalidate]; } } @end @implementation NSTimer (IDLAddion) + (NSTimer *)scheduledWeakTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo { IDLWeakTimmerObject *weakTimmerObject = [IDLWeakTimmerObject new]; weakTimmerObject.target = aTarget; weakTimmerObject.selector = aSelector; weakTimmerObject.timmer = [NSTimer scheduledTimerWithTimeInterval:ti target:weakTimmerObject selector:@selector(fire) userInfo:userInfo repeats:YES]; return weakTimmerObject.timmer; } @end
这种方式就是通过一个中间对象持有Object和NSTimer的弱引用,当Object被释放,并且下一个定时循环的时候,中间对象负责检测Object是否为空,如果为空则停止定时器,定时器一旦停止,RunLoop就会释放Timer。
YYText中也包含了一个YYTextWeakProxy类,它是使用YYTextWeakProxy作为代理,弱引用target,然后所有的方法都转发给target。 具体的代码这里也贴出来:
@implementation MyView { NSTimer *_timer; } - (void)initTimer { YYTextWeakProxy *proxy = [YYTextWeakProxy proxyWithTarget:self]; _timer = [NSTimer timerWithTimeInterval:0.1 target:proxy selector:@selector(tick:) userInfo:nil repeats:YES]; } - (void)tick:(NSTimer *)timer {...} @end
@implementation YYTextWeakProxy - (instancetype)initWithTarget:(id)target { _target = target; return self; } + (instancetype)proxyWithTarget:(id)target { return [[YYTextWeakProxy alloc] initWithTarget:target]; } - (id)forwardingTargetForSelector:(SEL)selector { return _target; } - (void)forwardInvocation:(NSInvocation *)invocation { void *null = NULL; [invocation setReturnValue:&null]; } - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { return [NSObject instanceMethodSignatureForSelector:@selector(init)]; } //........ @end
The text was updated successfully, but these errors were encountered:
No branches or pull requests
解决方案将delegate声明为weak类型,这个是最常见的。
解决方案,用weak指针指向self。然后在block中使用weak指针访问self对象。
方案2. 通过将对象在block中设置为nil,但是这种需要注意的是block一定要被执行。
使用Strong-Weak Dance 来避免执行block过程中self被释放。
假设 block 被放在子线程中执行,而且执行过程中 self 在主线程被释放了。由于 wself 是一个弱引用,因此会自动变为 nil。而在 KVO 中,这会导致崩溃。 如果在 block 内部不使用强引用,而是通过 if 判空貌似可以规避这个问题,但是在多线程的情况下有可能会在判断非空的时候self还没释放,但是在判断语句过后被置为空,同样也会崩溃。而Strong-Weak Dance的本质是在执行block的时候将self给hold在Block 局部变量中,所以在这个Block中会强持有self,保证在执行block的时候self不会被释放掉。但是它又回在Block结束的时候释放掉,所以不会导致循环引用。如果在 block 执行以前,self 就释放了,那么 block 的引用计数降为 0,所以自己就会被释放。这样它根本就不会被执行。
运行上面代码你会发现,由于上面发生了死循环所以dealloc不会执行,导致定时器不会停止。从而导致内存泄漏。
这里明显有循环引用的存在,Object 持有timmer,timmer持有Object,(Object 作为timmer的target)。
如果将timmer作为弱引用可以解决吗?
答案是否定的,因为在定时器分派之后,当前线程所在的Runloop会持有timmer,进而间接持有Object。
这种情况有哪些方式可以解除循环引用?
如果是单次的定时器比较简单,只要在block中将定时器停止就可以了,下面是针对循环定时器而言的
这也是BlocksKit中所采用的一种方法。
这种就可以在Object的dealloc中调用如下方法释放timmer了:
这种方式就是通过一个中间对象持有Object和NSTimer的弱引用,当Object被释放,并且下一个定时循环的时候,中间对象负责检测Object是否为空,如果为空则停止定时器,定时器一旦停止,RunLoop就会释放Timer。
YYText中也包含了一个YYTextWeakProxy类,它是使用YYTextWeakProxy作为代理,弱引用target,然后所有的方法都转发给target。 具体的代码这里也贴出来:
The text was updated successfully, but these errors were encountered: