从GCD推送视图控制器时延迟很长 [英] Long delay when pushing a view controller from GCD

查看:55
本文介绍了从GCD推送视图控制器时延迟很长的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题已解决.有关详细信息,请参见最后一次编辑!

我有一个带有音频可视化效果和一个按钮的UIViewController.当按下按钮时,将触发以下功能:

I have an UIViewController with an audio visualisation and a button. When a button is pressed, the following function is fired:

func use(sender:UIButton!) {
    // Analyse the audio
    let analysisQueue = dispatch_queue_create("analysis", DISPATCH_QUEUE_CONCURRENT)
    dispatch_async(analysisQueue, {
        // Initialise the analysis controller
        let analysisController = AnalysisController()
        analysisController.analyseAudio(global_result.filePath, completion: {
            // When analysis is complete, navigate to another VC
            dispatch_async(dispatch_get_main_queue(), {
                let mainStoryboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
                let vc : UIViewController = mainStoryboard.instantiateViewControllerWithIdentifier("ResultDetailViewController") as UIViewController
                let navigationController = self.navigationController
                // Pop the current VC before pushing the new one
                navigationController?.popViewControllerAnimated(false)
                navigationController?.pushViewController(vc, animated: false)
            })
        })
    })
}

在这里,我创建了一个后台队列,并开始了一个非常漫长的信号处理操作.处理完成后,我将使用主队列执行导航到另一个视图控制器的操作.

Here, I create a background queue and start a very lengthy signal processing operation. Once the processing is complete, I perform navigation to another view controller using the main queue.

这将导致ResultDetailViewController出现在屏幕上,并且所有相关数据和可视化文件均已完全加载.

This causes the ResultDetailViewController to appear on the screen with all the relevant data and visualisation fully loaded.

但是,在VC加载后的最初2-3秒中,没有任何按钮起作用!如果我在此初始时间段内单击任何按钮,则在该初始时间段结束后便会触发该操作.

However, for the first 2-3 seconds after the VC has been loaded, none of the buttons work! If I click any button within this initial period, the action would be fired once that initial period is over.

当我从任何其他VC执行此过渡时,ResultDetailViewController会顺利加载,并且一切正常.

When I perform this transition from any other VC, the ResultDetailViewController is loaded smoothly, and everything works.

我做错了什么?任何帮助将不胜感激!

What am I doing wrong? Any help would be much appreciated!

我将添加有关我的设置的更多详细信息: 在AnalysisController中,我正在执行以下操作:

I will add more details about my set-up: In AnalysisController, I am doing the following:

  1. 使用FFT等处理信号
  2. 更新global_result
  3. 的属性
  4. 触发方法global_result,该方法将结果存储在CoreData中,并使用Alamofire将数据发布到我的服务器中
  5. 第一个POST成功后,回调将更新global_result的ID,并触发更多的POST请求.
  6. 触发完成处理程序,然后导致过渡
  1. Process the signal using FFT's and such
  2. Update a the properties of a global_result
  3. Trigger a method of global_result, which stores the result in CoreData and uses Alamofire to POST the data to my server
  4. Once the first POST succeeds, the callback updates global_result's id, and fires few more POST requests.
  5. Completion handler is triggered, which then causes the transition

global_result是我自定义的全局对象,它初始化为public var.

global_result, is my custom global Object which is initialised as a public var.

从理论上讲,完成处理程序应在处理完成后触发,将结果保存在CoreData中,并分派第一个POST请求.

In theory, the completion handler should be triggered once the processing completes, results are saved in CoreData, and the first POST request is dispatched.

ResultDetailViewControllerviewDidLoad函数中,我将global_result复制到本地变量中,并使用global_result中的数据创建UI元素.

In ResultDetailViewController's viewDidLoad function, I am copying the global_result into a local variable and creating the UI elements using data from global_result.

现在,我怀疑由于已经加载了ResultDetailViewController时使用global_result的后台线程而发生了延迟,因此我尝试创建Result类的新实例,而不是复制global_result,但是这也没有帮助.

Now, I suspected that the lag occurs due to background thread using global_result when ResultDetailViewController is already loaded, so I tried to create a new instance of Result class, instead of copying the global_result, but that also didn't help.

这是Result类:

import Foundation
import CoreData
import Alamofire
import CryptoSwift

public class Result {
    var localID: Int
    var id: Int
    var filePath: NSURL!
    var result: Double
    var origSound: [Double]


    init(localID: Int, id: Int, filePath: NSURL, result: Double, origSound: [Double]) {
        // Initialize stored properties.
        self.localID = localID
        self.id = id
        self.filePath = filePath
        self.result = result
        self.origSound = origSound
    }

    func store() {
        self.storeLocal()

        // Serialise data
        let parameters = [
            "localID": self.localID,
            "id": self.id,
            "result": self.result
        ]

        // Prepare request
        let request = NSMutableURLRequest(URL: NSURL(string: "my_server/script.php")!)
        request.HTTPMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")

        // Encode parameters in JSON and encrypt them
        request.HTTPBody = dictToEncryptedJSONData(rsaKey, parameters: parameters)

        // POST the data to server
        Alamofire.request(request)
            .responseJSON { response in
                if let JSON = response.result.value {
                    if let myID = JSON as? Int {

                        self.id = myID

                        // Store the global ID in CoreData
                        updateIDLocal(self.localID, id: self.id)

                        // Serialise and POST array
                        let param = [
                            "origSound": self.origSound
                        ]

                        // Prepare request
                        var request = NSMutableURLRequest(URL: NSURL(string: "my_server/script2.php?id=\(self.id)")!)
                        request.HTTPMethod = "POST"
                        request.setValue("application/json", forHTTPHeaderField: "Content-Type")

                        // Encode parameters in JSON and encrypt them
                        request.HTTPBody = dictToEncryptedJSONData(rsaKey, parameters: param)

                        // POST the data to server
                        Alamofire.request(request)

                        // Upload the file
                        let upURL = "my_server/script3.php?id=\(self.id)"

                        // Prepare request
                        request = NSMutableURLRequest(URL: NSURL(string: upURL)!)
                        request.HTTPMethod = "POST"
                        request.setValue("application/json", forHTTPHeaderField: "Content-Type")

                        // Encrypt the file
                        request.HTTPBody = fileToEncryptedData(rsaKey, filePath: self.filePath)

                        // POST the data to server
                        Alamofire.request(request)
                    }
                }
        }
    }

