About

This is the M Cubed Software weblog. To find out more about us head to our about page.


Search


Feed

 Subscribe (Atom)


Archives

New Objective-C Features [UPDATEDx2]

Posted on 25/06/2010 at 08:54 PM in Coding

Prior to Apple acquiring NeXT, Objective-C had had relatively little added to it since it became a fully fledge language rather than simply a C preprocessor back in the 80s. About the only thing added for many years after Apple acquired NeXT was the @try…@catch…@finally exception syntax.

Then in 2007 Apple released Leopard and with it Objective-C 2.0 and the modern runtime. This added features such as properties, fast enumeration, class extensions and garbage collection and improved areas such as protocols, while making the runtime far more flexible and less fragile to change. Last year with Snow Leopard, Apple added blocks and associative storage to the language in what can sort of be thought of as Objective-C 2.1.

At WWDC Apple announced a few more features, which I personally think of as Objective-C 2.2. These are "@synthesize by default" and "declare ivars in class extensions". These are features that I have wanted for quite a while and I am sure a lot of other Objective-C developers have wanted. So what are they and how do you get them?


Getting the Features

To get them there are two requirements:

  • Your app must use the modern runtime (so no 32 bit Mac apps)
  • You have to use LLVM 1.5 or higher as your compiler

If you fulfil those requirements then you're almost able to use them. All that is left to do is add these two flags to your "Other C Flags" build setting:

  • -Xclang
  • -fobjc-nonfragile-abi2

So now we're all set up, what the hell are these features?


@synthesize by Default

With the addition of properties, Apple was able to remove a lot of boilerplate code from our classes. If you are using the legacy runtime then you are able to write code like this:

==========MyClass.h==========

@interface MyClass {
    NSString *foo;
}

@property (copy) NSString *foo;

@end



==========MyClass.m==========

@implementation MyClass

@synthesize foo;

@end

It is a lot cleaner than what you had to write pre Obj-C 2.0, but it still has a lot of stuff to write. If you are using the modern runtime (ie are writing an iOS or 64 bit only Mac app) then you can get rid of the instance variable:

==========MyClass.h==========

@interface MyClass {}

@property (copy) NSString *foo;

@end



==========MyClass.m==========

@implementation MyClass

@synthesize foo;

@end

That's a bit cleaner, we're only having to sort out our property in two places rather than three, but it's still one too many. 95% of the time, you just want to synthesise the methods and instance variable and so the vast majority of property code in your implementation is the same. Well as you can probably guess, "@synthesize by default" means that this is done for you by the compiler by default. If you want to override it with an @dynamic statement or link it to a different instance variable you can, but for the rest of the time you can reduce your code to this:

==========MyClass.h==========

@interface MyClass {}

@property (copy) NSString *foo;

@end



==========MyClass.m==========

@implementation MyClass

@end

There is one slight gotcha with this. Due to what seems to be a compiler bug, you will get an error if you try to directly access the synthesised instance variable. Unfortunately the only ways to fix this are to either add @synthesize, add the instance variable or only access it via the property methods.

[Update] It seems that this isn't a bug but is instead expected behaviour. The @synthesize methods are generated at the end of the class if you don't specify them explicitly, as the compiler can't tell if the properties need to be default synthesised until it has got to the end of the implementation.

[Update 2] And now it seems the LLVM/Clang team are going to try and work around this and get it working as you would expect. Three cheers for the LLVM/Clang team!


Declare Instance Variables in Class Extensions

So, we have got rid of the instance variables for our properties, but these are public anyway, it's not that big a deal if developers can see them in the header. However, what is left are the private instance variables, the ones only used internally in a class. We can hide methods we don't want as our public interface in our implementation file, but not our instance variables. Instead we end up with this:

==========MyClass.h==========

@interface MyClass {
    id internal1;
    id internal2;
}

@end



==========MyClass.m==========

@interface MyClass ()

- (void)internalMethod;

@end


@implementation MyClass

- (void)internalMethod {
    NSLog(@"%@ %@", internal1, internal2);
}

