Broken MVC?


#1

Hi,

More of a philosophical question here - I use a number of different OOP and graphic systems (most notably Java) and I see a very distinct difference in design philosophies. Here is my thinking…

An NSString is a model - data. You can create and manipulate it quite independently of any view - be it on an old fashioned line printer, a computer display, iphone, or more esoteric: database store or even sound. One way of looking at it - models should not even know they have a view.

However, when writing text to the hypnosis view we send the text the message “sizeWithFont:” and “drawInRect:withFont:” meaning that NSStrings have to know a lot about the graphics context that it is in, something a sound or database string would not.

To my way of thinking, it would be more natural to have

[font sizeOfString: text]

and

[context drawString: text inRect: rect withFont: font]

Looking at the documentation for NSString, I see that these methods are not a part of the original definition of NSString, but have been added by the UIKit - a design decision that I don’t fully understand. I’m not trying to change the way Apple development is done, just trying to understand the motivation behind the design of the system.

Thanks

Dave


#2

Good question.

The short answer is “this is the way Apple chose to do it, so you must do it this way.” But that answer isn’t very fulfilling.

So…

In regards to having the font object determine the size given a string parameter: the font object is a model object as well. Regardless, the implementation of finding the size of a string given a particular font would rely on pulling data from both of these objects; the object that does the actual work (i.e., the one that has implemented the method to perform this task) will be doing the same thing, the only difference is which object’s ivars are accessed directly and which are accessed via an accessor method.

Now, in terms of the context object doing the drawing versus the string object doing the drawing… the context object is actually doing the drawing here. These NSString category methods actually make Core Graphics function calls, like CGContextShowText, CGContextShowGlyphs, etc. The Core Graphics routines are fairly “low level” and they are not easy to gather the correct parameters for. Therefore, the UIStringDrawing category (which contains the drawInRect:withFont: method) was added to NSString so you don’t have to do that. You have a string, you want to draw it, you invoke one of the UIStringDrawing methods, and all of the Core Graphics code is written for you.

In effect, UIStringDrawing is a set of convenience methods to help bridge the gap between NSString/NSFont and Core Graphics. The UIStringDrawing additions do not fall exactly into what an NSString is (a model object representing a string of characters), but they are very strongly related to the process of drawing a string and rely on the existence of an NSString. Also, because the Core Graphics library is C, it cannot be extended via a category nor would it even understand what an NSString is (since NSString is an Objective-C object). Therefore, the decision was made to add these gap-briding methods as a category of NSString.

This is a fairly common practice with model objects that can be represented in some other context. Apple puts these convenience methods onto the object itself, and in practice, it works out rather well. Another example is UIColor. UIColor has an instance method, set (also: setStroke, setFill) that does something very similar. When you send the message set to a UIColor instance, it ends up calling CGContextSetFillColor/CGContextSetStrokeColor with its instance variables that represent the red, green, blue and alpha channels it stores.

Hope that helps.


#3

Thanks Joe - it does indeed help to have a full detailed explanation.

I’m curious - since text -> speech is a much newer technology than text->pixels, I wonder if similar extensions were made to the NSString class or if the thinking has changed over the years. I’m just learning to search the documentation well, so if I find out the answer, I’ll report back (but it might take a while…)

Dave