Saturday, March 31, 2012

Adding IVars to Library Classes in Objective C

For a recent project, I needed to add some data to one of the Core Data classes (NSManagedObjectContext) and found Tom Harrington's post regarding a bug work-around he implemented, which required adding a method for freeing a memory block upon class dealloc (a class he didn't control). He details how he creates a Category for NSObject and a separate class to hold an array of deallocators.

I tweaked Tom's approach slightly, to get the following design methodology: Adding IVars to a Class (and it's subclasses). A future post will explain in detail why I needed these extra parameters, but briefly, I want to extend NSManagedObjectContext, a class I do not own, with methods that short cut the work required to build and execute fetching from Core Data. Adding a little bit of state and/or default values to the context (instead of passing that data into/out of the method calls) simplifies matters. Making IVar access transparent eases the burden on the caller. Since I don't own the class definition, I needed another method to associate data and Tom's post pointed the way.

Example Code

CompactFetch.h - Category declaration

@interface NSManagedObjectContext (CompactFetch)
@property (assign,nonatomic) NSUInteger defaultFetchLimit;
@property (retain,nonatomic,readonly) NSError* lastError;
@end

In our header, we create a category, CompactFetch, for NSManagedContext. Notice the two properties created. These property declarations do not use @synthesize for their implementations. The nice thing about making them properties, is that code can use dot-notation to set or get their values. In addition, the code is KVC compliant. Note, you can use objects here, just be careful with assign & retain.

CompactFetch.m - IVar class declaration

@interface CompactFetchIVars : NSObject
@property (assign,nonatomic) NSUInteger defaultFetchLimit;
@property (retain,nonatomic) NSError* lastError;
+ (CompactFetchIVars*)fetch:(NSManagedObjectContext*)moc;
@end

Next, we create a class, CompactFetchIVars, which mirrors the @property declarations on the Category. Again, be sure to match up the assign & retain annotations. Although the property names need not be the same, keeping them that way reduces confusion.

Look at the class method, +fetch:, the heart of our design. It takes an NSManagedObjectContext and returns an instance of our class. We use this class method to attach new ivars (new data) onto the Core Data class. By the way, this @interface resides in the .m file, there's no reason to expose the caller to this detail.

Now, for CompactFetchIVars's class definition:

CompactFetch.m - IVar class definition

@implementation CompactFetchIVars

@synthesize defaultFetchLimit, lastError;

+ (CompactFetchIVars*)fetch:(NSManagedObjectContext*)moc
{
  static void *compactFetchIVarKey = &compactFetchIVarKey;
  CompactFetchIVars *ivars = objc_getAssociatedObject(moc, &compactFetchIVarKey);
  if (ivars == nil) {
    ivars = [[CompactFetchIVars alloc] init];
    objc_setAssociatedObject(moc, &compactFetchIVarKey, ivars, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    [ivars release];
  }
  return ivars;
}

- (id)init
{
  self = [super init];
  return self;
}

- (void)dealloc
{
  self.lastError = nil;
  [super dealloc];
}

@end

A typical class definition, we have @synthesize and init/dealloc. Note, -dealloc silently releases lastError through the nil assignment (via the synthesized property).

What's +fetch: up to? objc_getAssociatedObject retrieves an object previously associated with the target class, our NSManagedObjectContext. If this is the first time through, there is no association and nil is returned. In that case, we must create and associate an instance of our class, CompactFetchIVars, with the context. objc_setAssociatedObject performs the association. Note that both the get & set association calls require a key (in this case, compactFetchIVarKey), and despite the documentation, that key need not be a string. The functions use the key's unique location in program memory (assigned by the compiler) to determine uniqueness of the key.

CompactFetch.m - Category definition

@implementation NSManagedObjectContext (CompactFetch)

- (NSUInteger)defaultFetchLimit
{
  return [CompactFetchIVars fetch:self].defaultFetchLimit;
}

- (void)setDefaultFetchLimit:(NSUInteger)limit
{
  [CompactFetchIVars fetch:self].defaultFetchLimit = limit;
}

- (NSError*)lastError
{
  return [CompactFetchIVars fetch:self].lastError;
}

- (void)setLastError:(NSError*)error
{
  [CompactFetchIVars fetch:self].lastError = error;
}
//...

Finally, we implement the get/set methods for the Category we attached to NSManagedObjectContext. In them, we use the +fetch: class method to retrieve (or create, associate and retrieve) the instance of CompactFetchIVars attached to the NSManagedObjectContext. Then, we get or set the parameter, as appropriate. Under the covers, this goes from a method call on the Core Data instance via the Category through to the attached IVar instance. The runtime knows to call -dealloc on both NSManagedObjectContext and CompactFetchIVars, when retain count goes to zero. DO NOT RETAIN a reference from the IVar class to the attached class; any back track should be assign only, or a retain cycle is created and memory leaks.

Friday, March 16, 2012

Trip Report: Salesforce.com's Cloudstock

Cloudstock

On March 15th, I attended Salesforce.com's Cloudstock (& Cloudforce).

First off, Marc Benioff has built an incredible company - for the industry, for their customers and for their employees. I know at least a handle of current and previous employees that have nothing but good things to say about their employer.

As for the event, if you are even remotely involved in High Tech, I would encourage you to attend.  They did a 2.5 hour keynote in the morning that featured C-level employees, customers and, of course, Marc. Marc is inspiring and paints a great picture of where we are in the technological evolution of computing (mainframe, PC, client-server, web, mobile, social). It's not an earth shattering story (if you've been around awhile, you know it), but what is apparent is Salesforce's ability to pivot and take advantage of the last three.

