为啥 NSTimer 在 swift 中只工作一次
Posted
技术标签:
【中文标题】为啥 NSTimer 在 swift 中只工作一次【英文标题】:why NSTimer works just once in swift为什么 NSTimer 在 swift 中只工作一次 【发布时间】:2016-01-17 12:40:25 【问题描述】:我有一个 http 请求,如果我正确接收到响应,那么我想启动一个每秒触发一个函数的计时器。该函数也是一个http请求。
这是我触发计时器的代码
if let data = data
do
let resultJSON = try NSJSONSerialization.JSONObjectWithData(data, options: [])
requestClient.id = resultJSON["id"] as! Double
self.timerResponse = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "checkIfThereAreResponeses", userInfo: nil, repeats: true)
self.timerResponse!.fire()
catch
如你所见,我正在调用一个名为checkIfThereAreResponses
的函数
在该函数中,我有一个打印语句,并且该打印语句只打印一次,虽然我的计时器应该每 1 秒工作一次
我缺少什么?
这就是函数
func checkIfThereAreResponeses()
if (requestClient!.id != nil)
let url = NSURL(string: "http://localhost:blablabla")
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
let session = NSURLSession.sharedSession()
task = session.dataTaskWithRequest(request, completionHandler: (data, response, error) in
if let error = error
print("error = \(error)")
if let data = data
do
let resultJSONArray = try NSJSONSerialization.JSONObjectWithData(data, options: []) as! NSArray
bla bla bla
catch
print("no responses yet = \(error)")
)
task!.resume()
else
print("not yet ")
我收到的 JUST ONCE 是no response yet
【问题讨论】:
我无法重现该问题,print
是 checkIfThereAreResponeses
中的第一条语句吗?
@luk2302 但它在里面。
但它是第一个吗?查看实际的checkIfThereAreResponeses
可能会有所帮助!
@luk2302 好的,我给了你代码
@luk2302 我现在在函数的开头打印了一个,它仍然只工作一次,但是当我将计时器放在主线程中时它可以工作
【参考方案1】:
如果您在完成处理程序中有此代码,建议 NSTimer 在主线程中运行,因此这可能是原因。你可能需要这样的东西:
dispatch_async(dispatch_get_main_queue(),
// Your timer logic here
)
Apple 文档说:
定时器与运行循环一起工作。为了有效地使用计时器, 你应该知道运行循环是如何运作的——参见 NSRunLoop。
(NSTimer)
NSRunLoop 类通常不被认为是线程安全的,并且 它的方法只能在当前的上下文中调用 线。你永远不应该尝试调用 NSRunLoop 的方法 对象在不同的线程中运行,因为这样做可能会导致 意想不到的结果。
(NSRunLoop)
【讨论】:
您是否打算将我的代码更改为`dispatch_async(dispatch_get_main_queue(), self.timerResponse = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "checkIfThereAreResponeses", userInfo: nil, 重复: true) self.timerResponse!.fire() )` ? @sarah 是的,类似的就是答案的含义。 我可以有一个链接来增加我在这个问题上的知识吗? 不建议在主线程中运行 NSTimer,只是为了方便。 NSTimer 可以在任何线程的 runloop 上运行,您只需要了解该线程的生命周期。主线程很方便,因为除非您的应用程序死亡,否则它永远不会死亡。重要的是要意识到,当计时器触发时,选择器会在具有运行计时器的 runloop 的线程上调用。【参考方案2】:NSTimer 的工作原理是被安排在一个运行循环上。如果在 session 的 completionHandler 中调用这个方法,它会被调度到当前正在运行该 completionHandler 的线程的 runloop。由于这个线程不属于你,而是由系统拥有,它会被系统处理掉。这可能是在完成处理程序完成执行之后立即发生的,也可能是很久以后。有了线程,runloop 也消失了,因此,您的计时器可能永远不会触发,几次或仅触发一次;没有确切的说法,因为这取决于系统何时删除线程。
因此,您应该在您拥有的线程上创建计时器,或者更简单地将计时器的创建分派给主线程(dispatch_get_main_queue()
是您的朋友)。另一种选择是使用+timerWithTimeInterval...
方法之一创建一个NSTimer,然后使用[NSRunloop mainRunloop] addTimer: yourTimer forMode NSRunLoopCommonModes]
将其添加到主运行循环中。请记住,计时器将在与其运行的 runloop 相同的线程上调用您的选择器。
附带说明,轮询服务器可能不是在移动设备上运行的最佳方式。如果您控制服务器,最好在有新数据时向设备发送推送通知。这将节省设备的电池电量,并减少服务器的负载。但是,如果你不控制服务器,实现这一点会更复杂,然后轮询可能是一个很好的折衷方案。
【讨论】:
以上是关于为啥 NSTimer 在 swift 中只工作一次的主要内容,如果未能解决你的问题,请参考以下文章
cellForItemAt 在 Swift collectionView 中只调用一次