Просмотр исходного кода

Add event callback fix memory leak

onevcat 6 лет назад
Родитель
Сommit
400fd81032

+ 6 - 0
Demo/Demo/Kingfisher-Demo/ViewControllers/SwiftUIView.swift

@@ -34,6 +34,12 @@ struct SwiftUIView : SwiftUI.View {
             Text("Hello World")
             KFImage(url: URL(string: "https://onevcat.com/assets/images/avatar.jpg")!)
                 .resizable()
+                .onSuccess { r in
+                    print(r)
+                }
+                .onFailure { e in
+                    print("Get an error \(e)")
+                }
                 .padding()
                 .background(Color.red)
                 .frame(width: 400, height: 400)

+ 25 - 20
Sources/SwiftUI/ImageBinder.swift

@@ -28,32 +28,37 @@ import Combine
 import SwiftUI
 
 @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
-class ImageBinder: BindableObject {
-    let url: URL
-    var didChange = PassthroughSubject<KFCrossPlatformImage?, Never>()
+extension KFImage {
+    class ImageBinder: BindableObject {
+        let url: URL
 
-    var onDone = PassthroughSubject<RetrieveImageResult, KingfisherError>()
+        var didChange = PassthroughSubject<KFCrossPlatformImage?, Never>()
+        var subject = PassthroughSubject<RetrieveImageResult, KingfisherError>()
 
-    var image: Kingfisher.KFCrossPlatformImage? {
-        didSet {
-            didChange.send(image)
+        var subscriber: Subscribers.Sink<PassthroughSubject<RetrieveImageResult, KingfisherError>>?
+
+        var image: Kingfisher.KFCrossPlatformImage? {
+            didSet {
+                didChange.send(image)
+            }
         }
-    }
 
-    init(url: URL) {
-        self.url = url
-    }
+        init(url: URL) {
+            self.url = url
+        }
 
-    func start() {
-        _ = KingfisherManager.shared.retrieveImage(with: .network(url)) { r in
-            switch r {
-            case .success(let result):
-                self.image = result.image
-                self.onDone.send(result)
-                self.onDone.send(completion: .finished)
-            case .failure(let error):
-                self.onDone.send(completion: .failure(error))
+        func start() {
+            _ = KingfisherManager.shared.retrieveImage(with: .network(url)) { r in
+                switch r {
+                case .success(let result):
+                    self.image = result.image
+                    self.subject.send(result)
+                    self.subject.send(completion: .finished)
+                case .failure(let error):
+                    self.subject.send(completion: .failure(error))
+                }
             }
         }
     }
+
 }

+ 40 - 1
Sources/SwiftUI/KFImage.swift

@@ -36,6 +36,9 @@ public struct KFImage: View {
 
     @ObjectBinding var binder: ImageBinder
 
+    private let onFailureDelegate = Delegate<KingfisherError, Void>()
+    private let onSuccessDelegate = Delegate<RetrieveImageResult, Void>()
+
     public init(url: URL) {
         binder = ImageBinder(url: url)
         configs = []
@@ -50,7 +53,23 @@ public struct KFImage: View {
 
         return configs
             .reduce(image) { current, config in config(current) }
-            .onAppear { self.binder.start() }
+            .onAppear { [unowned binder] in
+                binder.subscriber = binder.subject.sink(
+                    receiveCompletion: { complete in
+                        switch complete {
+                        case .failure(let error):
+                            self.onFailureDelegate.call(error)
+                        case .finished: break
+                        }
+                        binder.subscriber?.cancel()
+                        binder.subscriber = nil
+                    },
+                    receiveValue: { result in
+                        self.onSuccessDelegate.call(result)
+                    }
+                )
+                binder.start()
+            }
     }
 }
 
@@ -83,12 +102,32 @@ extension KFImage {
     }
 }
 
+@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
+extension KFImage {
+    public func onFailure(perform action: ((Error) -> Void)?) -> KFImage {
+        onFailureDelegate.delegate(on: binder) { _, error in
+            action?(error)
+        }
+        return self
+    }
+
+    public func onSuccess(perform action: ((RetrieveImageResult) -> Void)?) -> KFImage {
+        onSuccessDelegate.delegate(on: binder) { _, result in
+            action?(result)
+        }
+        return self
+    }
+}
+
 #if DEBUG
 @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
 struct KFImage_Previews : PreviewProvider {
     static var previews: some View {
         KFImage(url:URL(string: "https://raw.githubusercontent.com/onevcat/Kingfisher/master/images/logo.png")!)
         .resizable()
+        .onSuccess { r in
+            print(r)
+        }
         .interpolation(.medium)
         .aspectRatio(contentMode: .fit)
         .padding()