ben*_*ree 52 objective-c nstimer automatic-ref-counting retain-cycle
我用的NSTimer是这样的:
timer = [NSTimer scheduledTimerWithTimeInterval:30.0f target:self selector:@selector(tick) userInfo:nil repeats:YES];
Run Code Online (Sandbox Code Playgroud)
当然,NSTimer保留了创建保留周期的目标.此外,self不是一个UIViewController所以我没有任何东西viewDidUnload,我可以无效的计时器来打破循环.所以我想知道我是否可以使用弱引用:
__weak id weakSelf = self;
timer = [NSTimer scheduledTimerWithTimeInterval:30.0f target:weakSelf selector:@selector(tick) userInfo:nil repeats:YES];
Run Code Online (Sandbox Code Playgroud)
我听说计时器必须无效(我想从运行循环中释放它).但我们可以在我们的dealloc中做到这一点,对吗?
- (void) dealloc {
[timer invalidate];
}
Run Code Online (Sandbox Code Playgroud)
这是一个可行的选择吗?我已经看到人们处理这个问题的方法很多,但我还没有看到这个.
Tom*_*mmy 74
建议的代码:
__weak id weakSelf = self;
timer = [NSTimer scheduledTimerWithTimeInterval:30.0f target:weakSelf selector:@selector(tick) userInfo:nil repeats:YES];
Run Code Online (Sandbox Code Playgroud)
具有以下效果:(i)对自我的弱引用; (ii)读取弱引用以提供指针NSTimer.它不具有NSTimer使用弱引用创建的效果.该代码与使用__strong引用之间的唯一区别是,如果在给定的两行之间释放self,那么您将传递nil给计时器.
您可以做的最好的事情是创建一个代理对象.就像是:
[...]
@implementation BTWeakTimerTarget
{
__weak target;
SEL selector;
}
[...]
- (void)timerDidFire:(NSTimer *)timer
{
if(target)
{
[target performSelector:selector withObject:timer];
}
else
{
[timer invalidate];
}
}
@end
Run Code Online (Sandbox Code Playgroud)
然后你会做类似的事情:
BTWeakTimerTarget *target = [[BTWeakTimerTarget alloc] initWithTarget:self selector:@selector(tick)];
timer = [NSTimer scheduledTimerWithTimeInterval:30.0 target:target selector:@selector(timerDidFire:) ...];
Run Code Online (Sandbox Code Playgroud)
或者甚至将类方法添加到表单的BTWeakTimerTarget +scheduledTimerWithTimeInterval:target:selector:...以创建该代码的更简洁的形式.您可能希望公开真实,NSTimer以便您可以invalidate,否则建立的规则将是:
Jay*_*hao 26
如果您不关心计时器事件的毫秒准确性,可以使用dispatch_after&__ weak而不是NSTimer来执行此操作.这是代码模式:
- (void) doSomethingRepeatedly
{
// Do it once
NSLog(@"doing something …");
// Repeat it in 2.0 seconds
__weak typeof(self) weakSelf = self;
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[weakSelf doSomethingRepeatedly];
});
}
Run Code Online (Sandbox Code Playgroud)
没有NSTimer @property,没有无效/ runloop的东西,没有代理对象,只是一个简单的干净方法.
这种方法的缺点是(不像NSTimer)块(包含[weakSelf doSomethingRepeatedly];)的执行时间将影响事件的调度.
ken*_*ytm 25
iOS 10和macOS 10.12"Sierra"引入了一种新方法+scheduledTimerWithTimeInterval:repeats:block:,因此您可以将其self简单地捕获为:
__weak MyClass* weakSelf = self;
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer* t) {
MyClass* _Nullable strongSelf = weakSelf;
[strongSelf doSomething];
}];
Run Code Online (Sandbox Code Playgroud)
Swift 3中的等价:
_timer = Timer(timeInterval: 1.0, repeats: true) { [weak self] _ in
self?.doSomething()
}
Run Code Online (Sandbox Code Playgroud)
如果您仍然需要针对iOS 9或更低版本(此时您应该使用此目标),则无法使用此方法,因此您仍需要在其他答案中使用代码.
斯威夫特3
应用目标<iOS 10:
自定义WeakTimer(GitHubGist)实现:
final class WeakTimer {
fileprivate weak var timer: Timer?
fileprivate weak var target: AnyObject?
fileprivate let action: (Timer) -> Void
fileprivate init(timeInterval: TimeInterval,
target: AnyObject,
repeats: Bool,
action: @escaping (Timer) -> Void) {
self.target = target
self.action = action
self.timer = Timer.scheduledTimer(timeInterval: timeInterval,
target: self,
selector: #selector(fire),
userInfo: nil,
repeats: repeats)
}
class func scheduledTimer(timeInterval: TimeInterval,
target: AnyObject,
repeats: Bool,
action: @escaping (Timer) -> Void) -> Timer {
return WeakTimer(timeInterval: timeInterval,
target: target,
repeats: repeats,
action: action).timer!
}
@objc fileprivate func fire(timer: Timer) {
if target != nil {
action(timer)
} else {
timer.invalidate()
}
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
let timer = WeakTimer.scheduledTimer(timeInterval: 2,
target: self,
repeats: true) { [weak self] timer in
// Place your action code here.
}
Run Code Online (Sandbox Code Playgroud)
timer是标准的类的实例Timer,所以你可以使用所有可用的方法(例如invalidate,fire,isValid,fireDate和等).
timer当self取消分配或计时器的工作完成时(例如repeats == false),将释放实例.
App target> = iOS 10:
标准计时器实现:
open class func scheduledTimer(withTimeInterval interval: TimeInterval,
repeats: Bool,
block: @escaping (Timer) -> Swift.Void) -> Timer
Run Code Online (Sandbox Code Playgroud)
用法:
let timer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { [weak self] timer in
// Place your action code here.
}
Run Code Online (Sandbox Code Playgroud)
在 Swift 中我定义了一个WeakTimer辅助类:
/// A factory for NSTimer instances that invoke closures, thereby allowing a weak reference to its context.
struct WeakTimerFactory {
class WeakTimer: NSObject {
private var timer: NSTimer!
private let callback: () -> Void
private init(timeInterval: NSTimeInterval, userInfo: AnyObject?, repeats: Bool, callback: () -> Void) {
self.callback = callback
super.init()
self.timer = NSTimer(timeInterval: timeInterval, target: self, selector: "invokeCallback", userInfo: userInfo, repeats: repeats)
}
func invokeCallback() {
callback()
}
}
/// Returns a new timer that has not yet executed, and is not scheduled for execution.
static func timerWithTimeInterval(timeInterval: NSTimeInterval, userInfo: AnyObject?, repeats: Bool, callback: () -> Void) -> NSTimer {
return WeakTimer(timeInterval: timeInterval, userInfo: userInfo, repeats: repeats, callback: callback).timer
}
}
Run Code Online (Sandbox Code Playgroud)
然后你可以像这样使用它:
let timer = WeakTimerFactory.timerWithTimeInterval(interval, userInfo: userInfo, repeats: repeats) { [weak self] in
// Your code here...
}
Run Code Online (Sandbox Code Playgroud)
返回的值NSTimer对 具有弱引用self,因此您可以invalidate在 中调用其方法deinit。