简体   繁体   中英

iOS swift background threads blocking the main thread

I am trying to create a background thread by using grand central dispatch and do some stuff here from alertcontroller action but it blocks the main thread. Here is my full class code, please check because I could not find what am I doing wrong? :

class DisplayMangaViewController: UIViewController, UIScrollViewDelegate, UIPageViewControllerDataSource, UIToolbarDelegate {
    //variables set there
    @IBAction func actionBarButtonItem(sender: AnyObject) {
        if let popoverController = controller!.popoverPresentationController {
            popoverController.barButtonItem = sender as! UIBarButtonItem
        }
        self.presentViewController(controller!, animated: true, completion: nil)
    }
    @IBAction func actionTabBarItem(sender: AnyObject) {
        ActionSheetStringPicker.showPickerWithTitle("Sayfaya Git", rows: self.pages, initialSelection: self.pageCounter, doneBlock: {
            picker, value, index in
            var pageIndex = index.integerValue - 1
            let aimedController = self.getItemController(pageIndex)!
            let startingViewControllers: NSArray = [aimedController]
            self.pageViewController!.setViewControllers(startingViewControllers as [AnyObject], direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)
            return
            }, cancelBlock: { ActionStringCancelBlock in return }, origin: sender)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        self.navigationItem.title = self.currentChapter
        setActionSheet()
    }

    private func createPageViewController() {

        let pageController = self.storyboard!.instantiateViewControllerWithIdentifier("PageViewController") as! UIPageViewController
        pageController.dataSource = self
        if self.currentToolBar.hidden {
            pageController.view.frame = CGRect(x: 0.0, y: 0.0, width: self.view.frame.size.width, height: (self.view.frame.size.height))
        }else {
            pageController.view.frame = CGRect(x: 0.0, y: 0.0, width: self.view.frame.size.width, height: (self.view.frame.size.height - 40.0))
        }
        if imageUrlsList.count > 0 {
            let firstController = getItemController(0)!
            let startingViewControllers: NSArray = [firstController]
            pageController.setViewControllers(startingViewControllers as [AnyObject], direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)
        }
        pageViewController = pageController
        addChildViewController(pageViewController!)
        self.view.addSubview(pageViewController!.view)
        pageViewController!.didMoveToParentViewController(self)
    }

    func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {

        let itemController = viewController as! PageItemViewController

        if itemController.itemIndex > 0 {
            return getItemController(itemController.itemIndex-1)
        }

        return nil
    }

    func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {

        let itemController = viewController as! PageItemViewController

        if itemController.itemIndex+1 < pages.count {
            return getItemController(itemController.itemIndex+1)
        }

        return nil
    }

    private func getItemController(itemIndex: Int) -> PageItemViewController? {
        pageCounter = itemIndex
        setLabel()
        if itemIndex < pages.count {
            let pageItemController = self.storyboard!.instantiateViewControllerWithIdentifier("PageItemViewController") as! PageItemViewController
            pageItemController.itemIndex = itemIndex
            pageItemController.totalItem = self.pages.count
            pageItemController.strUrl1 = self.imageUrlsList[pageCounter]
            pageItemController.strUrl2 = self.imageUrlsList[(pageCounter + 1)]
            return pageItemController
        }

        return nil
    }

