首先,我只是一个初学者,目前正在用Swift语言开发一个应用程序,所以请不要太介意我的问题,因为我真的需要知道,而且我在维护我构建的代码时遇到了麻烦。
这是关于异步委托模式的。
这是我的API课程。假设有许多类似的API类进行异步调用。
protocol InitiateAPIProtocol{
func didSuccessInitiate(results:JSON)
func didFailInitiate(err:NSError)
}
class InitiateAPI{
var delegate : InitiateAPIProtocol
init(delegate: InitiateAPIProtocol){
self.delegate=delegate
}
func post(wsdlURL:String,action:String,soapMessage : String){
let request = NSMutableURLRequest(URL: NSURL(string: wsdlURL)!)
let msgLength = String(soapMessage.characters.count)
let data = soapMessage.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
request.HTTPMethod = "POST"
request.addValue("text/xml; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.addValue(msgLength, forHTTPHeaderField: "Content-Length")
request.addValue(action, forHTTPHeaderField: "SOAPAction")
request.HTTPBody = data
let task = session.dataTaskWithRequest(request) {
data, response, error in
if error != nil {
self.delegate.didFailInitiate(error!)
return
}
let jsonData = JSON(data: data)
self.delegate.didSuccessInitiate(jsonData)
}
task.resume()
}
func doInitiate(token : String){
let soapMessage = “”
// WSDL_URL is the main wsdl url i will request.
action = “”
post(WSDL_URL, action: action, soapMessage: soapMessage)
}
}
这是我的ViewController:
class ViewController : UIViewController,InitiateAPIProtocol{
var initiateAPI : InitiateAPI!
var token : String = “sWAFF1”
override func viewWillAppear(animated: Bool) {
// Async call start
initiateAPI = InitiateAPI(delegate:self)
initiateAPI.doInitiate(token)
}
// Here comes call back
func didSuccessInitiate(results: JSON) {
//handle results
}
func didFailInitiate(err: NSError) {
//handle errors
}
}
我的问题是,我说有很多类似的API类,所以如果一个视图控制器处理4个API类,那么在扩展视图控制器时,我必须处理许多协议委托方法。视图控制器下面将有许多委托方法。如果其他视图控制器调用相同的API并必须处理相同的委托,那么我在维护代码时会遇到问题,因为每次我更改一些委托参数时,我都必须修复使用这些API类的所有视图控制器上的代码。
有其他处理异步调用的好方法吗?
如果我的问题有点复杂,请留言评论,我会回复并解释清楚
委托(OOP)和"完成处理程序"(类似函数的编程)不能很好地结合在一起。
为了提高理解能力并使代码更加简洁,需要一种替代方法。其中一种方法已经由@PEEJWEEJ单独使用完成处理程序提出。
另一种方法是使用"期货或承诺"。这些极大地扩展了完成处理程序的思想,并使异步代码看起来更像同步。
期货的运作方式基本如下。假设您有一个API函数,它从远程web服务获取用户。此调用是异步的。
// Given a user ID, fetch a user:
func fetchUser(id: Int) -> Future<User> {
let promise = Promise<User>()
// a) invoke the asynchronous operation.
// b) when it finished, complete the promise accordingly:
doFetchAsync(id, completion: {(user, error) in
if error == nil {
promise.fulfill(user!)
} else {
promise.reject(error!)
}
})
return.promise.future
}
首先,这里的重要事实是,没有完成处理程序。相反,异步函数会返回一个未来。future表示底层操作的最终结果。当函数fetchUser
返回时,结果尚未计算,未来处于"挂起"状态。也就是说,您无法从未来立即获得结果。所以,我们必须等待??-其实不然,这将类似于带有完成处理程序的异步函数来完成,即注册一个"continuation":
为了获得结果,注册一个完成处理程序:
fetchUser(userId).map { user in
print("User: (user)")
}.onFailure { error in
print("Error: (error)")
}
如果出现错误,它还会处理这些错误。
函数map
是注册延续的函数。它也是一个"组合子",也就是说,它返回另一个未来,您可以将其与其他函数组合,并组成更复杂的操作。
当future最终完成时,代码将继续,并在future中注册闭包。
如果您有两个相关的操作,例如OP1生成一个结果,该结果应在OP2中用作输入,并且组合的结果应返回(作为将来),您可以以全面简洁的方式实现这一点:
let imageFuture = fetchUser(userId).flatMap { user in
return user.fetchProfileImage()
}
imageFuture.onSuccess { image in
// Update table view on main thread:
...
}
这只是对未来的一个非常简短的介绍。他们可以为你做更多的事情。
如果你想看到未来的行动,你可以在第三方图书馆FutureLib(我是作者)中启动Xcode游乐场"激励示例"。您还应该检查其他Future/Promise库,例如BrightFutures。这两个库都在Swift中实现了类似Scala的futures。
您查看过NSNotificationCenter吗?
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/
您将能够从您的api类发布事件,然后每个视图控制器都会订阅这些事件,并相应地收到通知
这有道理吗?这种模式有很多很好的例子:https://stackoverflow.com/a/24049111/2678994
https://stackoverflow.com/a/28269217/2678994
我在下面更新了你的代码:
class InitiateAPI{
//
// var delegate : InitiateAPIProtocol
// init(delegate: InitiateAPIProtocol){
// self.delegate=delegate
// }
func post(wsdlURL:String,action:String,soapMessage : String){
let request = NSMutableURLRequest(URL: NSURL(string: wsdlURL)!)
let msgLength = String(soapMessage.characters.count)
let data = soapMessage.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
request.HTTPMethod = "POST"
request.addValue("text/xml; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.addValue(msgLength, forHTTPHeaderField: "Content-Length")
request.addValue(action, forHTTPHeaderField: "SOAPAction")
request.HTTPBody = data
let task = session.dataTaskWithRequest(request) {
data, response, error in
if error != nil {
// self.delegate.didFailInitiate(error!)
/* Post notification with error */
NSNotificationCenter.defaultCenter().postNotificationName("onHttpError", object: error)
return
}
let jsonData = JSON(data: data)
// self.delegate.didSuccessInitiate(jsonData)
/* Post notification with json body */
NSNotificationCenter.defaultCenter().postNotificationName("onHttpSuccess", object: jsonData)
}
task.resume()
}
func doInitiate(token : String){
let soapMessage = “”
// WSDL_URL is the main wsdl url i will request.
action = “”
post(WSDL_URL, action: action, soapMessage: soapMessage)
}
}
您的视图控制器类:
class ViewController : UIViewController { //,InitiateAPIProtocol{
var initiateAPI : InitiateAPI!
var token : String = “sWAFF1”
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.didSuccessInitiate(_:)), name: "onHttpSuccess", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.didFailInitiate(_:)), name: "onHttpError", object: nil)
}
override func viewWillAppear(animated: Bool) {
// Async call start
initiateAPI = InitiateAPI(delegate:self)
initiateAPI.doInitiate(token)
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
/* Remove listeners when view controller disappears */
NSNotificationCenter.defaultCenter().removeObserver(self, name: "onHttpSuccess", object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self, name: "onHttpError", object: nil)
}
// Here comes call back
func didSuccessInitiate(notification : NSNotification) { //results: JSON) {
if let payload = notification.object as? JSON {
//handle results
}
}
func didFailInitiate(notification : NSNotification) { //err: NSError) {
if let payload = notification.object as? NSError {
//handle errors
}
}
}
您可以(应该?)使用闭包/函数:而不是使用委托
func post(/*any other variables*/ successCompletion: (JSON) -> (), errorCompletion: (NSError) ->()){
/* do whatever you need to*/
/*if succeeds*/
successCompletion("")
/*if fails*/
errorCompletion(error)
}
// example using closures
post({ (data) in
/* handle Success*/
}) { (error) in
/* handle error */
}
// example using functions
post(handleData, errorCompletion: handleError)
func handleData(data: JSON) {
}
func handleError(error: NSError) {
}
这也将为您提供用一个函数处理所有错误的选项。
此外,在返回JSON之前,最好将其解析为所需的对象。这样可以保持ViewControllers的干净,并明确解析的发生位置。