我正在开发一款iPhone应用程序,它可以进行大量的数学计算,大约需要5-6秒,但重复几次(因此,通常需要大约50-60秒)。完整的计算集可以一起重复几次。每个计算都封装在NSOperation
。
我遇到一个问题,在第一次运行计算期间,如果某个应用尝试显示UITableView
,其中任何单元格都有UISwitch
,那么所有应用内容都会冻结,直到NSOperationQueue
计算变空。 (在我的应用中,如果用户尝试打开包含此类表视图的应用内首选项,则在首次运行计算完成之前,UI会冻结)。
当我发生这种情况时,我花了大约一个星期的时间在冰点到尖点的状态。以下是在我的应用中重现问题的代码。如果你运行它并在所有5个操作完成之前按“显示表”,UI将冻结,直到队列为空(如果操作在你的机器上完成得太快,则减少添加的数字)。
如果我用GCD替换NSOperationQueue
,问题就会消失。如果我将UISwitch
替换为UIButton
,问题也会消失。我正在运行Xcode 6.4(6E35b)。
有时UI不会在此演示代码中冻结(但是,它总是在我的实际应用程序中冻结),但重新运行它会使它最终出现。此外,我发现在我的机器上设置maxConcurrentOperationCount
为2(2核心2009 MBP)会使问题消失,但将其设置为更高的数字会使其更频繁地发生。
任何甚至是偏远的建议都将受到高度赞赏。
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
private let queue = NSOperationQueue()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let viewController = UIViewController()
viewController.view.backgroundColor = UIColor.whiteColor()
viewController.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Show table", style: .Plain, target: self, action: "showTable:")
let navigationController = UINavigationController(rootViewController: viewController)
let screenBounds = UIScreen.mainScreen().bounds
window = UIWindow(frame: screenBounds)
window?.rootViewController = navigationController
window?.makeKeyAndVisible()
setUpComputations()
return true
}
func showTable(sender: UIBarButtonItem!) {
let tableViewController = TableViewController()
let navigationController = UINavigationController(rootViewController: tableViewController)
window?.rootViewController?.presentViewController(navigationController, animated: true, completion: nil)
println("showed table")
}
private func setUpComputations() {
// queue.maxConcurrentOperationCount = 2
for i in 0...5 {
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
queue.addOperationWithBlock {
dispatch_async(dispatch_get_main_queue()) {
println("starting operation \(i)")
}
var sum = 0.0
for _ in 0...5 {
while sum < 1000.0 {
sum += 1.0e-6
}
}
dispatch_async(dispatch_get_main_queue()) {
println("finishing operation \(i)")
}
}
}
}
}
internal final class TableViewController: UITableViewController {
func dismiss(sender: UIBarButtonItem!) {
dismissViewControllerAnimated(true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Done", style: .Done, target: self, action: "dismiss:")
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell
?? UITableViewCell(style: .Default, reuseIdentifier: "Cell")
cell.textLabel?.text = "Some text"
cell.accessoryView = UISwitch()
// cell.accessoryView = UIButton.buttonWithType(.InfoLight) as! UIButton
return cell
}
}
附录:感谢 Rob 的输入,我注意到在操作上设置qualityOfService = .Background
而不是队列,似乎可以解决问题。即,用
setUpComputations
private func setUpComputations() {
for i in 0...5 {
let operation = NSBlockOperation {
dispatch_async(dispatch_get_main_queue()) {
println("starting operation \(i)")
}
var sum = 0.0
for _ in 0...5 {
while sum < 1000.0 {
sum += 1.0e-6
}
}
dispatch_async(dispatch_get_main_queue()) {
println("finishing operation \(i)")
}
}
operation.qualityOfService = .Background
queue.addOperation(operation)
}
}