Monday, December 28, 2009

It will die: Accessing instance variables in init and dealloc

Michael, the guy behind the NSBlog, wrote a nice summery about the pros and cons of using accessors in -init* and -dealloc. He is right by pointing out the possibilities of certain side effects when using accessors in -init* and -dealloc.  On the other hand let's have a look at the near future. The Objective-C 2.0 language comes with a really nice new feature called Properties. Properties let you describe the - guess what - properties of a class very easily.

@interface Person : NSObject {
    NSString *firstName;
    NSString *lastName;
} 

@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
@end

Pretty easy stuff so far. But I don't know about you but I hate redundancy in my source code. I would love to be able to just write the following piece of code:


@interface Person : NSObject
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
@end


What happened? The instance variables are gone. But the cool thing is that you can do that. The code will compile assuming the usage of the 64-bit mode. Try it! Now let's face facts: Does Apple still build 32 bit computers? No. How long will it take until there are only 64 bit computers in use? A few years maybe. This all means that instance variables will die in the near future. Nobody wants to write redundant code. Now I am asking you: How to set/release an instance variable directly when you can't access it? You simply don't.

Friday, December 18, 2009

Atlas makes Objective-J/Cappuccino so much more fun

This blog post is about Atlas. Atlas is an IDE for Cappuccino/Objective-J applications. But first things first.

Objective-J is a new programming language developed by the guys from 280 North. The cool thing about Objective-J is that it is a real superset of Java Script which means that every valid Java Script code is also valid Objective-J code. You can also find this paradigm in the Objective-C world because Objective-C is a real superset of C. Now the coolness begins: The guys from 280 North are geniuses. They built an Objective-J interpreter with Java Script. Which means that every browser that supports Java Script can also execute Objective-J code without the need of plugins. The Cappuccino toolset also contains an ahead of time compiler for Objective-J code. By the way: Cappuccino is the application development framework developed in Objective-J.

That said it is now time to show you what the guy from 280 North built with Cappuccino and Objective-J. Open your favorite browser and go to http://280slides.com/Editor/. Yes, you are right. They built a Powerpoint killing web app by using Cappuccino and Objective-J.

I hope that, at this point, you are all ears to what I have to say about Atlas because if gets even better and cooler.

At this time the guys from 280 North have a cool programming language and an application development framework. Atlas brings it all together. Atlas lets you create new projects.



Once created a new project Atlas opens up a window that lets you select a file and edit it's content.



As far I know Atlas is using Bespin as an editor component. Cappuccino applications usually have an user interface. Atlas makes it really easy to create and edit user interfaces. By opening the Resources folder and then clicking on "MainMenu.cib" an interface builder opens up that let's you visually edit the user interface by drag and drop.



This makes developing rich web application front ends so much easier. One more extremely cool feature:  With Atlas you can target two platforms: A web browser and the real native desktop platform. By selecting "Native Desktop" in the platform popup menu and then clicking on "Build and Run" Atlas creates a real Mac OS X application for me: With its own window and icon in the dock.



Join the Atlas Beta Program today.

Thursday, December 17, 2009

Using Objective-C Categories: The safe way


A Objective-C developer can add methods to an existing class by using categories. Let's assume you ever wanted NSString to have a -reverse method that returns the receiving string in reverse order. "Cocoa" would become "aocoC". So you extend NSString by writing a category that implements the reverse method.

Interface:

@interface NSString (reverse)
-(NSString *) reverse;
@end

Implementation:

@implementation NSString (reverse)

-(NSString *) reverse
{
  NSMutableString *reversedStr;
  int len = [self length];

  // Auto released string
  reversedStr = [NSMutableString stringWithCapacity:len];     

  // Probably woefully inefficient...
  while (len > 0)
    [reversedStr appendString:
         [NSString stringWithFormat:@"%C", [self characterAtIndex:--len]]];   

  return reversedStr;
}

@end


The actual code has no problem. The problem is the name of the method added by this category. What happens if you use a third party framework that also adds a reverse method to NSString? Well, the behavior is undefined. To solve this problem I suggest that you simply prefix methods you add by using categories with the same prefix you use to indicate "your" classes. To give an example: I prefix my classes with CMK which means that I prefix methods that I add by using a category with cmk_. In the example given above this means that reverse has to be renamed to cmk_reverse. This technique is also used by Google and Jonathan Rentzsch (in his method swizzle code).

In general you should avoid adding too many categories. Use them wisely.

Monday, December 14, 2009

Last minute optimization and lazy fetching

This post is about how a tiny method saved me a lot of additional work. Maybe you know that I am the developer of Ebbinghaus. Ebbinghaus is a flash card application. It heavily uses Core Data. Like every good developer I didn't really care about performance issues too early. In fact I did test the performance of the whole application when it was done. I remembered the following quote.
Premature optimization is the root of all evil. (Donald Knuth)
The performance tests showed that Ebbinghaus had only one performance related problem. Let me describe the problem: Ebbinghaus lets the user create decks. Every deck can contain cards. The user is also able to create smart decks. They work like smart playlists you know from iTunes.



