6

这个问题CKRecord询问是否可以在 Swift中使用下标。虽然我已经知道如何做提问者想要的,但它的每一个排列都会给我一个堆栈溢出:

subscript(key: String) -> CKRecordValue? {
    get {
        return objectForKey(key) as CKRecordValue?
    }
    set {
        setObject(newValue, forKey: key)
    }
}

堆栈溢出发生在 getter 中。(我从未尝试过 setter,所以它也可能在那里发生。)我尝试使用objectForKey:objectForKeyedSubscript:valueForKey:. 都产生相同的结果:堆栈溢出。

这很奇怪,因为CKRecord肯定是用 Objective-C 编写的。为什么会递归调用 Swift 的subscript方法?这没有道理。Nate Cook 在回答提问者时想知道为什么 Swift 不会objectForKeyedSubscript:自动桥接。好吧,也许执行此操作的代码没有完全烘焙,但导致了这个问题。我将不得不与另一个具有objectForKeyedSubscript:.

更新

看起来objectForKeyedSubscript:通常是桥接的。我使用适当的方法在 Objective-C 中创建了一个类,将其添加到桥接头中,索引器就在那里并且没有问题地编译。更好的是,它在没有堆栈溢出的情况下工作。

这意味着正在发生一些非常不寻常的事情CKRecord

理论

如果您在 Swift 中创建一个类,该类从a 作为键继承NSObject并在其上实现该subscript方法,则它String变为objectForKeyedSubscript:. (对于“纯 Swift”类,我怀疑情况并非如此。)您可以通过将 Swift 类导入到 Objective-C 并验证它objectForKeyedSubscript:是否存在来验证这一点。

由于CKRecord继承自NSObject,因此实现subscript会覆盖默认实现。objectForKey:此外,似乎valueForKey:所有最终都调用objectForKeyedSubscript:了 ,这导致(阅读:“与”相同)调用subscript,从而导致堆栈溢出。

这可以解释为什么会发生堆栈溢出。它仍然没有解释为什么objectForKeyedSubscript:没有自动桥接,但也许是因为 的定义setObject:forKeyedSubscript:与规范的类型签名略有不同:- (void)setObject:(id <CKRecordValue>)object forKeyedSubscript:(NSString *)key;. 这对 Objective-C 没有影响,但可能会导致“桥接代码”出错。毕竟,Swift 是相当新的。

4

3 回答 3

2

CKRecord经过一些测试和调试(通过子类) ,我发现 forobjectForKey:确实调用了objectForKeyedSubscript:. 此外,在隐式标记(通过从 下降)或显式subscript标记的 Swift 类中实现意味着实现为.@objcNSObjectsubscriptobjectForKeyedSubscript:

这意味着在扩展中实现subscripton会隐藏默认实现,这会导致堆栈溢出。CKRecord

于 2014-11-27T00:09:16.830 回答
1

这是 CKRecord 的一个简单扩展,使其更容易下标。

extension CKRecord {
    struct Sub {
        let record: CKRecord

        subscript(key: String) -> CKRecordValue? {
            get {
                return record.objectForKey(key) as? CKRecordValue
            }
            set {
                record.setObject(newValue, forKey: key)
            }
        }
    }

    var sub: Sub {
        return Sub(record: self)
    }

    var : Sub {
        return sub
    }
}

用法:

var sub = record.sub
sub["name"] = name

/* Or */

// One does not simply subscript CKRecord
record.["name"] = name

(顺便说一句,我在开玩笑)

于 2015-05-20T08:35:06.500 回答
1

通过在开发论坛上搭载 Apple 工程师编写的一些代码,我能够成功下标。

import CloudKit

protocol MyCKRecordValueType {
    var asObject: CKRecordValue { get }
}

extension CKRecord {
    func set<ValueType>(value: ValueType, forKey key: String) where ValueType : MyCKRecordValueType {
        let object = value.asObject
        self.setObject(object, forKey: key)
    }
    subscript(key : String) -> MyCKRecordValueType? {
        set {
            self.setObject(newValue?.asObject, forKey: key)
        }
        get {
            return object(forKey: key) as? MyCKRecordValueType
        }
    }
}

extension String : MyCKRecordValueType {
    var asObject: CKRecordValue { return self as NSString }
}
extension Bool : MyCKRecordValueType {
    var asObject: CKRecordValue { return self as NSNumber }
}
extension Int : MyCKRecordValueType {
    var asObject: CKRecordValue { return self as NSNumber }
}
extension Data : MyCKRecordValueType {
    var asObject: CKRecordValue { return self as NSData }
}

然后,您可以按预期调用下标:

let firstRecordID = CKRecordID(recordName: "0")

        let record = CKRecord(recordType: "Foo", recordID: firstRecordID)

        record["title"] = "Hello World"

        record["year_established"] = 2000
于 2017-10-11T00:12:21.050 回答