The following code works perfectly well, but I am surprised, because I thought NSCache
would require object keys & values, and would not take CGFloat
s. Can someone explain to me what is happening?
class A
var cachedPoints = NSCache()
func rAt(theta theta: CGFloat) -> CGFloat {
if let r = self.cachedPoints.objectForKey(theta) as? CGFloat {
return r
}
// do some very expensive maths here...
let r = self.veryExpensiveFunction(theta)
self.cachedPoints.setObject(r, forKey: theta)
return r
}
}
From Working with Cocoa Data Types in the "Using Swift with Cocoa and Objective-C" documentation:
Numbers
Swift automatically bridges certain native number types, such as
Int
andFloat
, toNSNumber
.
...
It also allows you to pass a value of typeInt
, for example, to an argument expecting anNSNumber
.
The documentation mentions Int
, UInt
, Float
, Double
, Bool
as types which are automatically bridged to NSNumber
, but apparently
this works for CGFloat
as well (if "Foundation" is imported).
So in
self.cachedPoints.setObject(r, forKey: theta)
both r
and theta
are wrapped into NSNumber
instances
and then passed to the setObject()
method which takes two parameters
of type AnyObject
.
There is also a bridging in the reverse direction, but that is
less well documented (or I could not find it). You can cast an
instance of NSNumber
to CGFloat
(or Int
, Float
, ...)
let num : NSNumber = ...
let x = num as CGFloat
and the result is the same as if you called doubleValue
(or floatValue
, depending on the architecture) on the number instance.
In
let obj : AnyObject = ...
let y = obj as? CGFloat // CGFloat?
the object is optionally cast to NSNumber
and – if that was successful
– converted to CGFloat
as in the previous example.
And that is what happens in
if let r = self.cachedPoints.objectForKey(theta) as? CGFloat { ... }
If the return value from objectForKey()
is an NSNumber
then
it is converted to CGFloat
and assigned to r
. Otherwise,
the optional binding fails.