Confession: I left the keynote after 90 minutes, that was enough for me.

Mid-day, the expo floor opened. There were two main sections: Cloudstock and Cloudforce. The former was for developers, so I headed over there. I never did make it to Cloudforce.

One thing that stood out, among many stand out items, was the high quality conversations I had with every person I met at the show.

The Expo

DataSift.com - Far and away, the most impressive product was from DataSift.com. Imagine your company wants to see every social media tweet, post, facebook, yelp, digg, etc. about itself. Furthermore, the data should be tagged and categorized by geo location, sentiment, and whatever else the marketing research department wants. You could build a feed reader for the 30 or so social media platforms and collect the portion of the data they send you (you won't get everything you want). Or, you could go through Data Sift's interface, build a simple query (via GUI or by hand), and have your data feed running in less than a minute. Output is in JSON and easily handled by current tools. Definitely, check them out.

Heroku - Salesforce bought Heroku, recently. Heroku is a cloud platform development platform whose backend runs in AWS. If you like to code in their provided languages (Java, Ruby, PHP), you can use their system "out of the box". If not, you can provide your own libraries for install. They also integrate with Database.com (another Salesforce acquisition). They have a dyno model, in which your system runs. They claimed this provides some higher levels of security. Securing cloud services against back-end attacks should be the chief security concern for every cloud application. I'm skeptical of the level of protection, however, they said a security white paper was forthcoming. Stay tuned.

StackMob - Another backend system, this one dedicated to running your server side operations without having to do any infrastructure work. Basically, think of having to write only the Java classes that respond to GET/POST calls for an Apache Tomcat plug-in with some nice glue into a SQL database. That's StackMob.

Database.com - I didn't speak with these folks at great length, but they have a nice product. Check them out.

Force.com - This is Salesforce's cloud development platform. They've pivoted hard into the social part of the web, leveraging their Chatter application and creating a social media datastream for the Enterprise. There's some great stuff in there that I won't review, you can check out their site. There are, however, some items worthy to note, they have two programming models for handling mobile: an SDK and a hybrid wrapper. The SDK provides calls into the force.com system and is to be integrated into a native application (iOS/Objective-C). The hybrid wrapper uses PhoneGap (below) to wrap HTML/JavaScript.

PhoneGap - These folks provide a shell that wraps HTML and JavaScript web pages. They have developed a JS library to provide native access to phone hardware (camera, GPS, etc.). If you like, you can even build your own native operations and map them to JS calls compiled into the wrapper. The wrapped code gets submitted to the application store. Although I'm a big fan of the mobile web, some applications do best using the mobile platform's look and feel and will continue to be coded in iOS or Java. I think if you're in a hurry, this is a good way to go.

Sencha - These folks have a back end web server that can build for the mobile web browser (HTML5/CSS) or for Desktop browser. I wasn't clear if the browser required a plug-in, but the mobile web pages they showed me looked quite clean. Worth checking them out if you want standard, mobile web browsing.

In the notable mention side, I'd like to put a plug in for Brain Engine and GitHub.

Brain Engine provides an augmented IDE that runs in the cloud and sits atop the Force.com infrastructure. As I have not done a lot of Force.com development, I couldn't tell you if they truly benefit the user, but they seemed convinced and seemed to know what they were talking about. In addition, we discussed a downfall of the Force.com platform, something they are not able to fix, either.

Apparently, Force.com doesn't have a good (or any) source control. All programs are checked into the DB, and there's no branching or merging. That has to be a nightmare for development. Salesforce should address this problem and their users should demand a change.

GitHub - They provide hosted source control and, also, an issue tracker. There are plenty of cloud based source control systems out there. My friend uses GitHub and is quite happy. YMMV.

Finally, an SalesForce architect and I were discussing securing at rest data on the phone and may have found what could a huge security misunderstanding for mobile development work. We are researching the problem and will write a paper, if true. Be on the lookout.