Swift 3 DispatchQueue.global (загрузка изображений из Firebase и пользовательского интерфейса заморожена)

У меня проблема с этим методом:

func DownloadImages(uid: String, indice: Int) {    
    DispatchQueue.global(qos: .background).async {
        let refBBDD = FIRDatabase.database().reference().child("users").child(uid)
        refBBDD.observeSingleEvent(of: .value, with: { snapshot in
            let snapshotValue = snapshot.value as? NSDictionary
            let profileImageUrl = snapshotValue?.value(forKey: "profileImageUrl") as! String

            let storage = FIRStorage.storage()
            var reference: FIRStorageReference!

            if(profileImageUrl == "") {
                return
            }
            print("before")

            reference = storage.reference(forURL: profileImageUrl)
            reference.downloadURL { (url, error) in
                let data = NSData(contentsOf: url!)
                let image = UIImage(data: data! as Data)

                print("image yet dowload ")
                self.citas[indice].image = image

                DispatchQueue.main.async(execute: { () -> Void in
                    self.tableView.reloadRows(at: [IndexPath(row: indice, section: 0)], with: .none)
                    //self.tableView.reloadData()
                    print("image loaded")
                })
            }
            print("after")
        })
    }
}

Я хочу скачать изображения в фоновом режиме. Я хочу следить за использованием приложения, но пользовательский интерфейс замерз до тех пор, пока методы не входят в reloadRows

Можно ли запустить в режиме истинного фона и могу ли я следовать с помощью приложения?

Программа трассировки:

before
after
before
after
...
before
after
before
after
image yet dowload  --> here start UI frozen
image loaded
image yet dowload 
image yet dowload 
...
image yet dowload 
image yet dowload 
image loaded  
image loaded
...
image loaded
image loaded
image yet dowload ------> here UI is not frozen
image loaded

2 ответа

Решение

Проблема вызвана этой строкой: let data = NSData(contentsOf: url!), Этот конструктор NSData следует использовать только для чтения содержимого локального URL-адреса (путь к файлу на устройстве iOS), поскольку это синхронный метод, и, следовательно, если вы вызываете его для загрузки файла с URL-адреса, он будет блокировать пользовательский интерфейс для много времени.

Я никогда не использовал Firebase, но, глядя на документацию, мне кажется, что вы используете неправильный метод для загрузки этого файла. Вы должны использовать func getData(maxSize size: Int64, completion: @escaping (Data?, Error?) -> Void) -> StorageDownloadTask вместо func downloadURL(completion: @escaping (URL?, Error?) -> Void)поскольку, как указано в документации, последний только "получает долгоживущий URL-адрес для загрузки с отзывным токеном", но не загружает содержимое самого URL-адреса.

Более того, вам не следует принудительно распаковывать значения в обработчике завершения сетевого запроса. Сетевые запросы часто могут не выполняться по причинам, отличным от программной ошибки, но если вы не обработаете эти ошибки корректно, ваша программа потерпит крах.

Ваша проблема в этой линии let data = NSData(contentsOf: url!) и давайте теперь посмотрим, что яблоко говорит об этом методе ниже

Не используйте этот синхронный метод для запроса сетевых URL-адресов. Для сетевых URL-адресов этот метод может блокировать текущий поток на десятки секунд в медленной сети, что приводит к ухудшению работы пользователя, а в iOS может привести к прекращению работы вашего приложения.

Таким образом, ясно говорится, что этот метод заблокирует ваш пользовательский интерфейс.

Другие вопросы по тегам