分类 ios多线程 下的文章

自定义NSOperation类

//
//  ViewController.swift
//  缓存类使用
//
//  Created by admin on 16/2/24.
//  Copyright © 2016年 jin. All rights reserved.
//

import UIKit

class ViewController: UIViewController{

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        let queue = NSOperationQueue()
        let operation = MyOperation()
        queue.addOperation(operation)
        // 线程执行完毕调用的方法
        operation.completionBlock = {
            ()->(Void) in
            print("线程执行完毕,输出信息")
        }
    }
}

class MyOperation: NSOperation {
    // 注意点:需要自己手动添加自动释放池
    // 自定义的操作,可以取消
    override func main() {
        super.main()
        func autorelasepool(code:()->(Void)){
            code()
        }
        autorelasepool { () -> (Void) in
            // 如果操作在队列里,还没有调度,就直接return
            if self.cancelled == true
            {
                return
            }
            NSThread.sleepForTimeInterval(2)
            print("执行结束")
        }
    }
}

多线程在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)
    }
}

NSOperation高级属性及总结

//
//  ViewController.swift
//  NSOperation使用
//
//  Created by admin on 16/2/22.
//  Copyright © 2016年 jin. All rights reserved.
//

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        self.operationB()
    }
    /**
     GCD --> iOS 4.0
     - 将任务(block)添加到队列(串行/并发(全局)),指定 执行任务的方法(同步(阻塞)/异步)
     - 拿到 dispatch_get_main_queue()。 线程间通信
     - NSOperation无法做到,一次性执行,延迟执行,调度组(op相对复杂)


     NSOperation ----> iOS 2.0 (后来苹果改造了NSOperation的底层)
     - 将操作(异步执行)添加到队列(并发/全局)
     - [NSOperationQueue mainQueue] 主队列。 任务添加到主队列, 就会在主线程执行
     - 提供了一些GCD不好实现的,”最大并发数“
     - 暂停/继续 --- 挂起
     - 取消所有的任务
     - 依赖关系
     */

     /**
     小结一下:
     只要是NSOperation的子类 就能添加到操作队列
     - 一旦操作添加到队列, 就会自动异步执行
     - 如果没有添加到队列, 而是使用start方法,会在当前线程执行操作
     - 如果要做线程间通信,可以使用[NSOperationQueue mainQueue]拿到主队列,往主队列添加操作(更新UI)
     */
    /**负责调度所有的操作*/
    let queue = NSOperationQueue()
    func operationB()
    {
        let operationA = NSBlockOperation { () -> Void in
            print(NSThread.currentThread())
            print("下载")
        }
        let operationB = NSBlockOperation { () -> Void in
            print(NSThread.currentThread())
            print("解压")
        }
        let operationC = NSBlockOperation { () -> Void in
            print(NSThread.currentThread())
            print("更新UI")
        }
        // 指定任务之间的依赖关系 -- 依赖关系可以跨队列(可以在子线程下载完,到主线程更新UI)
        operationB.addDependency(operationA)
        operationC.addDependency(operationB)
        // 注意点:一定不要出现循环依赖关系
        // operationA.addDependency(operationC)
        NSOperationQueue.mainQueue().addOperation(operationC)
        queue.addOperations([operationA,operationB], waitUntilFinished: true)

    }

    // MARK: 取消队列里的所有操作
    // “取消操作,并不会影响队列的挂起状态”
    @IBAction func clearQueue(sender: AnyObject) {
        self.queue.cancelAllOperations()// 取消队列的所有操作,会把任务从队列里面全部删除
        self.queue.suspended = false // (只要是取消了队列的操作,我们就把队列处于启动状态。以便于队列的继续)
    }
    // MARK: 暂停/继续 (对队列的暂停和继续)
    @IBAction func suspend(sender: AnyObject) {
        // 判断操作的数量,当前队列里面是不是有操作
        if self.queue.operationCount <= 0
        {
            return
        }
        // 队列的挂起以后,队列里面的操作还在
        self.queue.suspended = !self.queue.suspended
    }
    // 最大并发数
    func operationA()
    {
        // 设置最大的并发数是2 (最大并发数,不是线程的数量。 而是同时执行的操作的数量)
        queue.maxConcurrentOperationCount = 2
        for var i = 0;i < 10;i++
        {
            let operation = NSBlockOperation(block: { () -> Void in
                NSThread.sleepForTimeInterval(2)
                print(NSThread.currentThread())
                print(i)
            })
            queue.addOperation(operation)
        }
    }
}