@end

Thanks to Objective-C 2.0 and class extensions we can put our internal method declaration into our .m file and also have the compiler check that it is implemented and warn us if it isn't (unlike if we added a simple category). What we haven't been able to do is do the same for our instance variables. Well we can now, so our code can now look like this:

==========MyClass.h==========

@interface MyClass
@end


==========MyClass.m==========

@interface MyClass () {
 id internal1;
 id internal2;
}

- (void)internalMethod;

@end


@implementation MyClass

- (void)internalMethod {
 NSLog(@"%@ %@", internal1, internal2);
}

@end

Our internal instance variables really are internal now, which makes the header effectively become purely our public interface. And if you are feeling really crazy, you can have multiple class extensions:

==========MyClass.h==========

@interface MyClass
@end


==========MyClass.m==========

@interface MyClass () {
 id internal1;
}

- (void)internalMethod;

@end


@interface MyClass () {
 id internal2;
}
@end


@implementation MyClass

- (void)internalMethod {
 NSLog(@"%@ %@", internal1, internal2);
}

@end



These may only seem like small enhancements, but they help you reduce the code you need to write and make that which you do much neater. It is little enhancements like this that keep trickling out that makes the life of an Objective-C developer that much easier.

(10) Comments







The End of the Tinkering Geek (aka Closed is Good)

Posted on 02/04/2010 at 07:07 PM in General

There has long been a grip on digital technology, so tight that it has choked much of it. It is the grip of the tinkering geek. Slowly though, this grip is being loosened and the tinkering geek is becoming less and less relevant to technology and to society. And I say good riddance.

The tinkering geek, by definition loves to tinker. They want to be able to open something up, hack it together, use it for something for which it wasn't originally intended. The tinkering geek wants to run Linux on a toaster or use their iPhone as a remote for everything in their house. The tinkering geek is also who designs and makes a lot of digital products.

Now you may think that these people are the sort of people you want working on designing and building this stuff. They know the subject, they have interesting ideas, they know what they want. The reality is these are the worst people to design anything. For all that they do know, they don't know the most important thing: what does a regular person want?


Closed is good

We are brainwashed from childhood that open is good, choice is good, freedom is good. And yes, to a degree all of those are good. But too much openness leads to unhappiness, too much choice leads to confusion and too much freedom leads to anarchy. There's a reason I don't have the freedom to walk down the street killing everyone I see.

Closed isn't inherently bad. Design by committee is open, design by a single person is closed. I can guarantee that the design by committee will produce a worse product. Open is good in some places and bad in others. Lets take software for example. Tell me a good piece of open source software that is a backend product eg server software, framework etc. There are 100s out there that are best in their class. Now tell me a good piece of open source software aimed at regular people. Not all that many.

Closed also leads to things that open can't. Apple's ecosystems are often considered closed. OS X only runs on Macs, iPhone OS only runs on iPhone, iPod touch and iPad. Compare this to Windows or Android which runs across 1000s of hardware configurations. Well, if we do compare then we'll see that Apple's ecosystems are generally more stable because they have more control.

One example of a closed ecosystem in our products is Code Collector Pro and codecollector.net. By controlling the whole stack we will be able to implement stuff faster than competitors and even add some things that others aren't able to do at all. If we chose to be completely open and let it work with everything, it is more work for us, more code (increasing the chance of bugs) and doesn't really do anything quite as well.

Now I'm not saying everything in the world should be completely closed. I believe that some things need to be open, but most of these are things for people creating products to take advantage of, not for users to care about. A user doesn't care if a file format is an open standard. What they do care about is "can I save this at work and open it at home" and "will I be able to open it in a few years time".


Choice is bad

There was a great TED talk a few years ago on the "Paradox of choice" by a psychologist called Barry Schwartz. You should really watch the full video, but in a nutshell he points out that choice makes people unhappy and reduces your freedom. You don't want to spend choosing, you want to spend time doing. The best way to give users what they want is to have a few distinct choices rather than a large range of slightly different choices.

