- 사전 operation이 완료되기 전까지는 dependent operation은 작동하지 않는다.
- 첫 번째 operation에서 두 번째 operation으로 데이터를 보낼 때 자동적으로 깔끔하게 데이터를 보낼 수 있다.
addDependency(op:)
메서드를 활용해서 간단한게 이용할 수 있다. networkOp
가 작동이 완료되면, decryptOP
가 작동되고 그 후 tiltShiftOp
가 작동된다.let networkOp = NetworkImageOperation()
let decryptOp = DecryptOperation()
let tiltShiftOp = TiltShiftOperation()
decryptOp.addDependency(op: networkOp)
tiltShiftOp.addDependency(op: decryptOp)
removeDependency(op:)
메서드를 호출하면 된다. tiltShiftOp.removeDependency(op: decryptOp)
dependencies
를 제공하며, 이는 주어진 작업에 대해 의존성으로 표시된 operations 배열을 반환한다.tiltShiftOp.dependencies
// -> dependency Operation array 반환
let network = NetworkClass()
network.onDownloaded { raw in
guard let raw = raw else { return }
let decrypt = DecryptClass(raw)
decypt.onDecrypted { decrypted in
guard let decrypted = decryped else { return }
let tilt = TiltShiftClass(decrypted)
tilt.onTiltShifted { tilted in
guard let tilted = tilted else { return }
}
}
}
- Operation2는 Operation5가 끝나기 전까지는 시작하지 않는다.
- Operation5는 Operation3이 끝나기 전까지는 시작하지 않는다.
- Operation3은 Operation2가 끝나기 전까지는 시작하지 않는다.
NetworkImageOperation
은 output property image
을 호출한다. image
로 호출할 것이라고 기대할 수 없다. 예를들어, 클래스 내부에서는 foodImage
로 호출하는 것이 좋을 수도 있다.UIImage
type의 image
속성을 제공할께."라는 의미이다.import UIKit
protocol ImageDataProvider {
var image: UIImage? { get }
}
UIImage
를 output data로 하는 Any Operation은 위의 프로토콜을 채택하면 된다. NetworkImageOperation.swift
에 아래의 코드를 더한다.extension NetworkImageOperaton: ImageDataProvider {
var image: UIImage? { return outputIamge }
}
TiltShiftOperation
또한 수정한다extension TiltShiftOperation: ImageDataProvider {
var image: UIImage? { return outputImage }
}
TiltShiftOperation
은 UIImage
를 input으로 필요로한다. 그냥 inputImage
속성을 설정하는 것이 아니라, dependency가 UIImage
를 제공하는 것이 있는지 확인해야 한다. let dependencyImage = dependencides
.compactMap { ($0 as? ImageDataProvider)?.image}
.first
guard let inputImage = inputImage ?? dependecyImage else {
return
}
image
를 확인하는 것은 TiltShiftOperation
를 input Image없이 초기화 해야한다. TiltShiftOperation
의 init
함수를 수정한다.init(image: UIImage? = nil) {
inputImage = image
super.init()
}
tableView(_: cellForRowAt: )
을 수정한다.let downloadOp = NetworkImageOperation(url: urls[indexPath.row])
let tiltShiftOp = TiltShiftOperation()
tiltShiftOp.addDependency(downloadOp)
tiltShiftOp.completionBlock = {
DispatchQueue.main.async {
guard let cell = tableView.cellForRow(at: indexPath) as? PhotoCell else { return }
cell.isLoading = false
cell.display(image: tiltShiftOp.image)
}
}
queue.addOperations([downloadOp, tiltShiftOp], waitUntilFinished: false)
completionBlock
을 제공한다. 이미지를 가져오고 메인 큐로 다시 dispatch 하는 작업에 약간의 추가 작업을 수행해야 한다. 이러한 경우에는 custom completion block을 고려할 수 있다. var onImageProcessed: ((UIImage?) -> Void)?
main()
메소드 안에서 outputImage
다음으로 아래의 completion handler 을 작성한다.if let onImageProcessed = onImageProcessed {
DispatchQueue.main.async { [weak self] in
onImageProcessed(self?.outputImage)
}
}
tableVIew(_: cellForRowAt:)
에 completion block을 수정한다.tiltShiftOp.onImageProcessed = { image in
guard let cell = tableView.cellForRow(at: indexPath) as? PhotoCell else { return }
cell.isLoading = false
cell.display(image: image)
}
import Foundation
class AsyncOperation: Operation {
// Create state management
enum State: String {
case ready, executing, finished
fileprivate var keyPath: String {
return "is\(rawValue.capitalized)"
}
}
var state = State.ready {
willSet {
willChangeValue(forKey: newValue.keyPath)
willChangeValue(forKey: state.keyPath)
}
didSet {
didChangeValue(forKey: oldValue.keyPath)
didChangeValue(forKey: state.keyPath)
}
}
// Override properties
override var isReady: Bool {
return super.isReady && state == .ready
}
override var isExecuting: Bool {
return state == .executing
}
override var isFinished: Bool {
return state == .finished
}
override var isAsynchronous: Bool {
return true
}
// Override start
override func start() {
main()
state = .executing
}
}
import UIKit
protocol ImageDataProvier {
var image: UIImage? { get }
}
final class NetworkImageOperation: AsyncOperation {
var outputImage: UIImage?
private let url: URL
private let completionHandler: ((Data?, URLResponse?, Error?) -> Void)?
init(url: URL, completionHandler: ((Data?, URLResponse?, Error?) -> Void)? = nil) {
self.url = url
self.completionHandler = completionHandler
super.init()
}
convenience init?(string: String, completionHandler: ((Data?, URLResponse?, Error?) -> Void)? = nil) {
guard let url = URL(string: string) else { return nil }
self.init(url: url, completionHandler: completionHandler)
}
override func main() {
URLSession.shared.dataTask(with: url) { [weak self]
data, response, error in
guard let self = self else { return }
defer { self.state = .finished }
if let completionHandler = self.completionHandler {
completionHandler(data, response, error)
return
}
guard error == nil, let data = data else { return }
self.outputImage = UIImage(data: data)
}.resume()
}
}
extension NetworkImageOperation: ImageDataProvier {
var image: UIImage? { return outputImage }
}
import UIKit
final class TiltShiftOperation: Operation {
private static let context = CIContext()
var outputImage: UIImage?
var onImageProcessed: ((UIImage?) -> Void)?
private let inputImage: UIImage?
init(image: UIImage? = nil, completion: ((UIImage?) -> Void)? = nil) {
onImageProcessed = completion
inputImage = image
super.init()
}
override func main() {
// guard let inputImage = inputImage else { return }
let dependencyImage = dependencies
.compactMap { ($0 as? ImageDataProvier)?.image }
.first
guard let inputImage = inputImage ?? dependencyImage else { return }
guard let filter = TiltShiftFilter(image: inputImage, radius:3),
let output = filter.outputImage else {
print("Failed to generate tilt shift image")
return
}
let fromRect = CGRect(origin: .zero, size: inputImage.size)
guard
let cgImage = TiltShiftOperation.context.createCGImage(output, from: fromRect),
let rendered = cgImage.rendered()
else {
print("No image generated")
return
}
outputImage = UIImage(cgImage: rendered)
if let onImageProcessed = onImageProcessed {
DispatchQueue.main.async { [weak self] in
onImageProcessed(self?.outputImage)
}
}
}
}
extension TiltShiftOperation: ImageDataProvier {
var image: UIImage? { return outputImage }
}
import UIKit
class TiltShiftTableViewController: UITableViewController {
private let queue = OperationQueue()
private var urls: [URL] = []
override func viewDidLoad() {
super.viewDidLoad()
guard let plist = Bundle.main.url(forResource: "Photos", withExtension: "plist"),
let contents = try? Data(contentsOf: plist),
let serial = try? PropertyListSerialization.propertyList(from: contents, format: nil),
let serialUrls = serial as? [String] else {
print("Something went horribly wrong!")
return
}
urls = serialUrls.compactMap { URL(string: $0) }
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return urls.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "normal", for: indexPath) as! PhotoCell
cell.display(image: nil)
let downloadOp = NetworkImageOperation(url: urls[indexPath.row])
let tiltShiftOp = TiltShiftOperation { image in
cell.isLoading = false
cell.display(image: image)
}
tiltShiftOp.addDependency(downloadOp)
queue.addOperations([downloadOp, tiltShiftOp], waitUntilFinished: false)
return cell
}
}
제가 학습한 내용을 요약하여 정리한 것입니다. 내용에 오류가 있을 수 있으며, 어떠한 피드백도 감사히 받겠습니다.
감사합니다