Nuke読んでて詰まったとこ 7/11〜
@_monoさんが、TwitterでNukeはお手本の様だと仰っていたのもあって、読んでみた
GitHub - kean/Nuke: A powerful image loading and caching framework
読み終えるまで随時更新
読み解けた
internal
internal final class Lock {
クラスのアクセス修飾子
同じライブラリ内ではpublicとして使える
import先の外部からは見えない、という認識でいる
参照:
[iOS 8][Swift] アクセス修飾子を理解する | Developers.IO
DispatchQueue
private let queue = DispatchQueue(label: "com.github.kean.Nuke.Manager")
同期、非同期するにせよ、作っておくタスク
この変数に対して、同期/非同期実行を関数で呼び出す
private var observers = [() -> Void]()
クロージャの配列 配列の後の()が別物感醸してたけど、配列で良いらしい
参照:
[Swift]クロージャーの使い方 ※swift2.2の記事の為、不可となっているが
subscript
public protocol Caching: class { /// Accesses the image associated with the given key. subscript(key: AnyHashable) -> Image? { get set } } public extension Caching { /// Accesses the image associated with the given request. public subscript(request: Request) -> Image? { get { return self[Request.cacheKey(for: request)] } set { self[Request.cacheKey(for: request)] = newValue } } }
これすごくかっこいい
まだざっくりとでも自分の言葉で説明できるほどに理解はしてない
※配列な訳ではない(確保されるメモリはこのクラス1つ分)
2箇所挙げたのは、ここの引数が違うことに気付かなくて数分混乱したから
上の方のは、準拠先のCacheでオーバーライドされている
objc_setAssociatedObject
objc_setAssociatedObject(target, &contextAK, context, .OBJC_ASSOCIATION_RETAIN)
変数をクラスに生やす(外部から)
ランタイムの機能を使っているそう ← 今度調べる
ここで渡している&contextAKが分からない
private static var contextAK = "Manager.Context.AssociatedKey"
第2引数自体がイマイチ分かってない ← 今度試し書きしてみる
参照:
継承を使わずにクラスにプロパティを設定する方法! - VASILY DEVELOPERS BLOG
Result列挙型
public enum Result<T> { case success(T), failure(Error) /// Returns a `value` if the result is success. public var value: T? { if case let .success(val) = self { return val } else { return nil } } /// Returns an `error` if the result is failure. public var error: Error? { if case let .failure(err) = self { return err } else { return nil } } } // 以下、実際に使用している箇所. // 設定箇所. if let data = data, let response = response, error == nil { completion(.success((data, response))) // タプルだよね?. } else { completion(.failure((error ?? NSError(domain: NSURLErrorDomain, code: NSURLErrorUnknown, userInfo: nil)))) } // valueの使用箇所. public func handle(response: Result<Image>, isFromMemoryCache: Bool) { guard let image = response.value else { return }
valueを呼ぶと、自身(設定箇所で渡されているResult変数)が.successなら、.successの値を返す
swiftの列挙型にまだ慣れていなくてやたら時間掛かった…
参照:
Swiftの列挙型(enum)おさらい - Qiita
Pattern Matching, Part 4: if case, guard case, for case – Crunchy Development
isKnownUniquelyReferenced()
private mutating func applyMutation(_ closure: (Container) -> Void) { if !isKnownUniquelyReferenced(&container) { container = container.copy() } closure(container) }
名前通り、参照(※強参照)が1つかどうかを確認する
この関数が何をしようとしているのかは未だ理解できてない
参照:
isKnownUniquelyReferenced(_:) - Swift Standard Library | Apple Developer Documentation
reduce
public func process(_ input: Image) -> Image? { return processors.reduce(input as Image!) { image, processor in return autoreleasepool { image != nil ? processor.process(image!) : nil } } }
コメント読む限り、Image!に変換したinput(=image)を追加した順のprocessorに渡しつつprocess実行したい、ということだと思う(コード的にもそう思う)
ということは、processorは配列だし、for文でも出来る??
けどこっちのがスマート、ってことで合ってるのだろうか?
参照:
Swiftのmap, filter, reduce(などなど)はこんな時に使う! - Qiita
DispatchWorkItem
let work = DispatchWorkItem(block: closure) queue.async(execute: work)
キューの指定が必要ない + 細かい制御ができる 今回は渡したclosureを非同期実行させたいから使ってる
参照:
[Swift 3] Swift 3時代のGCDの基本的な使い方 | Developers.IO
convenience
public convenience init(maxConcurrentOperationCount: Int) { let queue = OperationQueue() queue.maxConcurrentOperationCount = maxConcurrentOperationCount self.init(queue: queue) }
親クラスのinitの前に処理したい為につけてる
参照:
[Swift] convenienceイニシャライザとdesignated(指定)イニシャライザ - Qiita
willChangeValue/didChangeValue
override var isExecuting: Bool { get { return _isExecuting } set { willChangeValue(forKey: "isExecuting") _isExecuting = newValue didChangeValue(forKey: "isExecuting") } }
プロパティがコードで変更された時に明示的に通知する 変更内容はwillとdidで挟んであげる isExecutingのsetterの中なのだから、通知する必要がなさそうに思ったけど、どこで必要になるのだろう??
参照:
willChangeValue(forKey:) - NSObject | Apple Developer Documentation
Swift で Model を書いてみた (observer編) - Qiita
ヒレガス本の例題を Swift で書いてみる(3) - Qiita
CALayerを設定している意味
extension ImageView: Target { /// Displays an image on success. Runs `opacity` transition if /// the response was not from the memory cache. public func handle(response: Result<Image>, isFromMemoryCache: Bool) { guard let image = response.value else { return } self.image = image if !isFromMemoryCache { let animation = CABasicAnimation(keyPath: "opacity") animation.duration = 0.25 animation.fromValue = 0 animation.toValue = 1 let layer: CALayer? = self.layer // Make compiler happy on macOS layer?.add(animation, forKey: "imageTransition") } } }
表示する時に綺麗なフェードインをしてくる所以がここか!
不透明度を0から1にするまでのアニメーションを設定している
読み解けてない
CancellationTokenSource::cancel
public func cancel() { if isCancelling { return } // fast pre-lock check lock.sync { if !isCancelling { isCancelling = true observers.forEach { $0() } observers.removeAll() } } }
ManagerクラスのloadImage関数ののcancelRequest(for: target)でtargetが既に登録されているなら、cancel掛ける、という意味だと思ったけれど…。
CancellationTokenSourceクラスのcancel関数の中で、キャンセル状態に切り替える時にobservers.forEach { $0() }で実行してからクロージャを削除しているところが分からない。