This question asks whether one can use subscripting with CKRecord
in Swift. While I already knew how to do what the questioner wanted, every permutation of it gives me a stack overflow:
subscript(key: String) -> CKRecordValue? {
get {
return objectForKey(key) as CKRecordValue?
}
set {
setObject(newValue, forKey: key)
}
}
The stack overflow occurs in the getter. (I've never tried the setter, so it may occur there, too.) I've tried implementing with objectForKey:
, objectForKeyedSubscript:
, and valueForKey:
. All produce the same result: a stack overflow.
This is very strange, since CKRecord
is certainly written in Objective-C. Why would it recursively call Swift's subscript
method? It makes no sense. Nate Cook, in his answer to the questioner, wonders why Swift doesn't bridge objectForKeyedSubscript:
automatically. Well, maybe the code to do that is not fully baked, but is causing this problem. I will have to try it with another class that has objectForKeyedSubscript:
.
It appears that objectForKeyedSubscript:
is ordinarily bridged. I created a class in Objective-C with the appropriate methods, added it to the bridging header, and the indexers were there and compiled without issue. Even better, it worked without a stack overflow.
This means that something very unusual is going on with CKRecord
.
If you create a class in Swift that descends from NSObject
and implements the subscript
method on it with a String
as the key, this becomes objectForKeyedSubscript:
. (For "pure Swift" classes, I suspect this is not the case.) You can verify this by importing your Swift class into Objective-C and verifying that objectForKeyedSubscript:
is there.
Since CKRecord
descends from NSObject
, implementing subscript
overrides the default implementation. Further, it seems that objectForKey:
and valueForKey:
all ultimately called objectForKeyedSubscript:
, which results in (read: "is the same as") a call to subscript
, which causes the stack overflow.
That may explain why the stack overflow occurs. It still does not explain why objectForKeyedSubscript:
was not automatically bridged, but perhaps it's because the definition of setObject:forKeyedSubscript:
has a slightly different type signature from the canonical one: - (void)setObject:(id <CKRecordValue>)object forKeyedSubscript:(NSString *)key;
. This makes no difference to Objective-C, but might trip up the "bridging code". Swift is pretty new, after all.
After some testing and debugging (via a subclass), I discovered that, for CKRecord
, objectForKey:
does indeed call objectForKeyedSubscript:
. Also, implementing subscript
in a Swift class that is marked @objc
implicitly (by descending from NSObject
) or explicitly means that subscript
is implemented as objectForKeyedSubscript:
.
This means that implementing subscript
on CKRecord
in an extension hides the default implementation, which causes the stack overflow.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With