Mobile Zone is brought to you in partnership with:

Łukasz works as a Technical Architect for an international IT company and is responsible for delivering applications written in Java EE, Spring, and .NET. He has been involved in many various projects ranging from online insurance systems, voice and video solutions, mobile systems (both native and HTML5-based), medical systems, and large system integration projects. Łukasz is an expert in distributed systems, SOA, and cloud. Łukasz holds PhD in Computer Science. Łukasz is a DZone MVB and is not an employee of DZone and has posted 20 posts at DZone. You can read more from them at their website. View Full User Profile

Objective-C Categories == Groovy Expando Mechanism

06.09.2013
| 7348 views |
  • submit to reddit

Long live dynamic languages! It's a short post glorifying dynamic languages :)

(BTW: do you remember my previous post about Groovy: 21 things I like and don't like about Groovy? - there is an example of Groovy expando mechanism)

I know and use Objective-C categories. I use them to, for example, re-implement methods that are already provided.

But only yesterday, while writing some iPhone piece of code using SBJSON library I realised that in fact Objective-C category mechanism is the same as Groovy expando mechanism.

SBJSON example

For example SBJSON adds JSONRepresentation method to NSObject. But in fact it only works for dictionaries and arrays so you have to invoke it this way:

User *user = [User new];
user.emai = @"lukasz@example.com";
user.password = @"super_secret";
NSDictionary *userDictionary = [user dictionaryWithValuesForKeys:[NSArray arrayWithObjects:@"email",@"password",nil]]; 
NSString *userDictionary = [person JSONRepresentation];

But I (with a help of categories of course!) went one step farther :) I used Apple Runtime library to dynamically scan for all properties in given object, create dictionary and then create JSON string in one step.

Enhanced JSONRepresentation method

I pointed my browser to Mac OS X Reference Library - Objective-C Runtime Reference.

Based on the reference I changed the original JSONRepresentation implementation (located in NSObject+SBJSON.m file) to this:

#import <Foundation/NSObjCRuntime.h>
#import <objc/objc.h>
#import <objc/runtime.h>

- (NSString *)JSONRepresentation {
    SBJsonWriter *jsonWriter = [SBJsonWriter new];    
    NSString *json = nil;
 if ([self isKindOfClass:[NSDictionary class]] || [self isKindOfClass:[NSArray class]]) {
  json = [jsonWriter stringWithObject:self];
 } else {
  NSUInteger propertyCount = 0;
  objc_property_t* properties = class_copyPropertyList([self class], &propertyCount);
  NSMutableArray *keys = [NSMutableArray new];
  for (NSUInteger i = 0; i < propertyCount; i++) {
   objc_property_t property = *(properties + i);
   [keys addObject:[NSString stringWithCString:property_getName(property) encoding:NSASCIIStringEncoding]];
  }
  free(properties);
  NSDictionary *dictionary = [self dictionaryWithValuesForKeys:keys];
  json = [jsonWriter stringWithObject:dictionary];
 }
    if (!json)
        NSLog(@"-JSONRepresentation failed. Error trace is: %@", [jsonWriter errorTrace]);
    [jsonWriter release];
    return json;

Works like a charm :) Now instead of explicitly creating NSDictionary all you have to do is:

[user JSONRepresentation];


Nice :)

Cheers,
Łukasz

Published at DZone with permission of Łukasz Budnik, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)