Apple is a prime example of this. Say I want a computer, here is the general thought process:

  • Do I want a desktop or laptop? Laptop
  • How big a screen do I want? 13"
  • Do I care most about power, price or portability?

To the last question if you say power you get a 13" MBP, if you say price you get a MacBook and if you say portability you get a MacBook Air. Yes there is then the case of choosing which model, but again that comes down to a question of price vs power. The rest is just tweaking.

You want to take away the pressure of choice, you want to choose for them. 99 potential customers may want A and 1 may want B. Ignore the one that wants B. Don't even give users the choice between A and B. Sure you've made 1 person unhappy, but you've made 99 people happy.

The only time you should give the choice to your customers is if there is a really significant minority. Say it was 60 people wanted A and 40 wanted B. That is when you should consider whether adding the choice between A and B outweighs having 66% more people buy your product

The problem is that many people who make the decisions about whether to add a choice are tinkerers and often think "we'll I'd like to tinker around so others might". This leads to the technology we have today which does a hell of a lot of stuff, but which most people don't care about.


Tinkering Is Vital

Bullshit. I hear this a lot and that is all it really is. Lets look at 2 quotes from a post by Cory Doctorow on BoingBoing.

The original Apple ][+ came with schematics for the circuit boards, and birthed a generation of hardware and software hackers who upended the world for the better. If you wanted your kid to grow up to be a confident, entrepreneurial, and firmly in the camp that believes that you should forever be rearranging the world to make it better, you bought her an Apple ][+.

The idea that having a product that you can tinker with is what allows you to be creative or confident is silly. Technology is a tool. Knowing what is in it and hacking around with it is something a small minority have any interest in or find useful. Far more people care about using tools than what makes them.

Sure, tinkering can be fun to many people, but making tinkering easier for a few could make it a worse tool for everyone else. Lets take the example of the iPad. It could be made easier to tinker with, if you added screws to it to take it apart. However, adding screws increases costs, reduces aesthetics and, if you are also going to lay things out best to those wanting to tinker, could increase the size of the product. How many people would want the ability to take something apart over a cheaper, smaller more aesthetically pleasing product? VERY few.

Buying an iPad for your kids isn't a means of jump-starting the realization that the world is yours to take apart and reassemble; it's a way of telling your offspring that even changing the batteries is something you have to leave to the professionals.

This is just crazy talk. Sure, you could allow user replaceable batteries, but again like above it leads to a worse product. You have to add a mechanism for the battery to go in, to keep it in place and to encase the actual battery itself. This leads to more weight, more parts so more chance for failure and higher costs and also less space for the actual battery, so lower battery life. Now how many people would choose an easily (because you will be able to do it yourself if you really wanted to) user replaceable battery over a lighter, cheaper, more reliable product with greater battery life? Again, VERY few people.

Now let's think about something fundamental to economics: what are we buying? Contrary to popular belief, we are rarely buying a product. What we are buying is experience and knowledge. When I buy a mobile phone, I am paying for the experience and knowledge of the people that designed it and assembled it. I could, if I really wanted go and design and build my own. But my time is worth more to me than that.

An even better example is visiting a doctor. Why do I go and see a doctor rather than treat myself? Because they have done 5+ years of learning and experience. They know a hell of a lot more than I do. Sure I could go on the internet to find out why I'm ill, but is it really worth it if I'm wrong?

People are willing to pay for stuff that makes their life easier, that means they don't need the knowledge and experience themselves. This is why people pay a mechanic to fix their car or pay a chef in a restaurant to cook a greta meal or pay a company to replace a battery.


Tinkering doesn't matter

As much as some people like to try and convince you that tinkering is key to our freedom, our progress and our existence, they really mean "tinkering is key to my hobby". You don't need to tinker to learn something. Most of my learning was done through reading, watching and listening. Sure it is a way to learn, but it is far from the only way.

People who want to tinker will find a way to tinker, even if it is harder. I can still open an iPhone if I want to, I can still install different software. It just isn't easy and for that I am grateful, because it means that what came out of it was a product that I care less about how it works and more about what I can do with it. The sooner we get more products like this, the sooner we can have technology that is made for the majority, not the minority of tinkering geeks.

(13) Comments







Code Collector Pro 1.4

Posted on 31/03/2010 at 03:42 PM in Code Collector

511 days. That is how long it has been between Code Collector Pro 1.3's release and the release of 1.4 (the geek inside of me now makes me wish I'd waited until tomorrow for a nice round 512 days). Yet despite this large amount of time, CCP 1.4 is admittedly lacking in new features. It has a lot of user interface re-design and bug fixes but in terms of new features, the most significant is probably being able to see the dates a snippet was added and last modified.

