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.

12 comments:

  1. I have been reading for the past two days about your blogs and topics, still on fetching! Wondering about your words on each line was massively effective. Techno-based information has been fetched in each of your topics. Sure it will enhance and fill the queries of the public needs. Feeling so glad about your article. Thanks…!
    magento training course in chennai
    magento training institute in chennai
    magento 2 training in chennai
    magento development training
    magento 2 course
    magento developer training

    ReplyDelete
  2. Thanks for the informative article About Java. This is one of the best resources I have found in quite some time. Nicely written and great info. I really cannot thank you enough for sharing.good luck.
    Ai & Artificial Intelligence Course in Chennai
    PHP Training in Chennai
    Ethical Hacking Course in Chennai Blue Prism Training in Chennai
    UiPath Training in Chennai

    ReplyDelete
  3. Kaspersky Total Security Crack is a best security app that helps you to protect your multiple devices with multi antivirus protection. Kaspersky Internet Security Activation Code Free

    ReplyDelete
  4. Happy Birthday Twin Girls ... Happy Birthday! You two girls came to this world together to make it a better place. May God shower you both with . Twins Birthday Wishes Greeting Card

    ReplyDelete
  5. Fl Studio Crack includes a graphical user interface based on a pattern-based music sequencer. It supports multiple MIDI inputs and outputs and can be used with a variety of MIDI controllers. It also includes a wide range of built-in virtual instruments, synthesizers, samplers, and effects plugins. Users can also add their own VST and VSTi plugins for additional functionality.

    ReplyDelete
  6. Dp Animation Maker Crack is a software program that allows users to create animations and graphics. The program is designed for both novice and advanced users, and it offers a range of tools and features to make the animation process easy and efficient.

    ReplyDelete