Hello,
I’ve been working through the book However, I’ve been using Swift 2.0. I’ve been correcting issues with the examples along the way without any issues thus far. However I am running into an issue with the extension to NSString that draws the number text centered on the die face. The issue is with the attributes dictionary that is being passed to sizeWithAttributes. In Swift 2.0 the signature of this method has changed from:
func sizeWithAttributes(attrs: [String : AnyObject]!) -> NSSize
to:
func sizeWithAttributes(attrs: [String : AnyObject]?) -> NSSize
I thus converted the method signature for drawCenteredInRect to use an optional as follows below. However I end up with a compilation error on the line invoking sizeWithAttributes(attributes). The error says:
/Users/rsaccone/Code/TechBookCode/Cocoa Programming 5th Edition/Dice/Dice/NSString+Drawing.swift:13:31: Cannot invoke ‘sizeWithAttributes’ with an argument list of type ‘([NSString : AnyObject]?)’
The type of attributes does match what is expected in sizeWIthAttrIbutes. After trying a number of work arounds I am unsure what the problem is. Any ideas would be most appreciated.
[code]extension NSString {
func drawCenteredInRect(rect: NSRect, attributes: [NSString : AnyObject]?) {
let stringSize = sizeWithAttributes(attributes)
let point = NSPoint(x: rect.origin.x + (rect.width - stringSize.width) / 2.0,
y: rect.origin.y + (rect.height - stringSize.height) / 2.0)
drawAtPoint(point, withAttributes: attributes)
}
}
[/code]
Regards,
Rob
You can fix this by changing the signature of drawCenteredInRect(_:attributes:) to take a [String : AnyObject]? instead of [NSString : AnyObject]?. The issue is that String and NSString can be cast between, but they are not literally the same thing in terms of types.
Does that make sense?
Yes, thanks. It works now. I had been staring at the dictionary type and was thinking that they (NSString & String) are equivalent. I’m curious though, in the documentation for the sizeWithAttributes the Swift version has it typed as taking [String : AnyObject] while Objective-C has NSDictionary<NSString *, id>. What I am wondering is if I am using Swift where is the cast between the String and NSString done and what is the overhead? Is a new NSDictionary created and populated from the Swift dictionary (iterating over each entry and casting each String element to NSString as the NSDictionary is populated) or is the dictionary as whole somehow just bridged between the two with no additional overhead.
Declaration
SWIFT
func sizeWithAttributes(_ attrs: [String : AnyObject]?) -> NSSize
OBJECTIVE-C
- (NSSize)sizeWithAttributes:(NSDictionary<NSString *,
id> * nullable)attributes
Insert a grain of salt because I haven’t looked at the disassembly (and real documentation on this sort of thing is hard to find – best bet is offhand remarks in WWDC talks), but in this case I believe there is zero or negligible overhead. String and NSString have the same representation in memory, so no work is needed, and though I am less certain, I think roughly the same is true for Dictionary and NSDictionary.
My understanding is that there can be a performance hit when downcasting a large collection, since Swift has to check each value as it performs the cast. In this case, since you are casting to ObjC types, and all ObjC cares about is whether it is an object reference, there isn’t much to check, so it may very well be that no work is done (no overhead).