    func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
        return pages.count
    }

    func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
        return 0
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()

    }

    override func viewWillAppear(animated: Bool) {
        self.view.addGestureRecognizer(tapGesture)
        self.tabBarController?.tabBar.hidden = true
    }

    override func viewDidAppear(animated: Bool) {

        if !userDefaults.boolForKey("kalınanMangaBool"){
            var viewToDisplay : UIWindow = UIApplication.sharedApplication().windows.last as! UIWindow
            self.mbProHud = MBProgressHUD.showHUDAddedTo(self.view, animated: true)
            self.mbProHud.dimBackground = true
            self.mbProHud.mode = MBProgressHUDMode.Indeterminate
            self.mbProHud.labelText = "Yükleniyor!.."
            UrlAndNameList.getPageUrlsOfChapter(currentChapter, ofManga: self.obtainedManga.name)

        }else{
            var encodedObject : NSData? = userDefaults.objectForKey("kalınanManga") as? NSData
            self.obtainedManga = NSKeyedUnarchiver.unarchiveObjectWithData(encodedObject!) as! Manga
            currentChapter = obtainedManga.chapterNu
            pageCounter = obtainedManga.pageNu
            self.pageCounter = 0
            pages = userDefaults.objectForKey("kalınanMangaPages") as! [String]
            self.imageUrlsList = userDefaults.objectForKey("kalınanMangaImageUrlsList") as! [String]
            self.navigationItem.title = obtainedManga.name + " " + currentChapter
            setLabel()
            createPageViewController()
        }

    }

    func setActionSheet(){

        controller = UIAlertController(title: "Seçenekler", message: "Ne yapmak İstersiniz?", preferredStyle: .ActionSheet)
        let actionDownload = UIAlertAction(title: "Bölümü İndir", style: UIAlertActionStyle.Default, handler: {(paramAction : UIAlertAction!) in
            var mbProHud : MBProgressHUD!
            var viewToDisplay : UIWindow = UIApplication.sharedApplication().windows.last as! UIWindow
            mbProHud = MBProgressHUD.showHUDAddedTo(viewToDisplay, animated: true)
            mbProHud.customView = UIImageView(image: UIImage(named: "37x-Checkmark.png"))
            mbProHud.mode = MBProgressHUDMode.CustomView
            mbProHud.dimBackground = true
            mbProHud.labelText = "İndirme Başlatıldı!"
            mbProHud.hide(true, afterDelay: 2)
            let qos = Int(QOS_CLASS_BACKGROUND.value)
            dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), {
                var selectedChapters : [String] = [self.currentChapter]
                var mangaNameArray = [self.obtainedManga.name]
                let notification = NSNotification(name: "downloadListNotification", object: self, userInfo: ["downloadList" : selectedChapters, "mangaName" : mangaNameArray])
                self.downloadChapter(notification)
                //NSNotificationCenter.defaultCenter().postNotification(notification)
            })

        })
        controller!.addAction(actionDownload)
        let actionShare = UIAlertAction(title: "Paylaş", style: UIAlertActionStyle.Default, handler: {(paramAction : UIAlertAction!) in
            var itemsToShare : [String] = [self.obtainedManga.name, self.obtainedManga.chapterNu, self.pages[self.pageCounter], "Süperr bi mangadan süperr bi sahne! TürkİşManga Aracılığıyla..."]
            let activityViewController = UIActivityViewController(activityItems: itemsToShare, applicationActivities: nil)
            self.presentViewController(activityViewController, animated: true, completion: nil)
        })
        controller!.addAction(actionShare)
        let actionSave = UIAlertAction(title: "Favorilere Ekle", style: UIAlertActionStyle.Default, handler: {(paramAction : UIAlertAction!) in
            self.addToFav(self.obtainedManga)
        })
        controller!.addAction(actionSave)
        let actionSettings = UIAlertAction(title: "Ayarlar vs.", style: UIAlertActionStyle.Default, handler: {(paramAction : UIAlertAction!) in
            self.performSegueWithIdentifier("ayarlar", sender: self)
        })
        controller!.addAction(actionSettings)
        let actionAboutUs = UIAlertAction(title: "Biz Kimiz?", style: UIAlertActionStyle.Default, handler: {(paramAction : UIAlertAction!) in
            self.performSegueWithIdentifier("hakkımızda", sender: self)
        })
        controller!.addAction(actionAboutUs)
        let actionCancel = UIAlertAction(title: "İptal", style: UIAlertActionStyle.Default, handler: nil)
        controller!.addAction(actionCancel)
    }

    func downloadChapter(notification : NSNotification){

        let userDefaults = NSUserDefaults.standardUserDefaults()
        var filePath : String = "Turk Isi Manga"
        let qos = Int(QOS_CLASS_USER_INITIATED.value)
        //dispatch_async(dispatch_get_global_queue( qos, 0), {
            var chaptersToDownload : [String] = notification.userInfo!["downloadList"] as! [String]
            var mangaName : String = (notification.userInfo!["mangaName"] as! [String]).first!
            var downList : [DownloadingManga] = []
            for chapter in chaptersToDownload{
                var down = DownloadingManga()
                down.mangaName = mangaName
                down.chapterName = chapter
                down.progress = 0
                down.title = down.mangaName + " " + down.chapterName + "İndirilen \(down.progress)"
                downList.append(down)
            }
            var encodedObject : NSData? = userDefaults.objectForKey("currentDownloadingList") as? NSData
            var unarchivedlist = NSKeyedUnarchiver.unarchiveObjectWithData(encodedObject!) as! [DownloadingManga]
            unarchivedlist.extend(downList)
            var archivedList = NSKeyedArchiver.archivedDataWithRootObject(unarchivedlist)
            userDefaults.setObject(archivedList, forKey: "currentDownloadingList")
            userDefaults.synchronize()

            let path = NSURL.documentsFolder().URLByAppendingPathComponent("\(filePath)/covers/\(mangaName)")
            let manager = NSFileManager.defaultManager()
            if (manager.fileExistsAtPath("\(path)")) {
                println("cover image of manga named \(mangaName) has already saved")
            }else{
                var query = PFQuery(className:"MangaNamesAndUrls")
                query.whereKey("mangaName", equalTo: mangaName)
                query.findObjectsInBackgroundWithBlock {
                    (objects: [AnyObject]?, error: NSError?) -> Void in

                    if error == nil {
                        if let objects = objects as? [PFObject] {
                            var url = objects[0].objectForKey("coverImageUrl") as! String
                            if Reachability.isConnectedToNetwork(){
                                let url : NSURL? = NSURL(string: url)
                                let urlRequest = NSMutableURLRequest(URL: url!, cachePolicy: NSURLRequestCachePolicy.ReturnCacheDataElseLoad, timeoutInterval: 15.0)
                                var response: AutoreleasingUnsafeMutablePointer<NSURLResponse?> = nil
                                var error : NSErrorPointer = nil;
                                if let data = (NSURLConnection.sendSynchronousRequest(urlRequest, returningResponse: response, error: error) as NSData?) {
                                    if let img = UIImage(data: data){
                                        let filePath = NSURL.documentsFolder().URLByAppendingPathComponent("\(filePath)/covers/\(mangaName)")
                                        println("file path that cover image will be saved is \(filePath)")
                                        var fileError : NSErrorPointer = nil
                                        filePath.setResourceValue(NSNumber(bool: true), forKey: NSURLIsExcludedFromBackupKey, error: fileError)
                                        UIImagePNGRepresentation(img).writeToURL(filePath, atomically: true)
                                    }
                                }else{
                                }
                            }
                        }

                    } else {
                        println("Error: \(error!) \(error!.userInfo!)")
                    }
                }
            }

            for chapter in chaptersToDownload{
                self.createDirectory(mangaName, chapter: chapter)
                var pagesList : [String] = []
                var succeed = false
                var query = PFQuery(className: "MangaItems")
                query.whereKey("mangaName", equalTo: mangaName)
                query.whereKey("chapterName", equalTo: chapter)
                query.findObjectsInBackgroundWithBlock {
                    (objects: [AnyObject]?, error: NSError?) -> Void in
                    println(objects?.count)
                    if error == nil {
                        if let inobjects = objects as? [PFObject] {
                            for object in inobjects {
                                if let pages = object.objectForKey("pages") as? [String] {
                                    pagesList.extend(pages)
                                    println(pages)
                                }
                            }
                        }
                    }else{
                        println("Error: \(error!) \(error!.userInfo!)")
                    }
                    var counter = 0
                    for var i = 0 ; i < pagesList.count ; i++ {
                        println("attempted to download page number : \(i)")
                        if Reachability.isConnectedToNetwork(){
                            let url : NSURL? = NSURL(string: pagesList[i])
                            let urlRequest = NSMutableURLRequest(URL: url!, cachePolicy: NSURLRequestCachePolicy.ReturnCacheDataElseLoad, timeoutInterval: 15.0)
                            var response: AutoreleasingUnsafeMutablePointer<NSURLResponse?> = nil
                            var error : NSErrorPointer = nil;
                            if let data = (NSURLConnection.sendSynchronousRequest(urlRequest, returningResponse: response, error: error) as NSData?) {
                                if let img = UIImage(data: data){
                                    let filePath = NSURL.documentsFolder().URLByAppendingPathComponent("\(filePath)/\(mangaName)/\(chapter)/\(i)")
                                    UIImagePNGRepresentation(img).writeToURL(filePath, atomically: true)
                                    counter++
                                    println("downloaded page number : \(i) and counter is : \(counter)")
                                    //dispatch_async(dispatch_get_main_queue(), {
                                        var downloaded = DownloadingManga()
                                        downloaded.mangaName = mangaName
                                        downloaded.chapterName = chapter
                                        downloaded.progress = ((i + 1) * 100)/pagesList.count
                                        downloaded.title = downloaded.mangaName + " " + downloaded.chapterName + " İndirilen : \(downloaded.progress)"
                                        let notification = NSNotification(name: "downloadedNotification", object: DownloadHandler.self, userInfo: ["downloading" : downloaded])
                                        var downque = DownloadingQueueViewController()
                                        NSNotificationCenter.defaultCenter().postNotification(notification)
                                    //})
                                }
                            }else{
                            }
                        }else{
                            if userDefaults.boolForKey("isApplicationActive"){
                                //bağlantı koptu bildirilmeli ve indirmeler durdurulmalı
                                println("bağlantı koptu kontrol et")
                            }
                            //dispatch_async(dispatch_get_main_queue(), {
                                let notification = UILocalNotification()
                                notification.fireDate = NSDate(timeIntervalSinceNow: 1)
                                notification.timeZone = NSCalendar.currentCalendar().timeZone
                                notification.alertBody = "Bölüm İndirilirken Bağlantı Koptu. İndirme Tamamlanamadı! :("
                                notification.hasAction = true
                                notification.alertAction = "View"
                                notification.userInfo = [:]
                                UIApplication.sharedApplication().scheduleLocalNotification(notification)
                            //})

                            break
                        }
                        if counter == pagesList.count {
                            succeed = true
                            println("succeed = \(succeed)")
                        }else{
                            succeed = false
                            println("succeed = \(succeed)")
                        }
                    }
                    if succeed {

                        if !self.isContaining(mangaName){
                            var encodedObject : NSData? = userDefaults.objectForKey("indirilenlerList") as? NSData
                            var tempIndList = NSKeyedUnarchiver.unarchiveObjectWithData(encodedObject!) as! [Manga]
                            var manga = Manga()
                            manga.name = mangaName
                            tempIndList.insert(manga, atIndex: 0)
                            var updatedTempIndList = NSKeyedArchiver.archivedDataWithRootObject(tempIndList)
                            userDefaults.setObject(updatedTempIndList, forKey: "indirilenlerList")
                        }


                        if userDefaults.boolForKey("isApplicationActive"){
                        }else{
                            //dispatch_async(dispatch_get_main_queue(), {
                                let notification = UILocalNotification()
                                notification.fireDate = NSDate(timeIntervalSinceNow: 1)
                                notification.timeZone = NSCalendar.currentCalendar().timeZone
                                notification.alertBody = "Bölüm İndirildi :) : Belgeler/\(mangaName)/\(chapter)/! Tıkla, Hemen Oku!"
                                notification.hasAction = true
                                notification.alertAction = "View"
                                notification.applicationIconBadgeNumber++
                                notification.userInfo = [
                                    "MangaName" : mangaName,
                                    "Chapter" : chapter
                                ]
                                UIApplication.sharedApplication().scheduleLocalNotification(notification)
                            //})
                        }
                    }

                }

            }
            println("async download method has came to the end!!!!!!!!!")
        //})
    }

    func createDirectory(mangaName : String, chapter : String){
        var filePath1 : String = "Turk Isi Manga"
        // before create a new file path check it whether exist
        let filePath = NSURL.documentsFolder().URLByAppendingPathComponent("\(filePath1)/\(mangaName)/\(chapter)")
        var error:NSError?
        let fileManager = NSFileManager()
        if fileManager.createDirectoryAtURL(filePath, withIntermediateDirectories: true, attributes: nil, error: nil){
            println("succeed to create the directory")
            var fileError : NSErrorPointer = nil
            filePath.setResourceValue(NSNumber(bool: true), forKey: NSURLIsExcludedFromBackupKey, error: fileError)
        }else {
            println("failed to create the directory")
        }
    }

    func isContaining(name : String) -> Bool {
        let userDefaults = NSUserDefaults.standardUserDefaults()
        var encodedObject : NSData? = userDefaults.objectForKey("indirilenlerList") as? NSData
        var tempIndList = NSKeyedUnarchiver.unarchiveObjectWithData(encodedObject!) as! [Manga]
        var returnValue = false
        for manga in tempIndList {
            if manga.name == name {
                returnValue = true
            }
        }
        return returnValue
    }

it is a bit of long code but any help would be quite appreciated, thanks... EDIT: the problem occurs only when I tap the ui alert controller action button "Bölümü İndir". Because all the background stuff called from that action. All other codes can be considered as context. I think it has something to do with download chapter method...

The problem is in dowloadChapter. My guess as to what is happening here is that query.findObjectsInBackgroundWithBlock calls the completion block on the main thread and then you are doing nsurlconnection calls synchronous meaning they are blocking until complete.

So, if you want it to be asynchronous, you need to dispatch to the background thread again inside the query completion. Also, you may want to consider using NSURLSession instead of NSURLConnection.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM