Posts

Post marked as solved
1 Replies
428 Views
What is the purpose of the new .memoryTarget option in CIContextOption added in iOS 17? And it is interesting that this option is added only in swift interface. https://developer.apple.com/documentation/coreimage/cicontextoption/4172811-memorytarget
Posted Last updated
.
Post not yet marked as solved
0 Replies
515 Views
I use the most usual method to load images:dataSource = DataSource(collectionView: collectionView) { collectionView, indexPath, identifier in switch indexPath.section { case 0: return self.configureCell(type: DSCVFeed1CollectionViewCell.self, in: collectionView, for: indexPath) { cell in guard case let .feed1(model) = identifier else { return } cell.representedId = model.identifier cell.configure(with: model) self.asyncFetcher.fetchAsync( for: model.identifier, with: (model.imageUrl, cell.illustrationSize, collectionView.traitCollection.displayScale) ) { image in DispatchQueue.main.async { guard cell.representedId == model.identifier else { return } cell.configure(with: image) } } } // ...and func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) { for indexPath in indexPaths { guard indexPath.section != 10 else { continue } guard let item = dataSource.itemIdentifier(for: indexPath) else { continue } asyncFetcher.fetchAsync( for: item.identifier, with: (item.imageUrl, CGSize(width: 100.0, height: 100.0), collectionView.traitCollection.displayScale) ) } } func collectionView(_ collectionView: UICollectionView, cancelPrefetchingForItemsAt indexPaths: [IndexPath]) { for indexPath in indexPaths { guard indexPath.section != 10 else { continue } guard let item = dataSource.itemIdentifier(for: indexPath) else { continue } asyncFetcher.cancelFetch(item.identifier) } }Also I use simple AsyncImageFetcher(with some custom) class provided by Apple in UICollectionViewDataSourcePrefetching documentation:final class AsyncImageFetcher { // MARK: - OSLog private static let log = ProcessInfo.processInfo.environment.keys.contains("SIGNPOSTS_FOR_ASYNCIMAGEFETCHER") ? OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "AsyncImageFetcher") : .disabled // MARK: - Queues private let underlyingQueue: DispatchQueue? private let fetchQueue: PSOperationQueue // MARK: - Stores private var operations = [UUID: GetPreferredImageOperation]() typealias CompletionHandler = (UIImage) -> Void private var completionHandlers = [UUID: [CompletionHandler]]() private let cache = PSCache<uuid, uiimage="">() // MARK: - DI typealias Dependency = (URLSession) let (session): Dependency // MARK: - Initializers init(dependency: Dependency = (.shared), underlineQueue: DispatchQueue? = nil) { (session) = dependency self.underlyingQueue = underlineQueue fetchQueue = PSOperationQueue() fetchQueue.underlyingQueue = underlineQueue } // MARK: - Fetch API typealias Parameters = (url: URL, pointSize: CGSize, scaleFactor: CGFloat) func fetchAsync( for identifier: UUID, with params: Parameters, completionHandler: CompletionHandler? = nil ) { if let completionHandler = completionHandler { let completionHandlers = self.completionHandlers[identifier, default: []] self.completionHandlers[identifier] = completionHandlers + CollectionOfOne(completionHandler) } self.fetchData(for: identifier, with: params) } func cancelFetch(_ identifier: UUID) { //self.fetchQueue.isSuspended = true //defer { self.fetchQueue.isSuspended = false } // self.operations[identifier]?.cancel() // self.completionHandlers[identifier] = nil } // MARK: - Private private func fetchData(for identifier: UUID, with params: Parameters) { print(operations.count) guard operations[identifier] == nil else { return } if let data = cache[identifier] { invokeCompletionHandlers(for: identifier, with: data) } else { let operation = GetPreferredImageOperation( identifier: identifier, underlyingQueue: underlyingQueue, session: session, at: params.url, to: params.pointSize, withScale: params.scaleFactor ) { [weak self] (preferredImage) in guard let image = preferredImage else { return } self?.cache[identifier] = image self?.invokeCompletionHandlers(for: identifier, with: image) } operation.completionBlock = { [weak self, unowned operation] in DispatchQueue.main.async { self?.operations[operation.identifier] = nil } } operations[identifier] = operation fetchQueue.addOperation(operation) } } private func invokeCompletionHandlers(for identifier: UUID, with fetchedData: UIImage) { let completionHandlers = self.completionHandlers[identifier, default: []] self.completionHandlers[identifier] = nil for completionHandler in completionHandlers { completionHandler(fetchedData) } } }GetPreferredImageOperation is a simpleGroupOperationfrom AdvancedOperation WWDC topic.https://gist.github.com/jeudesprits/39c1e97bfda4e7a7b739c2deb50b9c3b https://gist.github.com/jeudesprits/8814279336d38de8167fd2426a63e995The problem is that without canceling operations, everything works much faster and creates much fewer threads. Although it should be exactly the opposite. What could be the matter? All OperationQueue`s use a same private serial queue as underlyingQueue.Without cancelling:8i3t26.jpg With cancelation:LfeKZa.jpg
Posted Last updated
.