I borrowed Cocoa Programming for Mac OS X from the library the other day. I’ve read most of it, away from the computer, although I plan to reread it in front of the iMac over the next few weeks. I’ll write a review of it a bit later, but it reminded me that I still have a memory leak in iTunesRater. Also, I think I’ll rewrite iTunesRater as a Cocoa Objective-C application, rather than AppleScript Studio. Then I can just use an AppleScript message to get/send data, rather than relying on AppleScript for interface updates. That way the application may be a bit snappier. And hopefully I’ll overcome the memory leak. I was actually thinking of a refactor anyway, as there’s a whole heap of semi-duplicated code in there.
While I’m in the “learning a new language” mode, I thought I may as well start listening to the podcasts I’d downloaded from UC Berkeley, from the well-regarded CS61A course, and the associated textbook The Structure and Interpretation of Computer Programs.
The textbook is also freely available, including in a handy PDF version, and I’m working through that too.
The language used is Scheme, which is a Lisp derivative. It actually looks a bit like Objective-C in the way functions are called. Thus you see stuff a bit like:
(+ 5 6)
(solve 4 65 3)
That is, a prefix notation is used. The function (and operators, such as +-/* are also functions) comes first, followed by the argument(s). Compare this to Objective C:
[celsiusTextField setTextColor: [NSColor blueColor]];
In Objective C, the object comes first, then the method, then the argument(s). A different structure appears where there are multiple arguments:
[object methodName: argument1 secondArgName: argument2]];
I’m still struggling to think back to my C days, but there are lots of things about every language I’ve used since python that I don’t like. Memory management, pointers, static typing, there just doesn’t seem to be any reason to have to worry about this shit.
Probably the biggest thing for me is that it is exciting me about study next year. Even if it will be learning Java, and putting up with rubbish all over again…
I’m going back to Uni next year, to complete a post-grad degree in Computer Science. This is a course that people without a background in Computing can enrol in, so I’ll be able to get a bit of credit towards some units, but not too much, since when I did part of a Computer Systems Engineering degree, Java wasn’t the language taught. Now, at all three Universities in Adelaide, it almost seems to be the only language being taught.
Just for my own personal gain, I’m also teaching myself Objective-C. This is the language used by Apple and third-party coders for most of the OS X application software. All of their APIs are available under Objective C, and Java, whilst still somewhat supported, doesn’t appear to be as high on Apple’s agenda as it is on the local Universities.
I’ve borrowed books in the past on Cocoa/ObjC, but never really made much progress. This time, having a laptop means I can actually do my learning on that, wherever I happen to be. In this coffee shop, for instance.
I’ve also found some handouts and example program “tests” at Stanford University, and have started to work through those. The first program, a simple conversion application for temperatures, was fairly simple, and I wrote that. I even managed to complete the extra credit sections, or at least two of them.
Psuedo-evidence (in that you can’t actually see this working):
(Note, the temperatures change to red when above a certain value, and to blue when below a certain value. Within the ‘normal’ range, they are black).
This wasn’t a programmatically onerous task, more about making sure you can connect up various components and have them affect one another.
Instead of doing the next task, I’m pretty confident I’ll be able to write the application I gave to my Year 12 students, a 9-letter word puzzle solver. It will be interesting to see if I can effectively re-implement the python version I’ve already written:
The only reason I really want to learn Obj-C is that it enables me to use a GUI design tool, rather than having to create the interface using coordinates. And packaging up python applications isn’t exactly fun, either.
And, after the 9-letter puzzle solver, I might reimplement a sudoku solver I wrote. Not sure if I have the python source code for that one. And I never wrote a GUI for it, either.
I bought a copy of this fantastic textbook on Friday.
I won’t write a review, suffice to say it is the book to use to learn how to program on the Mac.
Well, I’m learning stacks about Cocoa/Objective C. Quite a bit of what I’m doing is trial and error, and I still haven’t got the hang of retain/release. But I do have a couple of working programs.
The first one is an RPN calculator. Typing in or pressing buttons enters numbers, and the right arrow pushes them onto the stack. The exchange button swaps the top two items in the stack, the roll button moves the top item to the bottom.
When you press one of the operator keys, then it operates on the current data and the top item in the stack, or the top two items if there is nothing in the current data.
And this is all working very well. The calculator functions work as expected, and as described. However, the stack display in the NSTableView does not work. For some reason, whilst the data is stored, it isn’t being displayed.
A little more detail about that. The data is being put into the table, and I can select the X number of items that are in the stack. But they do not display. It is like the data is there, but just not being painted! And it is annoying the hell out of me!
I’ve done a heap of testing, and I can kind of describe what is not happening. There are two functions that need to be implemented by a dataSource, both of which I have implemented.
1 - (int)numberOfRowsInTableView:(NSTableView *)aTable; 2 - (id)tableView:(NSTableView *)aTable 3 objectValueForColumn:(NSTableColumn *)aCol 4 row:(int)aRow;
These are the interface definitions. The implementations are simple - they just use the data stored in an NSMutableArray, and return the size of the array, and the (nth) item in the array.
But the problem is the second one does not appear to be actually being executed. The first one is, each time I call
[stackView reloadData]. And I have no idea why the second one, which actually puts the data into the table, isn’t.
I’m giving up, for now. At least I have (mostly) implemented my 9-letter puzzle solver:
Although, it would be kind of nice to be able to use an NSTableView here, too. Although I might finish the implementation using an NSTextView, and style the 9-letter words differently. Just to totally make it the same as the wxPython version I wrote.
I’m currently rewriting iTunesRating to allow for half-star ratings. This is a program that sits in the menubar, and displays the rating of the current song, and allows it to be changed. I’ve learned a lot about how to create a NSStatusItem already tonight, and I’m working on getting the AppleScript interface to properly with it. It builds a bit on iTunesRater, a program I wrote in AppleScript Studio, which allows for the same thing, although this will have a much smaller screen footprint. If only I could get hold of the original source code - I could fix it in a few seconds, I suspect. More updates later.
Arse Kickin’ Lady From The Northwest • Tim Rogers And The Twin Set • What Rhymes With Cars And Girls ★★★★½
It’s not possible to directly hook up an NSSegmentedControl (or NSSegmentedCell) to a Core Data controlled NSArrayController (or any, for that instance). I wanted to be able to use one of these controllers to allow the user to select one, many or all items from the array.
To define the behaviour a little bit more: You can select the first item in the control (which will be called “All”), and it will select or deselect all of the other items, depending on what it’s state becomes. If you click on another segment, then it will toggle the selection of that segment. If all segments are ON after pressing a segment, then the first segment must also display ON, otherwise it will be OFF.
I did it by creating a new class: SegmentController. (I know, I should put on a prefix…)
SegmentController has two required IBOutlet variables, which refer to the NSSegmentedControl, the NSArrayController, and a property NSMutableIndexSet, which stores, sets and gets the selection indexes (I think this should be indices, but I’ll stick with Apple’s convention). I also have a sortDescriptors array, so that this object can sort the array controller.
There are a couple of limitations, which I might look at how I deal with - for instance, the NSSegmentedControl must have exactly one more segment than the NSArrayController has items. I’m using it to select one or more days, and this number doesn’t change. It should be possible to dynamically create the right number of segments, and populate them with values (I even have a readonly field called shortName set aside in my Day objects for this), but at awakeFromNib the NSArrayController is still empty, and I can’t figure out a nice neat way to force a fetch.
Hooking up the elements in Interface builder is easy. Create the NSArrayController, and set that up however you need to. Do the same with the NSSegmentedControl - at this stage you’ll need to put values in each of the segments. Now, create a new instance of SegmentController (you might need to add the class files to your XCode project first). Connect the two outlets up to the required objects.
Now, in the Bindings Inspector, connect up you’ll want to make the Selection Indexes parameter point to the selectionIndexes model key of your Segment Controller object.
Finally, make the selector of the NSSegmentedControl point to the selectSegment: action of the Segment Controller.
I did notice a slight delay between deselecting one segment and the “All” segment deselecting. A simple optimisation - moving the call to segmentCount out of the loop test and into a local variable made it much smoother.
That should probably do it. It might need a bit of tweaking. You should be able to have multiple instances of this in your project, if it is required. It’s been designed that you’ll need a seperate one for each Segmented Control, as it refers to the objects it is connected to, rather than the sender of the message.
My source code is here: SegmentController.zip
Three languages. Three different Object Relational Mapping systems. One operating system.
Over the past couple of days, I’ve been madly learning how to create programs in Core Data, using Objective C and Cocoa. It’s given me plenty of food for thought, and made me perhaps think that it’s not that SQL Alchemy rocks my world, it’s just that J2EE/EJB is just ORM done wrong.
I actually got exposure to SQL Alchemy in detail before J2EE - I first came across them at the same time, but using SQL Alchemy was at work, and I basically had to learn in 2 days what a full semester worth of J2EE taught me. Perhaps that was just because learning the python stuff was so damn easy.
Now that I understand just how cool an Object Relational Mapping is, getting into Core Data was easy. Creating the same schema in SQL Alchemy (actually, using Elixir, so it was just object creation), and then in a Core Data xcdatamodel - basically the same process, was even simpler in Core Data: simply because it is a GUI tool, and you can see the whole model in one go, instead of having to scroll through a text file examining classes.
But doing GUI programming using the ORM is where Core Data really shines. Using Cocoa Bindings, you can just plonk down an NSTableView, and tell the object where it gets it’s data from. If you have two GUI widgets using the same data model, and you change selection in one, it even changes the selection in the other!
Core Data also helps look after Undo, Saving and all other sorts of goodness I haven’t even come across yet.
The only thing that Core Data isn’t good for is a multi-user system - or more precisely, a system where multiple users are accessing the data at the same time. I’ve used SOAP as the messenger format, where I had a rich client accessing services provided by my server, but this is cumbersome. I’ll just use Pylons, or perhaps Django to do a web application - where the interface is largely a Web Browser. In this instance, it will probably be best to just stick to a python-based approach. I’m tempted by WebObjects, but that would still require me to use Java.
If only Apple would release a distributed Core Data. I might be able to do something kind of cool with Distributed Objects, for a rich client, at least, but for Web, I may as well do the whole thing in python.
I’m currently learning stacks (and maybe developing a useful application along the way) about Core Data and Cocoa Bindings.
I managed to hook up all of the “first level” Entities to UI elements, but was struggling a bit figurng out how to get the to-many relationships to work.
To make all elements of the type Person appear in an NSTableView, you can create an NSArrayController, set it to the Entity type, and put Person in the relevant field. Then all people will appear in the list.
If you have a manager, who might supervise zero or more people, then how do you get a sub-set of People. More specifically, how do you get a just the other side of a To-many relationship.
I thought I was going to have to resort to actually writing some code, and use a Predicate, or something like that. But there is an IB only method.
You can create an NSArrayController, and as well as putting in the Entity type (and hooking it up to the Managed Object Context), just put that it gets it’s Content Set from another object - in this case it would be People.selection.supervises (or whatever it is called).
This isn’t quite flawless - if you just hook up a button to the “add:” outlet, then it doesn’t quite add properly - the reverse of the attribute is not set. I had to make up an IBOutlet in my window controller that creates a new component, and sets the relationship up.
Which meant I ended up having to write two lines of code. For each relationship.
Categories are pretty cool in Objective C. Say you have some special methods, which you’d like to have another class to have, or wish to override a method for every instance of a class.
You can, with every OO language, just create a sub-class. However, this would mean having to use the sub-class in all of the locations throughout your program.
With categories, you can just (re)define a method, and use the same class. All of your code will know that this new method is available, or will automatically use the overriden version.
This to me obviates the need which is apparent in most OO languages to sub-class the hell out of everything. This in fact is what I like most about Cocoa/Objective-C. Most of the time, you don’t need to sub-class the widget and other API classes. Instead, you have archived instances of them.