当我将数据上传到firebase时,为什么我的应用会冻结?

时间:2016-05-03 17:10:55

标签: ios swift firebase grand-central-dispatch background-thread

更新: ****很少编辑,我发现实际上唯一冻结的是从firebase检索数据,尤其是需要检索的图像。 func initOberserver用于从firebase检索数据。所以这需要每次都在后台完成。但是表格视图必须同时可用?****

我在背景线程中苦苦挣扎。我制作了一个firebase应用程序,但是当我将某些内容上传到firebase并将其恢复回应用程序时,我的应用程序冻结了一段时间。

我在一个单独的打开文件中有两个常量:

let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)

我有一个viewcontroller:ListVC,它使用此函数从firebase检索数据。

func initObservers() {

    //LoadingOverlay.shared.showOverlay(self.view)
    dispatch_async(backgroundQueue, {
        DataService.ds.REF_LISTS.observeEventType(.Value, withBlock: { snapshot in
            print(snapshot.value)

            self.lists = []

            if let snapshots = snapshot.children.allObjects as? [FDataSnapshot] {

                for snap in snapshots {
                    print("SNAP: \(snap)")

                    if let listDict = snap.value as? Dictionary<String, AnyObject> {
                        let key = snap.key
                        let list = List(listKey: key, dictionary: listDict)
                        self.lists.insert(list, atIndex:0)
                    }
                }
            }

            //LoadingOverlay.shared.hideOverlayView()
            self.tableView.reloadData()

        })

    })

}

然后我有一个视图控制器addVC,它使用此函数将数据发布到firebase:

@IBAction func postListItem(sender: AnyObject) {

        if let addTitle = addTitle.text where addTitle != "", let addDescription = addDescription.text where addDescription != "", let addLocation = addLocation.text where addLocation != "" {

            dispatch_async(backgroundQueue, {
                self.postToFirebase(nil)

                dispatch_async(dispatch_get_main_queue(), { () -> Void in

                    let storyboard = UIStoryboard(name: "Main", bundle: nil)
                    let listVC = storyboard.instantiateViewControllerWithIdentifier("TBC") as! UITabBarController
                    listVC.selectedIndex = 1
                    self.presentViewController(listVC, animated: false, completion: nil)
                })
            })
        }
    }

func postToFirebase(imgUrl: String?) {

        LoadingOverlay.shared.showOverlay(self.overlayView)

        var post: Dictionary<String, AnyObject> = ["username": currentUsername, "description": addDescription.text!, "title": addTitle.text!, "location": addLocation.text!, "images": self.base64String]

        let firebasePost = DataService.ds.REF_LISTS.childByAutoId()
        firebasePost.setValue(post)

    }

正如您所看到的,我尝试使用我在Internet上找到的代码和解释,以及Stack Overflow。但是,如果我打开我的应用程序然后进入listVC,它会在2秒后冻结,可能持续10秒,当我发布某些内容时,它会冻结一段时间进入listVC。

我还有这段代码:

let uploadImage = image
        let imageData = UIImageJPEGRepresentation(uploadImage, 0.5)
        self.base64String = imageData!.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)

没有这个代码,它不会冻结,而且它确实是“它的事情”。在一秒钟内但是我需要这段代码才能将图片发布到firebase?

2 个答案:

答案 0 :(得分:4)

让我们简化一些事情 - 这是概念性的,但可能有所帮助。

首先,填充tableView dataSource。有一种更好的方法来初始填充dataSource并重新加载tableView,因此假设您只有一个小数据集用于此示例。

self.lists = []

DataService.ds.REF_LISTS.observeEventType(.ChildAdded, withBlock: { snapshot in
  self.lists.insert(snapshot.value, atIndex:0)
  self.tableView.reloadData //this is for example only
}

现在稍后,在您的IBAction中,将一些数据写入Firebase

var post: Dictionary<String, AnyObject> = ["username": currentUsername, "description": addDescription.text!, "title": addTitle.text!, "location": addLocation.text!, "images": self.base64String]

let firebasePost = DataService.ds.REF_LISTS.childByAutoId()
firebasePost.setValue(post)

从此处开始,每当Firebase数据写入观察节点时,您的应用程序都会收到新数据的通知,并将其添加到dataSource并更新tableView。

如果您需要先加载一个tableview,然后再观察.childAdded事件,这是一种技巧。

var lastNamesArray = [String]()
var initialLoad = true

//add observers so we load the current lastNames and are aware of any
// lastName additions

peopleRef.observeEventType(.ChildAdded, withBlock: { snapshot in

  let lastName = snapshot.value["last_name"] as? String      
  self.lastNamesArray.append(lastName)

  //upon first load, don't reload the tableView until all children are loaded
  if ( self.initialLoad == false ) {
    self.lastNamesTableView.reloadData()
  }
})

//this .Value event will fire AFTER the child added events to reload the tableView 
//  the first time and to set subsequent childAdded events to load after each
//  child is added in the future

peopleRef.observeSingleEventOfType(.Value, withBlock: { snapshot in

    print("inital data loaded so reload tableView!  /(snapshot.childrenCount)")
    self.lastNamesTableView.reloadData()
    self.initialLoad = false        
})

答案 1 :(得分:2)

在你的上一个函数postToFirebase中,你在后台线程下进行UI操作,这似乎是你的错误......说到

LoadingOverlay.shared.showOverlay(self.overlayView)

正如您的问题下面的评论中所写,在您的第一个函数reloadData()的末尾initObservers发生了同样的事情。

强烈建议不要从后台线程(例如计时器,通信等)更新UI控件等。这可能是崩溃的原因,有时很难识别。而是使用这些来强制在UI线程上执行代码(它始终是“主”线程)。

dispatch_async(dispatch_get_main_queue()) {
    // this code will be running on the main thread
}

<强>更新: 关于您对UIImageJPEGRepresentation的最后一个问题,这是启动它的示例:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
    let getImage =  UIImage(data: NSData(contentsOfURL: NSURL(string: self.remoteImage)))
    UIImageJPEGRepresentation(getImage, 100).writeToFile(imagePath, atomically: true)

    dispatch_async(dispatch_get_main_queue()) {
        self.image?.image = getImage
        return
    }
}

Swift 3

DispatchQueue.global(attributes: .qosBackground).async {
    print("This is run on the background queue")

    DispatchQueue.main.async {
        print("This is run on the main queue, after the previous code in outer block")
    }
}