我正在使用这样的NSTimer
:
timer = [NSTimer scheduledTimerWithTimeInterval:30.0f target:self selector:@selector(tick) userInfo:nil repeats:YES];
当然,NSTimer
会保留创建保留周期的目标。此外,self
不是UIViewController,所以我没有像viewDidUnload
这样的东西,我可以使计时器无效以打破循环。所以我想知道我是否可以使用弱引用:
__weak id weakSelf = self;
timer = [NSTimer scheduledTimerWithTimeInterval:30.0f target:weakSelf selector:@selector(tick) userInfo:nil repeats:YES];
我听说计时器必须无效(我想从运行循环中释放它)。但我们可以在我们的dealloc中做到这一点,对吧?
- (void) dealloc {
[timer invalidate];
}
这是一个可行的选择吗?我已经看到人们处理这个问题的方法很多,但我还没有看到这个。
答案 0 :(得分:74)
建议的代码:
__weak id weakSelf = self;
timer = [NSTimer scheduledTimerWithTimeInterval:30.0f target:weakSelf selector:@selector(tick) userInfo:nil repeats:YES];
具有以下效果:(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
然后你会做类似的事情:
BTWeakTimerTarget *target = [[BTWeakTimerTarget alloc] initWithTarget:self selector:@selector(tick)];
timer = [NSTimer scheduledTimerWithTimeInterval:30.0 target:target selector:@selector(timerDidFire:) ...];
甚至可以将类方法添加到+scheduledTimerWithTimeInterval:target:selector:...
形式的BTWeakTimerTarget中,以创建该代码的更简洁形式。您可能希望公开真实的NSTimer
,以便invalidate
可以展示它,否则建立的规则将是:
答案 1 :(得分: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];
});
}
没有NSTimer @property,没有无效/ runloop的东西,没有代理对象,只是一个简单的干净方法。
这种方法的缺点是(与NSTimer
不同)块的执行时间(包含[weakSelf doSomethingRepeatedly];
)将影响事件的调度。
答案 2 :(得分:22)
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];
}];
Swift 3中的等价:
_timer = Timer(timeInterval: 1.0, repeats: true) { [weak self] _ in
self?.doSomething()
}
如果您仍需要定位iOS 9或更低版本(此时您应该使用此目标),则无法使用此方法,因此您仍需要在其他答案中使用代码。
答案 3 :(得分:8)
Swift 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()
}
}
}
用法:
let timer = WeakTimer.scheduledTimer(timeInterval: 2,
target: self,
repeats: true) { [weak self] timer in
// Place your action code here.
}
timer
是标准类Timer
的实例,因此您可以使用所有可用的方法(例如invalidate
,fire
,isValid
,{{1}等等)
当fireDate
被取消分配或计时器的工作完成时(例如timer
),self
实例将被取消分配。
应用目标> = iOS 10 :
标准计时器实施:
repeats == false
用法:的
open class func scheduledTimer(withTimeInterval interval: TimeInterval,
repeats: Bool,
block: @escaping (Timer) -> Swift.Void) -> Timer
答案 4 :(得分:4)
在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
}
}
然后你可以这样使用它:
let timer = WeakTimerFactory.timerWithTimeInterval(interval, userInfo: userInfo, repeats: repeats) { [weak self] in
// Your code here...
}
返回的NSTimer
对self
的引用较弱,因此您可以在invalidate
中调用其deinit
方法。
答案 5 :(得分:3)
weakSelf 弱的情况并不重要,计时器仍保留对象,因此仍然存在保留周期。由于运行循环保留了一个计时器,你可以(我建议)保持一个指向计时器的弱指针:
NSTimer* __weak timer = [NSTimer scheduledTimerWithTimeInterval:30.0f target: self selector:@selector(tick) userInfo:nil repeats:YES];
关于无效您正在做的事情是正确的。
答案 6 :(得分:0)
如果你使用的是Swift,那么它是一个自动取消定时器:
https://gist.github.com/evgenyneu/516f7dcdb5f2f73d7923
计时器会在deinit
上自动取消。
var timer: AutoCancellingTimer? // Strong reference
func startTimer() {
timer = AutoCancellingTimer(interval: 1, repeats: true) {
print("Timer fired")
}
}
答案 7 :(得分:0)
Swift 4版。必须在dealloc之前调用Invalidate。
class TimerProxy {
var timer: Timer!
var timerHandler: (() -> Void)?
init(withInterval interval: TimeInterval, repeats: Bool, timerHandler: (() -> Void)?) {
self.timerHandler = timerHandler
timer = Timer.scheduledTimer(timeInterval: interval,
target: self,
selector: #selector(timerDidFire(_:)),
userInfo: nil,
repeats: repeats)
}
@objc func timerDidFire(_ timer: Timer) {
timerHandler?()
}
func invalidate() {
timer.invalidate()
}
}
用法
func startTimer() {
timerProxy = TimerProxy(withInterval: 10,
repeats: false,
timerHandler: { [weak self] in
self?.fireTimer()
})
}
@objc func fireTimer() {
timerProxy?.invalidate()
timerProxy = nil
}
答案 8 :(得分:0)
有理论和实践经验。汤米的解决方案行不通。
从理论上讲,__ weak实例作为参数,在
的实现中"""Rock, Paper, Scissors Exercise 8"""
game= input("Are you ready to ply? Y or N: ").capitalize()
user1 = input("What's your name? ")
user2 = input("What's your name? ")
p1 = input(user1 + ": Rock, Paper, Scissors? ").lower()
p2 = input(user2 + ": Rock, Paper, Scissors? ").lower()
p1_count=0
p2_count=0
games_played = 0
while game == "Y":
if p1 == "rock":
if p2 == "rock":
print("It\'s a tie!")
game = input("Are you ready to ply? Y or N: ").capitalize()
p1_count += 1
p2_count += 1
games_played += 1
elif p2 == "scissors":
print(user2 + ", you got beat mothafucka!")
game = input("Are you ready to play? Y or N: ").capitalize()
p1_count += 1
games_played += 1
elif p2 == "paper":
print(user1 + ", you got beat mothafucka!")
game = input("Are you ready to play? Y or N: ").capitalize()
p2_count += 1
games_played += 1
elif p1 == "scissors":
if p2 == "scissors":
print("It\'s a tie!")
game = input("Are you ready to play? Y or N: ").capitalize()
p1_count += 1
p2_count += 1
games_played += 1
elif p2 == "paper":
print(user2 + ", you got beat mothafucka!")
game = input("Are you ready to play? Y or N: ").capitalize()
p1_count += 1
games_played += 1
elif p2 == "rock":
print(user1 + ", you got beat mothafucka!")
game = input("Are you ready to play? Y or N: ").capitalize()
p1_count += 1
games_played += 1
elif p1 == "paper":
if p2 == "paper":
print("It\'s a tie!")
game = input("Are you ready to ply? Y or N: ").capitalize()
p1_count += 1
games_played += 1
elif p2 == "rock":
print(user2 + ", you got beat mothafucka!")
game = input("Are you ready to ply? Y or N: ").capitalize()
p1_count += 1
games_played += 1
elif p2 == "scissors":
print(user1 + ", you got beat mothafucka!")
game = input("Are you ready to ply? Y or N: ").capitalize()
p1_count += 1
games_played += 1
print("Thank you " + user1 + " and " + user2 + " for playing this classic fucking
game!")
print("With " + str(games_played) + " games played, " + "the score was " + user1 + "
with " + str(p1_count) + " and " + user2 + " with " + str(p2_count))
目标将保持不变。
您可以实现一个代理,该代理持有对self的弱引用和前向选择器调用,然后将代理作为目标传递。如YYWeakProxy。
答案 9 :(得分:0)
答案很简单。例如你可以试试这个:
@interface Person : NSObject
@property(nonatomic, strong) DemoViewController_19 *vc;
@end
@implementation Person
@end
@interface DemoViewController_19 ()
@property(nonatomic, strong) Person *person;
@end
@implementation DemoViewController_19
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.person = [Person new];
__weak typeof(self) weaks = self;
self.person.vc = weaks;
}
@end
运行后可以看到没有调用vc dealloc。这取决于Person 的强属性属性。