此问题现已解决。有关详细信息,请参阅上一个编辑!
我有一个带有音频可视化和按钮的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
会顺利加载,一切正常。
我做错了什么?任何帮助将不胜感激!
我将添加有关我的设置的更多详细信息:
在AnalysisController
中,我正在执行以下操作:
global_result
global_result
方法,该方法将结果存储在CoreData中并使用Alamofire将数据发布到我的服务器global_result
的ID,并触发更多POST请求。 global_result
,是我的自定义全局对象,初始化为public var
。
理论上,处理完成后应触发完成处理程序,结果保存在CoreData中,并调度第一个POST请求。
在ResultDetailViewController
的{{1}}函数中,我将viewDidLoad
复制到局部变量中,并使用来自global_result
的数据创建UI元素。
现在,我怀疑由于后台线程在global_result
已经加载时使用global_result
而导致延迟,所以我尝试创建ResultDetailViewController
类的新实例,而不是复制Result
,但这也没有帮助。
这里是global_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
我的想法是这样的:
实际上,这发生了:
感谢alexcurylo的建议和this SO thread,我意识到Alamofire的响应处理发生在主线程上,因此有必要使用一个很酷的Alamofire将响应处理推送到并发线程的功能,以免阻塞主队列。
对于任何人未来的参考,我都是这样实现的:
global_result.store()
感谢大家的帮助!即使它没有直接解决这个问题,Sandeep Bhandari的后台队列创建提示,而Igor B的故事板引用方法代表了更好的编码实践,应该采用而不是我的原始代码。
答案 0 :(得分:2)
延迟后触发的动作是一个死的赠品,有些处理正在阻止你的主线程。
Watchdog将帮助您确切了解该处理的内容。
用于在主线程上记录过多阻塞的类。它监视主线程并检查它是否被阻止超过定义的阈值。您还可以检查代码的哪一部分阻塞主线程。
简单地说,只需使用必须通过的秒数来实例化Watchdog以考虑主线程被阻止。此外,您可以启用strictMode,以便在达到阈值时停止执行。这样,您可以检查代码的哪一部分阻塞主线程。
据推测,这个问题显而易见!
答案 1 :(得分:1)
尝试更改创建可能有用的后台线程的方式:)
func use(sender:UIButton!) {
// Analyse the audio
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
// 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)
})
})
})
}
答案 2 :(得分:1)
我对UIStoryboard的实例化可能会消耗任务,所以我建议尝试这样的事情:
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 = self.navigationController!.storyboard
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)
})
})
})
}