This is the 11th day of my participation in Gwen Challenge

Kingfisher is a pure Swift library maintained by Meowgod for downloading and caching images from the Web, similar to SDWebImage. It already has 18.5K Star.

The following is to read the source of the receipt.

The use of autoreleasepool

Swift uses an automatic reference counting (ARC) approach to memory management, with little need to worry about content release during normal coding. It’s also easy to overlook the use of Autoreleasepool.

Consider using AutoReleasepool to clean up memory objects in a timely manner when creating large objects or when you need to constantly create objects in a loop.

For autoreleasepool, see the Cat God article @Autoreleasepool.

Autoreleasepool is used to convert UIImage into Data in Kingfisher, as follows:

 public func data(format: ImageFormat.compressionQuality: CGFloat = 1.0) -> Data? {
    return autoreleasepool { () -> Data? in
        let data: Data?
        switch format {
        case .PNG: data = pngRepresentation()
        case .JPEG: data = jpegRepresentation(compressionQuality: compressionQuality)
        case .GIF: data = gifRepresentation()
        case .unknown: data = normalized.kf.pngRepresentation()
        }

        return data
    }
}
Copy the code

Usage of a namespace

When using third-party libraries, you often see namespaces, such as SNPS for SnapKit and Rx for RxSwift. Also, Kingfisher uses Kf as the namespace.

Using namespace avoids having the same name as other framework apis, and you can see how Swift creates namespaces to see how they are used.

In addition to the namespace that the framework needs to consider, a separate module within the framework can also consider using a namespace.

In Kingfisher, MemoryStorage represents the namespace for MemoryStorage and DiskStorage represents the namespace for DiskStorage. The following describes the design of the DiskStorage.

public enum DiskStorage {
    public class Backend<T: DataTransformable> {}}extension DiskStorage {
    public struct Config {}}extension DiskStorage {
    struct Creation {}}Copy the code

The type of the DiskStorage that serves as the namespace is enum. Backend is defined in the DiskStorage and is placed at the top of the code file. Other related classes are placed in the DiskStorage extension.

Notifying the namespace of such modules has two functions

  • Can effectively shorten the class name, structure name, and more clear.DiskStorage.ConfigThe total is better thanDiskStorageConfig;
  • The related classes are defined together and the code structure is very clear.

File storage

In order to frequently request data from the network, downloaded image resources must be stored locally. How to save and obtain the last access time and expiration time of file resources?

One of the first things that might come to mind is to have a special file to hold resources and their corresponding times. Kingfisher’s strategy is to use file attributes to track the last access time and expiration time of a file.

File storage time

After saving a resource resource, the fileManager setAttributes(_ Attributes :ofItemAtPath path:) method is used to set the attributes of the resource. As follows:

let now = Date(a)let attributes: [FileAttributeKey : Any] = [
    // The last access date.
    .creationDate: now.fileAttributeDate,
    // The estimated expiration date.
    .modificationDate: expiration.estimatedExpirationSinceNow.fileAttributeDate //
]
do {
    try config.fileManager.setAttributes(attributes, ofItemAtPath: fileURL.path)
} catch {
    try? config.fileManager.removeItem(at: fileURL)
    throw KingfisherError.cacheError(
        reason: .cannotSetCacheFileAttribute(
            filePath: fileURL.path,
            attributes: attributes,
            error: error
        )
    )
}
Copy the code

Use FileAttributeKey’s creationDate to indicate the last access time and modificationDate to indicate the expiration time. Of course, the original meaning of creationDate and modificationDate should be used to describe the creation and modification time of the file.

Get file time

The time of obtaining file resources is obtained using the resourceValues(forKeys keys:) method of the URL. As follows:

let resourceKeys: Set<URLResourceKey> = [.contentModificationDateKey, .creationDateKey]
let meta = try fileURL.resourceValues(forKeys: resourceKeys)
Copy the code

Using the URLResourceKey creationDateKey said the last access time, expiration time contentModificationDateKey said. It can then be accessed via the meta properties.

meta.creationDate
meta.contentModificationDate ->
Copy the code

When clearing expired files, you need to obtain the creationDateKey and fileSizeKey (file size) as well as the isDirectoryKey to determine whether the URL is the folder path and avoid identifying isDirectory later.

let propertyKeys: [URLResourceKey] = [
    .isDirectoryKey,
    .creationDateKey, 
    .fileSizeKey
]
Copy the code

Note: When the date is saved, the FileAttributeKey value of the file is set by FileManager. When the date is obtained, the value of URLResourceKey is obtained by URL resourceValues method.