非同步事件,就是說這一個代碼或者代碼塊,並不會阻塞程式的運行,程式會立即執行下一條語句,而這條語句,會在相應的方法調用結束之後,執行它自身的回呼函數發送一些訊號,來表明這個非同步事件完成。就像你約會提前1小時到見面地點,先去買點東西踩點什麼的(……),等GF/BF到了之後簡訊通知你,你就立即回來。而不是一直在原地等到對方過來(……)
最早使用非同步開發,是在使用JavaScript來開發Web前端的時候,XMLHttpRequest或者jQuery的$.ajax中,都會用到回呼函數,來指明成功或者失敗之後的處理方法。當對應的網路請求得到響應之後,會調用響應的成功或者失敗的回呼函數,然後執行裡面相應的方法,這大大提升了前端的效率,不會在網路請求時整個頁面卡住,而且也不需要一次次輪詢看是否有響應,簡化了代碼的複雜性。
這點Node.js中更為常見,不過也更能表現中濫用非同步事件編程的問題。新人使用Node.js總會發現基本任何東西都是非同步,資料庫是非同步,IO檔案操作是非同步,Session讀寫是非同步,甚至獲得Request對象都是非同步。這就導致很多人一直在嵌套回呼函數,導致了著名的Callback Hell
在Node.js中,解決方案有非常成熟的Async,更有號稱能用同步思維寫非同步Promises,都是非常棒的解決方案。前者的本質就是一個自動產生回調的封裝……,後者則是一個真正意義上的全新的解決方案。
而在Swift和iOS開發中,也有必須用到非同步事件編程的地方。除了View層的簡單UI和Controller之間的互動以外(這部分一般不需要手寫代碼處理非同步互動或者順序),其他很多地方需要這些知識。例如網路請求的非同步呼叫,請求隊列的處理(雖然可以一個網路請求就是一個線程,但這種方法的效率不高,而且容易導致線程間衝突),SQLite資料庫大量資料的讀寫,本機存放區的大量資料讀寫,複雜UI的渲染順序等等……這些都是需要進行非同步編程的,而不能讓同步的代碼阻塞住整個應用或者UI。
舉個例子,這裡是一個UI順序載入的動畫……
func schoolLifeClicked()
{
var mydrawerController = self.mm_drawerController //一個用TableView實現的應用側邊欄抽屜View
let schoolLifeViewController:SchoolLifeViewController = SchoolLifeViewController(nibName: "SchoolLifeViewController", bundle: nil)
let navSchoolLifeViewController = CommonNavViewController(rootViewController: schoolLifeViewController)
self.mm_drawerController.toggleDrawerSide(MMDrawerSide.Left, animated: true, completion:{(complete) in
if complete{//如果成功拉出抽屜
mydrawerController.setCenterViewController(navSchoolLifeViewController, withCloseAnimation: true, completion: nil)//設定主視圖
mydrawerController.closeDrawerAnimated(true, completion:nil)//關閉抽屜
}
})//一個閉包,成功後調用
}
可以看到,Swift很多時候也可以依靠回呼函數,把一個閉包扔進去當參數,然後執行,從而控制這種非同步事件的流程……
但是,這種方法寫起來,就會回到和JS那種匿名函數閉包扔進去當參數一樣,小範圍用還可以,一旦你要進行複雜的流程式控制制,比如一系列非同步事件,AB同時執行,AB同時完成後執行C,C執行完成後執行D……這種控制下寫出來的代碼和JavaScript的callback hell是一樣的,難以維護。
怎麼辦呢?其實自己實現一個文法糖或者函數隊列來執行也不難,不過這裡可以推薦一下GitHub上非常厲害的庫,大家有怎麼使用呢?參考人家的Readme,用文法糖可以很簡單的使用:
Async.userInitiated {
println("start")
}.main {
println("1")
}.background {
println("2")
}.background {
println("2 all the same")
}.main {
println("stop")
}由於非同步事件的特點,所以整個輸出可能就會是
start
1
2
stop
2 all the same不要大驚小怪哦。利用這個就可以從繁重的callback中解放出來,簡單的處理非同步事件的順序,並且獲得很高的效能,這也是網路請求和資料庫訪問等必須要考慮的地方……
ios非同步載入表格式資料,內容不能及時顯示的問題
1,問題描述
我們使用 tableView 的時候,又是表格內容是非同步載入的。比如從網路擷取資料顯示、或是開啟個線程隊列定時重新整理載入表格式資料。
(1)比如我們要載入的資料如下:
[
{
"name": "hangge",
"age": 100,
},
{
"name": "big boss",
"age": 1,
},
{
"name": "batman",
"age": 12,
}
]
(2)使用 NSURLSession 擷取遠端資料後,調用 tableView的reloadData() 方法重新載入資料。
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var ctrlnames:NSArray = []
var tableView:UITableView?
override func viewDidLoad() {
super.viewDidLoad()
//建立表視圖
self.tableView = UITableView(frame: self.view.frame, style:UITableViewStyle.Plain)
self.tableView!.delegate = self
self.tableView!.dataSource = self
//建立一個重用的儲存格
self.tableView!.registerClass(UITableViewCell.self,
forCellReuseIdentifier: "SwiftCell")
self.view.addSubview(self.tableView!)
//建立NSURL對象
let urlString:String="http://www.hangge.com/code/test.php"
let url:NSURL! = NSURL(string:urlString)
//建立請求對象
let request:NSURLRequest = NSURLRequest(URL: url)
let session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request,
completionHandler: {(data, response, error) -> Void in
if error != nil{
print(error?.code)
print(error?.description)
}else{
self.ctrlnames = try! NSJSONSerialization.JSONObjectWithData(data!,
options: NSJSONReadingOptions.MutableContainers) as! NSArray
self.tableView?.reloadData()
}
}) as NSURLSessionTask
//使用resume方法啟動任務
dataTask.resume()
}
//在本例中,只有一個分區
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1;
}
//返回表格行數(也就是返回控制項數)
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.ctrlnames.count
}
//建立各單元顯示內容(建立參數indexPath指定的單元)
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)
-> UITableViewCell
{
//為了提供表格顯示效能,已建立完成的單元需重複使用
let identify:String = "SwiftCell"
//同一形式的儲存格重複使用,在聲明時登入
let cell = tableView.dequeueReusableCellWithIdentifier(identify,
forIndexPath: indexPath) as UITableViewCell
cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
let item = self.ctrlnames[indexPath.row] as! NSDictionary
cell.textLabel?.text = item.objectForKey("name") as? String
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
(3)但會探索資料載入完畢後表格還是空白的,拖動一點點表格式資料就顯示出來了。
原文:Swift - 非同步載入表格式資料,內容不能及時顯示的問題解決 原文:Swift - 非同步載入表格式資料,內容不能及時顯示的問題解決
2,解決辦法
reloadData() 方法需要在主線程中調用,這樣表格式資料就能及時更新。(代碼高亮出為修改的地方)
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var ctrlnames:NSArray = []
var tableView:UITableView?
override func viewDidLoad() {
super.viewDidLoad()
//建立表視圖
self.tableView = UITableView(frame: self.view.frame, style:UITableViewStyle.Plain)
self.tableView!.delegate = self
self.tableView!.dataSource = self
//建立一個重用的儲存格
self.tableView!.registerClass(UITableViewCell.self,
forCellReuseIdentifier: "SwiftCell")
self.view.addSubview(self.tableView!)
//建立NSURL對象
let urlString:String="http://www.hangge.com/code/test.php"
let url:NSURL! = NSURL(string:urlString)
//建立請求對象
let request:NSURLRequest = NSURLRequest(URL: url)
let session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request,
completionHandler: {(data, response, error) -> Void in
if error != nil{
print(error?.code)
print(error?.description)
}else{
self.ctrlnames = try! NSJSONSerialization.JSONObjectWithData(data!,
options: NSJSONReadingOptions.MutableContainers) as! NSArray
dispatch_async(dispatch_get_main_queue(), {
self.tableView?.reloadData()
return
})
}
}) as NSURLSessionTask
//使用resume方法啟動任務
dataTask.resume()
}
//在本例中,只有一個分區
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1;
}
//返回表格行數(也就是返回控制項數)
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.ctrlnames.count
}
//建立各單元顯示內容(建立參數indexPath指定的單元)
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)
-> UITableViewCell
{
//為了提供表格顯示效能,已建立完成的單元需重複使用
let identify:String = "SwiftCell"
//同一形式的儲存格重複使用,在聲明時登入
let cell = tableView.dequeueReusableCellWithIdentifier(identify,
forIndexPath: indexPath) as UITableViewCell
cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
let item = self.ctrlnames[indexPath.row] as! NSDictionary
cell.textLabel?.text = item.objectForKey("name") as? String
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}