現在已解決問題。詳情請看最後的編輯!從GCD推動視圖控制器時的延遲很長
我有一個音頻可視化和按鈕的UIViewController。當按下一個按鈕,下面的功能被激發:
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)
})
})
})
}
在這裏,我創建了一個背景隊列,並啓動一個非常漫長的信號處理操作。處理完成後,我使用主隊列執行導航到另一個視圖控制器。
這會導致ResultDetailViewController
出現在屏幕上,並且所有相關數據和可視化都完全加載。
但是,在VC加載後的前2-3秒,沒有任何按鈕可以工作!如果我在這個初始階段點擊任何按鈕,那麼一旦初始階段結束,就會觸發該動作。
當我從任何其他VC執行此轉換時,ResultDetailViewController
平滑加載,並且一切正常。
我在做什麼錯?任何幫助將非常感激!
編輯1
我會增加約我設置了更多的細節:使用FFT的和這種
- 過程中的信號: 在
AnalysisController
,我做了以下屬性global_result
- 觸發方法
global_result
,它將結果存儲在CoreData中並使用Alamofire將數據發佈到我的服務器 - 第一次POST成功後,回調會更新
global_result
的ID,並且會觸發幾個POST請求。 - 完成處理程序被觸發,然後使過渡
global_result
,是被初始化爲public var
我的自定義全局對象。
理論上,完成處理程序應該在處理完成後觸發,結果保存在CoreData中,並且分派第一個POST請求。
在ResultDetailViewController
的viewDidLoad
函數,我將global_result
複製到本地變量中,並使用global_result
中的數據創建UI元素。現在
,我懷疑是滯後的發生是由於使用global_result
的時候就已經被加載ResultDetailViewController
後臺線程,所以我想,而不是複製global_result
創建Result
類的新實例,但也沒有幫助。
而這裏的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()
EDIT 2
我的想法是怎麼回事,是這樣的:
- 創建了一個後臺線程
- 完成在該線程的繁重處理
- 發送該線程
- HTTP響應被處理POST請求,和大量的數據在該後臺線程
- 躍升至主線程
- 執行的轉變被加密主線程
在現實上,發生這種情況:
- 創建一個後臺線程
- 完成對螺紋
- 發送該線程
- POST請求重處理跳樓主線程
- 執行主線程的過渡
- HTTP響應突然回來了主線程,它被阻塞,直到大量數據完成加密。
由於alexcurylo的建議,並this SO thread,我意識到,在主線程中發生Alamofire的響應處理,因此,有必要使用一個很酷的Alamofire功能推響應處理到併發線程,因此不要阻塞主隊列。
對於任何未來的參考,我實現了它,像這樣:
// 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引用方法代表了更好的編碼實踐,應該採用這種實踐,而不是我的原始代碼。
你在viewDidLoad中viewWillAppear中或ResultDetailViewController的viewDidAppear做任何重物mainthread ??? –
在ResultDetailViewController的viewDidLoad方法中,我從CoreData獲取一些數據,並設置UI元素。但是,從我的應用程序的任何其他部分調用時,VC加載完全正常,所以我懷疑問題在於其他地方。 –
爲什麼DISPATCH_QUEUE_CONCURRENT什麼具體??? –