So why has it been so long between the two releases, and why has there seemingly been so little changed? This blog post is to answer those two questions.


Why so long?

Put simply, finishing my Computer Science degree got in the way. I release CCP 1.3 in November 2008. At this point my first dissertation deadline was approaching fast and so after I released 1.3 I had to focus on that (my dissertation contributing to over a third of the marks for my entire degree). My final deadline for my dissertation was late-April 2009 and from then until the end of May I had exams to revise for and take. Finally, come the 28th of May 2009, I was free and able to go full time with M Cubed.

But wait? That was 9 months ago, what have I been doing in the intervening time? Well first off I joined up with Fabio to start the design side of M Cubed which required management. And also I had to work on Minim 2.0 which was a complete re-write. Both of these required more time than I was expecting at first and so it was January before I could start work on CCP 1.4, which then took longer than I was expecting itself.


Why so little?

So why is there so little new feature wise in CCP 1.4? Well, put bluntly the code in 1.3 sucked. I have talked about why in a previous post, but in a nutshell it had a lot of 10.4 era code and was designed by someone much less experienced in software than me: me circa 2 years ago.

After hardly touching the code for over a year, when I came back to it I found I needed to re-write lots of it. In fact about 60% of the code in CCP has either been re-written or moved around between 1.3.6 and 1.4. A lot of old code was deleted with more modern alternatives put in its place. Essentially this re-write will make it easier to add newer features in 1.5 and 2.0. So while it doesn't add much now, it will allow me to add better features in the future at a faster pace with hopefully fewer bugs.


So make sure you checkout Code Collector Pro 1.4. While there isn't much new, there is a lot of stuff that has been massively improved. There's also a screencast showing some features that have been around for quite a while, but many people don't seem to know about.

(0) Comments







Building a Modern Cocoa App

Posted on 04/03/2010 at 06:13 PM in Coding

A while back I wrote a post on how I was pushing towards making my apps much more manageable, by separating my once monolithic app delegates and nibs into various view and window controllers. Yesterday Justin Williams wrote a post on his blog about Getting Started with Core Data, Bindings and NSViewController.

