如何解决SwiftUI Scrollview Kingfisher KFImage Firebase Storage IDEDebugSessionErrorDomain 代码:4 来自调试器的消息:由于内存问题而终止
我正在使用 Swift/SwiftUI 在 XCode 12.3 中进行开发。我遇到了一个问题,在调用 Firebase 存储下载 API 时,当用户滚动时,图像内存不会从 scrollView 中释放出来。最终应用程序崩溃了 ~3GB 的内存。如果我调用我的 Flickr Image 下载,内存被释放得很好并且内存保持在大约 140MB。
我的具体问题是,我需要更改什么才能允许 firebase 调用在滚动时从内存中删除图像,类似于 Flickr 调用的工作方式?
我在调用 Firebase 的过程中收到以下错误,但图像是从传递给 firebase API gs://<MY DOMAIN>/figure_images/SWBS6I_100300_PrimaryFrontImage_Small.jpg
2021-01-02 11:41:50.776056-0500 xxx[xxx] Task <xxx>.<87> finished with error [-1002] Error Domain=NSURLErrorDomain Code=-1002 "unsupported URL" UserInfo={NSLocalizedDescription=unsupported URL,NSErrorFailingURLStringKey=SWBS6I_116300_PrimaryFrontImage_Small.jpg,NSErrorFailingURLKey=SWBS6I_116300_PrimaryFrontImage_Small.jpg,_NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <xxx>.<87>"
),_NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <xxx>.<87>,NSUnderlyingError=0x2800c0840 {Error Domain=kCFErrorDomainCFNetwork Code=-1002 "(null)"}}
崩溃时出错
iPhone quit unexpectedly.
Domain: IDEDebugSessionErrorDomain
Code: 4
Failure Reason: Message from debugger: Terminated due to memory issue
--
System Information
macOS Version 11.1 (Build 20C69)
Xcode 12.3 (17715) (Build 12C33)
Timestamp: 2021-01-02T11:44:56-05:00
SwiftUI 视图
import SwiftUI
import struct Kingfisher.KFImage
struct SeriesView: View {
// Firebase auth
@EnvironmentObject var authState: AuthenticationState
// Listen for view model state changes,i.e. series
@ObservedObject var seriesList = seriesListSharedData
var showFigures: [Figure] {
let showFiguresFilter: [Figure] = seriesList.figureArray.filter({$0.seriesUniqueId == SeriesUniqueIdEnum.SWBS6I})
return showFiguresFilter
}
// view body
var body: some View {
ScrollView {
LazyVGrid(columns: [
GridItem(.adaptive(minimum: 100,maximum: 100))
],content: {
ForEach(showFigures,id: \.self) { figure in
FigurePhoto(figure: figure)
}
})
}
}
}
struct FigurePhoto: View {
@ObservedObject var figure: Figure
var body: some View {
// name photo and specifics
VStack(alignment: .center,spacing: 4) {
// image and specifics
HStack(alignment: .top,spacing: 4) {
VStack(alignment: .center,spacing: 0) {
let image = figure.imagePrimaryFrontString ?? ""
KFImage(URL(string: image))
.resizable()
.aspectRatio(1,contentMode: .fill)
Spacer()
} // end vstack
.onAppear() {
// (1) WORKS! - Call to flickr function works fine ~ 140MB
figure.fetchFlickrImageURL(withTags: figure.figureGlobalUniqueId,withText: "\(figure.figureGlobalUniqueId)\(kPrimaryFrontImageNameSuffix)\(kSmallSuffix)")
// One call OR the other,not both
// (2) MEMORY ISSUE - Call to firebase function retains memory of every photo whether in view or not until the app crashes ~3GB
figure.fetchFirebasePrimaryFrontImageString(firebaseStorageRef: kFigureImageRoot)
} // end vstack on appear
} // end hstack
} // end vstack
} // end body
}
按预期管理内存的 Flickr 函数,滚动时释放内存
// fetch all flickr photo images - set the primary photo as the first photo
func fetchFlickrImageURL(withTags tags: String,withText text: String?) {
var stringURL = String()
let flickr = Flickr()
flickr.flickrPhotosSearch(withTags: tags,withText: text) {
results,error in
if let error = error {
print("Error searching Flickr: \(error)")
}
if let results = results {
// check for no photos
if results.searchResults.count == 0 {
return
}
for flickrPhoto in results.searchResults {
// set front image if text is front image
if let text = text {
// insert the primary image at the beginning
if let farm = flickrPhoto.farm,let server = flickrPhoto.server,let secret = flickrPhoto.secret {
if text.contains(("\(kPrimaryFrontImageNameSuffix)\(kSmallSuffix)")) {
stringURL = "https://farm\(farm).staticflickr.com/\(server)/\(flickrPhoto.photoID)_\(secret).jpg"
}
else {
stringURL = "https://farm\(farm).staticflickr.com/\(server)/\(flickrPhoto.photoID)_\(secret).jpg"
}
}
}
}
self.imagePrimaryFrontString = stringURL
}
}
}
滚动时不释放内存的 Firebase 函数
func fetchFirebasePrimaryFrontImageString(firebaseStorageRef: String) {
let storage = Storage.storage()
// set the image locataion
getFirebasePrimaryFrontImageString()
// set the image
let storageRefImage = storage.reference().child(firebaseStorageRef)
let seriesImageRef = storageRefImage.child(self.imagePrimaryFrontString!)
seriesImageRef.downloadURL { firebaseImageURL,error in
if let error = error {
#if DEBUG
print("ERROR: Failure getting Firebase Series image: \(error.localizedDescription)")
#endif
}
else
{
self.imagePrimaryFrontString = firebaseImageURL!.absoluteString
}
}
}
func getFirebasePrimaryFrontImageString() {
self.imagePrimaryFrontString = "\(self.figureGlobalUniqueId)\(kPrimaryFrontImageNameSuffix)\(kSmallSuffix).\(kImageJpgExt)"
}
Flickr 实际调用
func flickrPhotosSearch(withTags tags: String,withText text: String? = nil,completion : @escaping (_ results: FlickrSearchResults?,_ error : NSError?) -> Void) {
var searchURL: URL
// check for name passed for front image
if let text = text {
guard let searchURLText = flickrSearch(withText: text) else {
let APIError = NSError(domain: "FlickrSearch",code: 0,userInfo: [NSLocalizedFailureReasonErrorKey:"Unknown API response"])
completion(nil,APIError)
return
}
searchURL = searchURLText
} else {
guard let searchURLTags = flickrSearch(withTags: tags) else {
let APIError = NSError(domain: "FlickrSearch",APIError)
return
}
searchURL = searchURLTags
}
let searchRequest = URLRequest(url: searchURL)
URLSession.shared.dataTask(with: searchRequest,completionHandler: { (data,response,error) in
if let _ = error {
let APIError = NSError(domain: "FlickrSearch",userInfo: [NSLocalizedFailureReasonErrorKey:"Unknown API response"])
OperationQueue.main.addOperation({
completion(nil,APIError)
})
return
}
guard let _ = response as? HTTPURLResponse,let data = data else {
let APIError = NSError(domain: "FlickrSearch",userInfo: [NSLocalizedFailureReasonErrorKey:"Unknown API response"])
OperationQueue.main.addOperation({
completion(nil,APIError)
})
return
}
do {
guard let resultsDictionary = try JSONSerialization.jsonObject(with: data,options: JSONSerialization.ReadingOptions(rawValue: 0)) as? [String: AnyObject],let stat = resultsDictionary["stat"] as? String else {
let APIError = NSError(domain: "FlickrSearch",userInfo: [NSLocalizedFailureReasonErrorKey:"Unknown API response"])
OperationQueue.main.addOperation({
completion(nil,APIError)
})
return
}
switch (stat) {
case "ok":
break
case "fail":
if let message = resultsDictionary["message"] {
let APIError = NSError(domain: "FlickrSearch",userInfo: [NSLocalizedFailureReasonErrorKey:message])
OperationQueue.main.addOperation({
completion(nil,APIError)
})
}
let APIError = NSError(domain: "FlickrSearch",userInfo: nil)
OperationQueue.main.addOperation({
completion(nil,APIError)
})
return
default:
let APIError = NSError(domain: "FlickrSearch",APIError)
})
return
}
guard let photosContainer = resultsDictionary["photos"] as? [String: AnyObject],let photosReceived = photosContainer["photo"] as? [[String: AnyObject]] else {
let APIError = NSError(domain: "FlickrSearch",APIError)
})
return
}
var flickrPhotos = [FlickrPhoto]()
for photoObject in photosReceived {
guard let photoID = photoObject["id"] as? String,let farm = photoObject["farm"] as? Int,let server = photoObject["server"] as? String,let secret = photoObject["secret"] as? String,let originalSecret = photoObject["originalsecret"] as? String else {
break
}
let flickrPhoto = FlickrPhoto(photoID: photoID,farm: farm,server: server,secret: secret,originalSecret: originalSecret)
flickrPhotos.append(flickrPhoto)
// thumbnail image
guard let url = flickrPhoto.flickrImageURL(),let imageData = try? Data(contentsOf: url as URL) else {
break
}
if let image = UIImage(data: imageData) {
flickrPhoto.thumbnail = image
}
}
OperationQueue.main.addOperation({
completion(FlickrSearchResults(searchTerm: tags,searchResults: flickrPhotos),nil)
})
} catch _ {
completion(nil,nil)
return
}
}) .resume()
}
Firebase POD 签名和来自 FIRStorageReference 的评论@see https://cloud.google.com/storage/
/**
* Asynchronously retrieves a long lived download URL with a revokable token.
* This can be used to share the file with others,but can be revoked by a developer
* in the Firebase Console if desired.
* @param completion A completion block that either returns the URL on success,* or an error on failure.
*/
open func downloadURL(completion: @escaping (URL?,Error?) -> Void)
调试时将我带到这里:
- (void)downloadURLWithCompletion:(FIRStorageVoidURLError)completion {
FIRStorageGetDownloadURLTask *task =
[[FIRStorageGetDownloadURLTask alloc] initWithReference:self
fetcherService:_storage.fetcherServiceForApp
dispatchQueue:_storage.dispatchQueue
completion:completion];
[task enqueue];
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。