//
// ViewController.swift
// 多线程使用
//
// Created by admin on 16/2/23.
// Copyright © 2016年 jin. All rights reserved.
//
import UIKit
class ViewController: UITableViewController {
// 数据模型数组
var apps:[App] = App.instance()
// cell重用标识
let cellIdentifier:String = "appCell"
// 下载的图片的缓存
var imageCache = NSMutableDictionary()
// 图片是否已经下载过的标识
var operationCache = NSMutableDictionary()
// 全局队列
let queue = NSOperationQueue()
override func viewDidLoad() {
/**
问题1: 如果网络比较慢,会比较卡
解决办法:用异步下载
问题2:图片没有frame,所有cell初始化的时候,给imageView的frame是0。 异步下载完成以后,不显示
解决办法:使用占位图(如果占位图比较大,下载的图片比较小。自定义cell可以解决)
问题3:如果图片下载速度不一致,同时用户快速滚动的时候,会因为cell的重用导致图片混乱
解决办法:MVC,使用模型保持下载的图像。 再次刷新表格
问题4:在用户快读滚动的时候,会重复添下载加操作到队列
解决办法:建立一个下载操作的缓冲池,首先检查”缓冲池“里是否有当前图片下载操作,有。 就不创建操作了。保证一个图片只对应一个下载操作
问题5:将图像保存到模型里优缺点
优点:不用重复下载,利用MVC刷新表格,不会造成数据混乱.加载速度比较快
缺点:内存:所有下载好的图像,都会记录在模型里。如果数据比较多(2000)
造成内存警告
--** 图像跟模型耦合性太强。导致清理内存非常困难
解决办法: 模型跟图像分开。在控制器里做缓存
问题6:下载操作缓冲池,会越来越大,想办法清理
*/
super.viewDidLoad()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return apps.count
}
// cell里面的imageView子控件也是懒加载。
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier(self.cellIdentifier)
if cell == nil
{
cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: self.cellIdentifier)
}
cell?.textLabel?.text = apps[indexPath.row].name
cell?.detailTextLabel?.text = apps[indexPath.row].download
let image = self.imageCache.valueForKey(apps[indexPath.row].icon)
// 是否有头像
if image == nil//内存中没有
{
// 从沙盒缓存中取出数据
let image:UIImage! = UIImage(contentsOfFile: self.getImageCachePath(apps[indexPath.row].icon))
if image == nil// 沙盒缓存中没有数据,下载头像,并设置占位图
{
cell?.imageView?.image = UIImage(imageLiteral: "user_default")
self.downloadImage(indexPath)
}
else// 把沙盒缓存的图片设置成当前图片
{
// print("从磁盘读取")
cell?.imageView?.image = image
}
}
else
{
cell?.imageView?.image = self.imageCache.valueForKey(apps[indexPath.row].icon) as! UIImage
}
// print(cell)
return cell!
}
// 下载方法
func downloadImage(indexPath:NSIndexPath)
{
// 如果已经开启啦线程下载当前图片,则不再开启进程
if self.operationCache.valueForKey(apps[indexPath.row].icon) == nil
{
// 记录当前图片已经开启过下载进程
self.operationCache.setValue(true, forKey: self.apps[indexPath.row].icon)
queue.addOperationWithBlock({ [weak self] () -> Void in
// print("图片下载")
NSThread.sleepForTimeInterval(2)
// print(self!.apps[indexPath.row].icon)
let data = NSData(contentsOfURL: NSURL(string: self!.apps[indexPath.row].icon)!)
// 当url没有请求到值的时候,需要设置判断下,不然会崩溃
if data != nil
{
// 下载的数据缓存到沙盒
data?.writeToFile((self?.getImageCachePath(self!.apps[indexPath.row].icon))!, atomically: true)
self!.imageCache.setValue(UIImage(data: data!), forKey: self!.apps[indexPath.row].icon)
}
NSOperationQueue.mainQueue().addOperationWithBlock({ [weak self] () -> Void in
self!.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Fade)
})
})
}
}
// 内存警告
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// 清空图片缓存
self.imageCache.removeAllObjects()
// 清空图片下载标识,不然清空后不会再开始下载
self.operationCache.removeAllObjects()
// 清空当前队列中的操作
self.queue.cancelAllOperations()
}
// 根据url获得缓存文件的路径
func getImageCachePath(url:String)->String
{
let cache = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.CachesDirectory, NSSearchPathDomainMask.UserDomainMask, true).last
return "\(cache!)/\(url.md5)"
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 100
}
}
// 把url转换成字串,要在桥接头中加入 #import <CommonCrypto/CommonCrypto.h>
extension String {
var md5: String! {
let str = self.cStringUsingEncoding(NSUTF8StringEncoding)
let strLen = CC_LONG(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
let digestLen = Int(CC_MD5_DIGEST_LENGTH)
let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen)
CC_MD5(str!, strLen, result)
let hash = NSMutableString()
for i in 0..<digestLen {
hash.appendFormat("%02x", result[i])
}
result.dealloc(digestLen)
return String(format: hash as String)
}
}