Cocoa has changed a lot over the years. I started programming on 10.3, when Xcode was a version 1.0 and Bindings, KVO and KVC were the new hotness. Over subsequent releases we gained Core Data, Objective-C 2.0, NSViewController, Garbage Collection and much more. I strongly believe that any modern Cocoa app should be using Core Data, Objective-C 2.0, Garbage Collection and NSViewController. Bindings should also be used where appropriate (I'll define where I think this is later).

Justin's post included a project he'd worked on, implementing core data tutorial application from CocoaDevCentral using several window and view controllers rather than one monolithic class and nib. The way he built his version was quite interesting, as it was differently to how I would have approached the task. As it was a relatively simple project, I thought it would be of benefit to some to provide an alternate way of building the same app. I don't think there has been two Cocoa developers giving two different ways of implementing an entire app before.

You can download the source for my version here. I also recommend downloading Justin's version. I'll first outline how Justin's implementation works and then go into how my implementation works and how it differs from Justin's.


The Application

But first it would make sense to outline the application. It is a simple blogging app. It consists of a main window containing a splitview. On the left is a list of posts, with an add button below. On the right are the details of the current post: the title, author, category and body. The author and category are chosen from a list which can be edited by clicking the edit button next to the field. This brings up a panel allowing editing of the author and category lists.

Main window

The data model is as shown below:

Data Model



Justin's Implementation

Justin has structured the application as 3 window controllers, 2 view controllers and the app delegate:

  • MDVCAppDelegate contains all the code to initialise the Core Data store and the main window controller. It also inserts the initial authors and categories if the database is empty.

  • MDVCMainWindowController is the window controller for the main window. When the window is loaded it initialises the view controllers and adds their views to the splitview, and sets up the frame and autoresizing mask.

    Perhaps the most interesting part of the whole implementation is that it uses a single NSArrayController in the main window's NIB, which is then passed to the view controllers when they are initialised. This array controller is in entity mode and bound to the managedObjectContext on the application's delegate. The add button's action is connected to the array controller.

  • MDVCPostsListViewController is a very light weight class. It just handles initialisation and pushes all the handling into the NIB. The table column is bound to the array controller that was passed in at initialisation

  • MDVCPostDetailViewController is almost identical to the posts list view controller, except that it includes methods to show the authors and categories lists. It uses the selection on the passed in array controller to fill in the details. The NIB also contains array controllers for the authors and categories lists, which are bound to the app delegates managed object context.

  • MDVCCategoriesWindowController is just a singleton that sets up the main window. Again, most of the logic is pushed to the NIB with an NSArrayController handling everything.

  • MDVCAuthorsWindowController is identical to the categories window controllers, except that it handles authors

And that is all there is to Justin's implementation. It is very simple and most of the processing is pushed off to Core Data and NSArrayController.


My Implementation

I built my implementation with the same structure as my 3 existing apps. Rather than relying on core data and bindings completely, I use KVO manually and have a model controller that wraps around core data. I have a similar set up in terms of controllers: 3 window controllers, 2 view controllers and the app delegate. But I also have 9 extra classes, my model controller, 2 classes to simplify core data and 6 model classes generated by MOGenerator.

  • M3MasterDetailVC_AppDelegate handles all the window controllers. In this case, all the window controllers are application global. As such I prefer to put the code handling them into the app delegate. You may be wondering how the editAuthors: and editCategories: methods are called, and all will be explained later in the post.

  • M3MainWindowController holds and mangages the view controllers. It initialises the view controllers in its init method and adds them to the split view in awakeFromNib. The two subviews of the splitview are connected to IBOutlets. The code for creating a new post is also in here, as I'm no longer relying on bindings for adding and removing objects.

    There is also the first bit of KVO usage. The window controller observes the selectedPost property on the posts list controller. When this changes it sets the post of the details controller to the newly selected post.

  • M3PostsListViewController provides 5 important methods. The first returns an array of posts from the model controller. This is used by the array controller in the NIB as its contentArray. The array controller is this time used in class mode, rather than entity mode, with the contained class set to M3Post. The reload data method tells KVO that the posts array has changed, which causes the array controller to refresh.

    The selected post method returns the post object that is currently selected in the array controller, if there is one. Conversely selectPost sets the selected post on the array controller. And finally there is the tableViewSelectionDidChange: delegate method, which informs KVO that the selectedPost changed whenever the table selection changes.

  • M3PostDetailViewController only contains code for observing and returning the list of authors and categories. Most of the logic is in the nib, where an NSObjectController represents the selected post. Other than this the NIB is almost identical to Justin's version

  • M3AuthorsWindowController contains code for listing, adding and deleting authors. The authors are displayed in the UI via an NSArrayController whose content array is set to the authors array. The add and delete actions are connected to the class which calls the model controller and invokes the appropriate KVO methods.

  • M3CategoriesWindowController is identical to the authors window controller, except that it deals with categories

  • M3ModelController is the main difference in the two implementations. It provides a single focus point through which all data creation, deletion and retrieval flows. While this may seem like overkill for this application, where bindings are pervasive, it is a very useful thing to have when you do a lot of work with data that doesn't involve bindings.

    The init methods sets up a core data manger and a simple core data instance. It also checks if the database is empty (there are no authors) and if so inserts the initial content. The rest of the methods just deal with returning, creating or deleting objects.

The following two classes I group together as M3CoreData. They are classes I've built that make CoreData cleaner and simpler to access.

  • M3CoreDataManager is where all the CoreData initialisation code that often resides in app delegates is pushed off to. It doesn't do anything special, it just makes life cleaner by pushing all the code you don't care about off into a class and putting the bits you do care about into an initialisation method.

  • M3SimpleCoreData is where I encapsulate the code required to add and retrieve objects with core data. This code is very samey and very tedious and there's no good reason to write it every time.

And finally there are the 6 classes generated by MOGenerator. I've learned to swear by MOGenerator for my core data applications, it is removes all the hassle that you would otherwise have if you used Xcode's built in tool for generating model classes for core data entities. The only change from the generated code is an awakeFromInsert method in M3Post that sets the creation date.


Responder Chain Hacking

One thing I like to do is abuse the responder chain (please don't call the police!). In larger applications I make sure every view and window controller on screen is in the responder chain. This has the advantage of being able to put methods where they belong, rather than where you can access them. For example, the newPost: method is in the window controller. In some of my apps I have it in the side bar controller, because that is all that cares about adding a post.

You can then connect you actions to the first responder proxy in the NIB. It will traverse the responder chain and find the appropriate class and then invoke the method. You can then control whether the method can be invoked by overriding respondsToSelector, which will give you menu validation control.

This is how the editAuthors: and editCategories: methods in the app delegate are called. The buttons in the details view are connected to these methods via the first responder proxy. As the app delegate is in the responder chain it will be the one receiving the methods (as nothing else futher up the chain implements them).


Where To Use Bindings

Bindings are a contentious issue. Some people love them, some people hate them. The fact is that they are incredibly useful and can save a LOT of code. To give one example, the new inline inspector in CCP 1.4 was initially all handled in code. Unfortunately this required a lot of logic for handling no selection, multiple selections etc. In the end I just replaced the code with bindings to an NSArrayController and managed to delete around 100-150 lines of code.

But bindings aren't useful in all cases. Over the years I've kept changing how I use bindings. I used to use them everywhere, then went through a phase where I didn't use them at all, but have now settled on a good middle ground, where I use them where they work best. So where is that?

  • Preferences - bindings are ideal for handling your preferences. Most preferences are just booleans, strings or integers, handled by checkboxes, text fields and popup lists/radio buttons. These can be bound directly to the shared user defaults controller and remove the vast majority of code from your preferences controller.
  • Forms - this is sort of a superset of the preferences use case. Anywhere that you have a form for displaying and/or editing data on an object (or especially in cases of multiple selections), you should use bindings. It saves on a huge amount of code and it handles all the issues of editing multiple items at once, populating pop up lists etc
  • Simple Tables - I have found that any sort of simple table should be implemented using bindings. By simple I mean ones that don't require any complicated logic in the data source. If you just have an array of objects and you want to display their properties in a table, use bindings, it is much simpler. However, do NOT use bindings for outline views. NSTreeController is evil and should be avoided like the plague. Consider this a public service warning, just avoid it and save yourself a lot of pain.

Use New Tech, It's There For A Reason

There are a lot of people who avoid some of the new technology in Cocoa. Some don't like Core Data because it seems like voodoo (if you learn a little bit more you'll actually find that it is quite simple, yet incredibly powerful) and some dislike Garbage Collection because of experiences with other garbage collectors (in reality the GC in Obj-C not only saves you the pain of memory management but also of threading, while also potentially being faster than a retain/release system).

You should always be looking at the new technology that comes in each release of Cocoa, most of it can save you 100s, if not 1000s of lines of code if you can adopt it. It can also make the code you do have much easier to manage. It seems odd that most people would agree it is insane to write and use an alternative to something like NSTextView, yet will write and use an alternative to some more modern technology in Cocoa.

(5) Comments







Page 1 of 26 pages  1 2 3 >  Last »