Swift - 如何使用 class 类型作为参数/变量

Swift - How to use a class type as a parameter / variable

我正在尝试创建一个有点通用的函数来根据属性

的值对文件 URL 进行排序

我的目标是:

1) 将URL 和一些参数(包括类型)传递给函数。 然后循环遍历文件的属性

2) 将匹配的文件属性和 URL 添加到元组数组

3) 根据找到的属性值对元组进行排序

4) Return 排序数组并按排序顺序显示项目

我认为我需要将属性的类型传递给排序函数,这样我就可以在元组中设置它,因为我无法使用 "Any" 进行排序,但我不确定如何做到这一点

我可以将任何东西传递给排序函数并构造或解构我在排序函数中需要的值,因为这将根据用户选择的操作进行预定义

//Initial implementation to be later tied to IBActions and simplified   

func sortFiles(sortByKeyString : String, fileURLArray : [URL]) -> [URL] 
{

        switch sortByKeyString {
            case "date-created-DESC":
                let fileAttributeKeyString : String = "creationDate"
                let isSortOrderDesc = true
                let objectTypeString : String = NSDate.className()
                let sortedFileURLArray = sortFileArrayByType(fileAttributeKeyString: fileAttributeKeyString, fileURLArray: fileURLArray, type: objectTypeString, isSortOrderDesc : isSortOrderDesc)
                return sortedFileURLArray
            default:
                return fileURLArray
        }
    }

//Generic function to get a files attributes from a URL by requested 
type

    func sortFileArrayByType(fileAttributeKeyString : String, fileURLArray : [URL], type: String, isSortOrderDesc : Bool) -> [URL] {
        let fileManager = FileManager.default
        let attributeToLookFor : FileAttributeKey = FileAttributeKey.init(rawValue: fileAttributeKeyString)
        var tupleArrayWithURLandAttribute : [(url: URL, attribute: *Any*)]? = nil

        for url in fileURLArray {
            do {
                let attributes = try fileManager.attributesOfItem(atPath: url.path)
                for (key, value) in attributes {
                    if key.rawValue == fileAttributeKeyString {
                        tupleArrayWithURLandAttribute?.append((url: url, attribute: value))
                    }

                }
                let sortedTupleArrayWithURLandAttribute = tupleArrayWithURLandAttribute?.sorted(by: { [=13=].attribute < .attribute)})
                // Need to Sort dictionary into array
                return sortedTupleArrayWithURLandAttribute
            } catch {
                return fileURLArray
            }
        }
    }

所以我想我知道你在说什么,这就是我想出的:

func checkType<T>(_ type: T.Type) {
    if type.self == String.self {
        print("It's a string!")
    } else if type.self == Int.self {
        print("It's an int!")
    } else {
        print("It's something else...")
    }
}

然后你可以通过直接向它传递一个类型来调用它,或者通过获取变量的类型并将其传递进来,如下所示:

checkType(String.self) // prints "It's a string!"
let number: Int = 1
checkType(type(of: number)) // prints "It's an int!"

希望对您有所帮助!

首先阅读Swift编程语言中的Metatype Type。阅读后继续回答。

从中您了解到可以将函数参数的类型声明为类型的类型(您可以斜视),又名 元类型,因此可以通过函数的类型。将其与泛型和 Swift 的类型推断相结合,您可以将函数声明为:

func sortFileArrayByType<T>(fileAttributeKeyString : String,
                            attributeType : T.Type,
                            fileURLArray : [URL]
                           ) -> [(url: URL, attribute: T)]
                           where T : Comparable

这会添加参数 attributeType,其类型是 T 的元类型,其中 T 将被推断。例如元类型 String.self 可以被传递并且 T 将被推断为 String.

where 子句约束 T,因此只允许 Comparable 类型,这是启用函数进行排序所必需的。文件属性可以是 DateStringNSNumber 值;不幸的是后者不符合 Comparable 所以你需要添加一个扩展来制作它,以下就足够了:

extension NSNumber : Comparable
{
   public static func <(a : NSNumber, b : NSNumber) -> Bool { return a.compare(b) == .orderedAscending }
   public static func ==(a : NSNumber, b : NSNumber) -> Bool { return a.compare(b) == .orderedSame }
}

在函数体内,您需要声明元组数组具有 T:

类型的属性
var tupleArrayWithURLandAttribute : [(url: URL, attribute: T)] = []

并且当您添加条目时,您需要将 attributesOfItem 返回的值转换为 T:

tupleArrayWithURLandAttribute.append((url: url, attribute: value as! T))

注意这里使用as!,你必须在函数调用中正确匹配属性名称和它的值的类型,否则你将得到一个运行时中止。如果需要,将其作为软错误处理,留作练习。

您发布的代码中有许多拼写错误等,这些问题留给您修复,完成后您的功能应该可以正常工作。调用可能如下所示:

let ans = sortFileArrayByType2(fileAttributeKeyString: "NSFileCreationDate",
                               attributeType: Date.self,
                               fileURLArray: urlArray)

在这种情况下 ans 的类型将是 [(url: URL, attribute: Date)]

HTH

您在这里寻找的是一种按 URLResourceKey(特别是与该键相关的 URLResourceValues 属性)对 URL 序列进行排序的方法。不幸的是,URLResourceValues 没有以有用的方式映射到 URLResourceKey。但我们可以通过扩展来解决这个问题:

extension URLResourceValues {
    static func key<T>(for keyPath: KeyPath<Self, T>) -> URLResourceKey {
        switch keyPath {
        case \Self.creationDate: return .creationDateKey
        // ... Other keys ...
        default: fatalError()
        }
    }
}

获取 URLResourceValues keyPath 的值将非常有用:

extension URL {
func resourceValue<T>(for keyPath: KeyPath<URLResourceValues, T?>) throws -> T? {
        return try resourceValues(forKeys: Set([URLResourceValues.key(for: keyPath)]))[keyPath: keyPath]
    }
}

有了它,我们可以构建一个基于 URLResourceValues 的排序方法(假设 nil 小于其他值;您可以用抛出不存在的值来替换它):

extension Sequence where Element == URL {
    func sorted<T>(by keyPath: KeyPath<URLResourceValues, T?>) throws -> [URL]
        where ResourceType: Comparable {
            return try self
                .sorted { (lhs, rhs) in
                    guard let lhsValue = try lhs.resourceValue(for: keyPath)
                        else { return true }
                    guard let rhsValue = try rhs.resourceValue(for: keyPath)
                        else { return false }
                    return lhsValue < rhsValue
            }
    }
}

最后,可以通过传递基于 URLResourceValues 的键路径来使用它:

let sortedFiles = try files.sorted(by: \.creationDate)