NSOperation的基本使用

//
//  ViewController.swift
//  NSOperation基本使用
//
//  Created by zhang on 16/2/22.
//  Copyright © 2016年 jin. All rights reserved.
//

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        self.operationD()
    }
    // 线程间通信
    func operationD()
    {
        let queue = NSOperationQueue()
        queue.addOperationWithBlock { () -> Void in
            print(NSThread.currentThread())//<NSThread: 0x7fe1a0703c50>{number = 2, name = (null)}
            // 主队列(跟GCD里的主队列一样),到主队列更新ui
            NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
                print(NSThread.currentThread())//<NSThread: 0x7fe1a0702d60>{number = 1, name = main}
            })
        }
    }
    // 往队列中直接添加block
    func operationC()
    {
        let queue = NSOperationQueue()
        // 不创建操作,直接往队列中直接添加block
        queue.addOperationWithBlock { () -> Void in
            print(NSThread.currentThread())
        }
    }
    // 往操作中添加block
    func operationB()
    {
        let queue = NSOperationQueue()
        let operation = NSBlockOperation { () -> Void in
            print(NSThread.currentThread())//<NSThread: 0x7fc53160b880>{number = 2, name = (null)}
            print("第一次执行")
        }
        // 往操作中添加block
        operation.addExecutionBlock { () -> Void in
            print(NSThread.currentThread())//<NSThread: 0x7fc5315492a0>{number = 3, name = (null)}
            print("第二次执行")
        }
        queue.addOperation(operation)
    }
    // 基本使用
    func operationA()
    {
        // 队列 (GCD里面的并发(全局)队列使用最多。所以NSOperation技术直接把GCD里面的并发队列封装起来)
        // NSOperationQueue队列,本质就是GCD里面的并发队列
        // 操作就是GCD里面异步执行的任务
        // 创建队列
        let queue = NSOperationQueue()
        // 创建操作
        let operation = NSBlockOperation { () -> Void in
            print(NSThread.currentThread())
        }
        // 添加操作到队列
        queue.addOperation(operation)
        // 使用start方法就是GCD中的同步执行
//        operation.start()
    }
}

GCD的基本使用以及常用的方法

//
//  ViewController.swift
//  GCD
//
//  Created by admin on 16/2/22.
//  Copyright © 2016年 jin. All rights reserved.
//

import UIKit

