多线程在tableview重用中的使用以及缓存的使用

//
//  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)
    }
}

标签: swift, iOS多线程

添加新评论