    // Store the object in CoreData
    func storeLocal() {
        // Create a local id
        if let oldID = NSUserDefaults.standardUserDefaults().integerForKey("localID") as Int? {
            // Increment the ID
            NSUserDefaults.standardUserDefaults().setInteger(oldID + 1, forKey: "localID")
            self.localID = oldID + 1
        }
        else {
            // First object in CoreData
            NSUserDefaults.standardUserDefaults().setInteger(0, forKey: "localID")
        }

        // Store data in CoreData
        var resultDatas = [NSManagedObject]()
        //1
        let appDelegate =
            UIApplication.sharedApplication().delegate as! AppDelegate

        let managedContext = appDelegate.managedObjectContext

        //2
        let entity =  NSEntityDescription.entityForName("Result",
                                                        inManagedObjectContext:managedContext)

        let resultData = NSManagedObject(entity: entity!,
                                         insertIntoManagedObjectContext: managedContext)

        // Store data
        resultData.setValue(localID, forKey: "localID")
        resultData.setValue(id, forKey: "id")
        resultData.setValue(filePath.path!, forKey: "url")
        resultData.setValue(result, forKey: "result")

        // Store the array
        var data = NSData(bytes: origSound, length: origSound.count * sizeof(Double))
        resultData.setValue(data, forKey: "origSound")

        //4
        do {
            try managedContext.save()
            //5
            resultDatas.append(resultData)
        } catch _  {
            print("Could not save")
        }
    }
}

AnalysisController中,我正在呼叫global_result.store()

我以为发生了什么事?

  1. 创建了后台线程
  2. 在该线程上完成了繁重的处理
  3. 在该线程上发送了POST请求
  4. 已处理HTTP响应,并在该后台线程上加密了许多数据
  5. 跳到主线程
  6. 在主线程上执行过渡

实际上,这发生了:

  1. 创建了后台线程
  2. 在该线程上完成了繁重的处理
  3. 在该线程上发送了POST请求
  4. 跳到主线程
  5. 在主线程上执行过渡
  6. HTTP响应突然回到主线程,并被阻塞,直到大量数据完成加密为止.

感谢 alexcurylo 的建议以及

Thanks to alexcurylo's suggestion, and this SO thread, I realised that the Alamofire's response handling occurs on the main thread, thus it was necessary to use one cool Alamofire feature to push the response handling onto a concurrent thread, so not to block the main queue.

对于以后的参考,我是这样实现的:

For anyone's future reference, I implemented it like so:

// Prepare request
let request = NSMutableURLRequest(URL: NSURL(string: "my_server/script.php")!)
request.HTTPMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")

// Encode parameters in JSON and encrypt them
request.HTTPBody = dictToEncryptedJSONData(rsaKey, parameters: parameters)

// Create a concurrent queue
let queue = dispatch_queue_create("DT.response-queue", DISPATCH_QUEUE_CONCURRENT)

// POST the data to server
Alamofire.request(request)
.response(
    queue: queue,
    responseSerializer: Request.JSONResponseSerializer(options: .AllowFragments),
    completionHandler: { response in
        // Perform response-handling HERE
})

感谢大家的帮助!即使它不能直接解决此问题,也可以 Sandeep Bhandari 的后台队列创建技巧,以及 Igor B 的Storyboard引用方法代表了一种更好的编码实践,应该采用它代替我的原始代码.

Thanks everyone for your help! Even though it didn't directly solve this problem, Sandeep Bhandari's background queue creation tip, and Igor B's method for Storyboard referencing represent a better coding practice, which should be adopted instead of my original code.

推荐答案

延迟后触发的动作是无用的,因为某些处理正在阻塞您的主线程.

The actions firing after a delay is a dead giveaway that some processing is blocking your main thread.

看门狗将帮助您准确确定处理的内容.

Watchdog will help you suss out exactly what that processing is.

用于在主线程上记录过多阻塞的类.它监视主线程,并检查它是否在超过定义的阈值时未被阻塞.您还可以检查代码的哪一部分阻塞了主线程.

Class for logging excessive blocking on the main thread. It watches the main thread and checks if it doesn’t get blocked for more than defined threshold. You can also inspect which part of your code is blocking the main thread.

简单来说,只需实例化看门狗,必须经过几秒才能考虑到主线程被阻塞.此外,您可以启用strictMode,只要达到阈值,该模式就会停止执行.这样,您可以检查代码的哪一部分阻塞了主线程.

Simply, just instantiate Watchdog with number of seconds that must pass to consider the main thread blocked. Additionally you can enable strictMode that stops the execution whenever the threshold is reached. This way, you can inspect which part of your code is blocking the main thread.

从检查来看,问题可能很明显!

Presumably the problem will be obvious from that inspection!

这篇关于从GCD推送视图控制器时延迟很长的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