class ViewController: UIViewController {
    /**
     概念
     任务:闭包
     队列:存放任务,先进先出的原则
     串行队列:任务一个一个顺序执行
     并发队列:同时取出多个任务

     同步sync:不会开辟新线程
     异步async:会开新线程

     串行队列同步执行:不开线程,在原来的线程里面顺序执行
     串行队列异步执行:开一条线程,在新线程里面顺序执行
     并发队列同步执行:不开线程,在原来线程顺序执行
     并发队列异步执行:开多条线程,在开辟的新线程里面执行

     总结
     1.是否sync决定是否开辟新任务
     2.开多少线程由队列决定,串行最多开一个,并发可以开多个,有GCD底层决定
     */
    @IBOutlet weak var imageView: UIImageView!
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        self.gcdL()
    }
    // 定义once变量
    static var once = dispatch_once_t()
    func gcdL()
    {
        print(ViewController.once)// 第一次打印 0
        dispatch_once(&ViewController.once) { () -> Void in
            print("只执行一次")
        }
        print(ViewController.once)// 打印 -1
        print("ok")
    }
    // 组队列
    func gcdK()
    {
        let queueGroup = dispatch_group_create()
        let queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT)
        dispatch_group_async(queueGroup, queue) { () -> Void in
            print(NSThread.currentThread())
            print("下载a")
        }
        dispatch_group_async(queueGroup, queue) { () -> Void in
            print(NSThread.currentThread())
            print("下载b")
        }
        dispatch_group_async(queueGroup, queue) { () -> Void in
            print(NSThread.currentThread())
            print("下载c")
        }
        // 队列里面的所有任务执行完毕之后执行,可以跨队列通信
        dispatch_group_notify(queueGroup, dispatch_get_main_queue()) { () -> Void in
            print(NSThread.currentThread())
            print("下载完毕")
        }
    }
    // 延迟执行
    func gcdJ()
    {
        let time = dispatch_time(DISPATCH_TIME_NOW, (Int64)(2 * NSEC_PER_SEC))
        /**
       第一个参数:表示从现在开始经过多少纳秒之后
       第二个参数:在哪个队列中执行
        */
        dispatch_after(time, dispatch_get_main_queue()) { () -> Void in
            print(NSThread.currentThread())
        }
    }
    // GCD线程间通信
    func gcdI()
    {
        dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
            let data = NSData(contentsOfURL: NSURL(string: "http://e.hiphotos.baidu.com/image/pic/item/29381f30e924b8995d7368d66a061d950b7bf695.jpg")!)
            let image = UIImage(data: data!)
            print(NSThread.currentThread())
            dispatch_sync(dispatch_get_main_queue(), { () -> Void in
                print(NSThread.currentThread())
                self.imageView.image = image
            })
        }
    }
    func gcdH()
    {
        let queue = dispatch_get_global_queue(0, 0)
        for var i = 0;i < 10;i++
        {
            print("主队列")
            dispatch_async(queue) { () -> Void in
                print(NSThread.currentThread())
            }
        }
    }
    /**
     全局队列跟并发队列的区别
     1. 全局队列没有名称 并发队列有名称
     2. 全局队列,是供所有的应用程序共享。
     3. 在MRC开发,并发队列,创建完了,需要释放。 全局队列不需要我们管理
     */
    func gcdG()
    {
        /**
         参数:第一个参数,一般 写 0(可以适配 iOS 7 & 8)
         iOS 7
         DISPATCH_QUEUE_PRIORITY_HIGH 2  高优先级
         DISPATCH_QUEUE_PRIORITY_DEFAULT 0  默认优先级
         DISPATCH_QUEUE_PRIORITY_LOW (-2) 低优先级
         DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后台优先级

         iOS 8
         QOS_CLASS_DEFAULT  0

         第二个参数:保留参数 0
         */
        let queue = dispatch_get_global_queue(0, 0)
        for var i = 0;i < 10;i++
        {
            print("主队列")
            dispatch_sync(queue) { () -> Void in
                print(NSThread.currentThread())
            }
        }
    }
    // 主队列异步
    func gcdF()
    {
        let queue = dispatch_get_main_queue()
        for var i = 0;i < 10;i++
        {
            print("主队列")
            dispatch_async(queue) { () -> Void in
                print(NSThread.currentThread())
            }
        }
    }
    /**
     主队列:专门负责在主线程上调度任务,不会在子线程调度任务,在主队列不允许开新线程.
     同步执行:要马上执行
     结果:死锁
     */
    func gcdE()
    {
        // 1. 获得主队列-> 程序启动,--> 至少有一个主线程-> 一开始就会创建主队列
        let queue = dispatch_get_main_queue()
        for var i = 0;i < 10;i++
        {
            print("主队列")
            // 同步:把任务放到主队列里,但需是马上执行
            dispatch_sync(queue) { () -> Void in
                print(NSThread.currentThread())
            }
        }
    }
    /**
     并发队列:可以同时执行多个任务
     异步执行:肯定会开新线程,在新线程执行
     结果:会开很多个线程,同时执行
     */
    func gcdD()
    {
        let queue = dispatch_queue_create("queue",DISPATCH_QUEUE_CONCURRENT)
        for var i = 0;i < 10;i++
        {
            dispatch_async(queue) { () -> Void in
                print(NSThread.currentThread())
            }
        }
    }
    /**
     并发队列:可以同时执行多个任务
     同步任务:不会开辟新线程,是在当前线程执行
     结果:不开新线程,顺序一个一个执行。
     */
    func gcdC()
    {
        let queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT)
        for var i = 0;i < 10;i++
        {
            dispatch_sync(queue) { () -> Void in
                print(NSThread.currentThread())
            }
        }
    }
    /**
     串行队列:一个一个执行
     异步执行:肯定会开新线程,在新线程执行
     结果:只会开一个线程,而且所有任务都在这个新的线程里面执行
     */
    func gcdB()
    {
        let queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL)
        for var i = 0;i < 10;i++
        {
            dispatch_async(queue) { () -> Void in
                print(NSThread.currentThread())
            }
        }
    }
    /**
     串行队列:顺序,一个一个执行
     同步任务:不会开辟新线程,是在当前线程执行
     结果:不开新线程,在当前线程顺序执行

     dispatch : 调度,GCD里面函数,都是以dispatch开头的
     */
    func gcdA()
    {
        let queue = dispatch_queue_create("queue", nil)
        dispatch_sync(queue) { () -> Void in
            print(NSThread.currentThread())
        }
    }
}