Filtering a lot of cards (+ 10.000) just was too slow. Scrolling the table view displaying the cards was not smooth at all. I asked myself how to solve this problem without having to write a lot of caching code myself. The solution was extremely easy. Maybe you already know: NSObjectController (and thus NSArrayController too)  has a method called -setUsesLazyFetching:. When setting -usesLazyFetching to YES the controller will try everything it can to improve performance by lazy fetching data. There is even a check box for this property so that you can set it directly in Interface Builder.


Checking this check box did solve the performance problem. I am still thrilled about this. Imagine what it kind of ugly work I had to do then implementing that for myself. Of course lazy fetching does only work  in combination with Core Data. So be a smart developer and use Core Data whenever you can.


Sunday, December 13, 2009

Using NSSet instead of plain old C-Enums and bitwise operations

In my first blog post I would like to convince you that using C-Enums and bit operations when writing Objective-C code is not such a good idea anymore. In order to convince you I will first show you how we deal with enumerations today. In the second act I will explain why the current handling of enumerations is bad. And at last I will show you a alternative that you can use to improve you enum and bit operation code.

Now, please have a look at NSView's autoresizingMask property. The autoresizingMask lets a developer specify how the view will change it's frame when it is being resized. When setting the auto resizing behavior you have create a mask using bitwise OR operations. Apple explains that in the documentation quite well:

- (void)setAutoresizingMask:(NSUInteger)mask

Parameters
mask
An integer bit mask. mask can be specified by combining using the C bitwise OR operator any of the options described in "Resizing masks".

The possible "values" that can be used to composite an auto resizing mask are also well documented:

enum {
   NSViewNotSizable     = 0,
   NSViewMinXMargin     = 1,
   NSViewWidthSizable   = 2,
   NSViewMaxXMargin     = 4,
   NSViewMinYMargin     = 8,
   NSViewHeightSizable  = 16,
   NSViewMaxYMargin     = 32
};

So, how does setting the auto resizing mask typically look like? Let's recap:

[aView setAutoresizingMask:(NSViewWidthSizable |
                            NSViewHeightSizable)];

One questions immediately pops up: Why does it have to be so complicated? There are good reasons.

  • Bit masks are usually very "small" and thus save memory.
  • Even though a resizing mask is nothing more than a single number it can describe nearly an infinite number of possible combinations. There is no need for setters/getters like -setHeightSizable:(BOOL)flag, ...

What is wrong with that? At the time Objective-C and Cocoa were first invented CPU speed and available memory were very limited resources. Thus using enumerations and bitwise operations to describe complex properties was a brilliant idea. But today, these concepts seem a little bit outdated, at least on the desktop platform. So let's improve that.

Let's try to describe to autoresizingMask-property in a modern way. The first step is to find an equivalent to a "mask". The most obvious equivalent is a simple mathematical set that is able to contain the possible mask values. Fortunately Objective-C (Foundation) does already know what a set is. A set is just an instance of NSSet. Brilliant. But wait! It is not possible to add plain old C objects into a NSSet. Well, we don't have to. Instead of assigning the mask values like NSViewHeightSizable unsigned integer values we assign each of them a constant NSString.

NSString *kViewNotSizable = @"kViewNotSizable";
NSString *kViewMinXMargin = @"kViewMinXMargin";
NSString *kViewWidthSizable = @"kViewWidthSizable";
NSString *kViewMaxXMargin = @"kViewMaxXMargin";
NSString *kViewMinYMargin = @"kViewMinYMargin";
NSString *kViewHeightSizable = @"kViewHeightSizable";
NSString *kViewMaxYMargin = @"kViewMaxYMargin";

We would also have to rewrite the accessors for the auto resizing mask to use NSSet.

- (void)setAutoresizingMask:(NSSet *)mask;
- (NSSet *)autoresizingMask;

Thats basically it. Setting the auto resizing mask by using this new pattern would look like this:

[aView setAutoresizingMask:[NSSet setWithObjects:
                                    kViewWidthSizable,
                                    kViewHeightSizable, nil];

No more bitwise operations. But there is more: By using NSSet we can now use the "operations" that are already built in NSSet. Let me give you just two examples:

Checking if the view should resize it's height:

if([[aView autoresizingMask] containsObject:kViewHeightSizable])

Checking if the auto resizing mask is "valid":

 NSSet *maxMask = [NSSet setWithObjects:
                   kViewNotSizable,
                   kViewMinXMargin,
                   kViewWidthSizable,
                   kViewMaxXMargin,
                   kViewMinYMargin,
                   kViewHeightSizable,
                   kViewMaxYMargin];

 if([[aView autoresizingMask] isSubsetOfSet:maxMask])

I hope you get the idea. Sure, you have to write a few extra lines of code. But it becomes much more readable. In addition to that key value coding and key value observing are possible and safe without doing additional work. If you want that the size of a view should stay the same just assign is an empty set. You don't have to know or lookup the value that represents that.

I suggest that you try this pattern for yourself where you can. I am pretty sure that you will fall in love with it. You don't have to worry about performance of memory related problems because I am sure that it doesn't have a significant impact. When developing for the iPhone/iPod platform you may want to consider not using this pattern. I can imagine that it could have a negative effect there. But I haven' tried it. That is up to you. :)

Let me know that you think about this "pattern". Will you try it? Are you already using it? Post a comment or send me an email.