Reader Submissions - New Year's 2013
In celebration of the forthcoming year++
, I thought it’d be fun to compile a list of some of your favorite tips and tricks of the trade–to give y’all a chance to show off some of your NSHipster cred.
Thanks to Cédric Luthi, Jason Kozemczak, Jeff Kelley, Joel Parsons, Maximilian Tagher, Rob Mayoff, Vadim Shpakovski, & @alextud for answering the call with excellent submissions.
Associated Objects in Categories
This first tip is so nice it was mentioned twice, both by Jason Kozemczak & Jeff Kelley.
Categories are a well-known feature of Objective-C, allowing new methods to be added to existing classes. Much less well known is that with some objc
runtime hacking, you can add new properties as well. Observe!
NSObject+IndieBandName.h
@interface NSObject (Indie Band Name)
@property (nonatomic, strong) NSString *indie Band Name;
@end
NSObject+IndieBandName.m
#import "NSObject+Extension.h"
#import <objc/runtime.h>
static const void *Indie Band Name Key = &Indie Band Name Key;
@implementation NSObject (Indie Band Name)
@dynamic indie Band Name;
- (NSString *)indie Band Name {
return objc_get Associated Object(self, Indie Band Name Key);
}
- (void)set Indie Band Name:(NSString *)indie Band Name {
objc_set Associated Object(self, Indie Band Name Key, indie Band Name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
This way, all of your objects can store and retrieve the name of their band, which–by the way–is performing this Wednesday night, and you should totally come.
While this is a cool trick and all, it should only be used as a method of last resort. Before you go this route, ask yourself if a particular property can’t either be derived from existing values, or should be managed by another class.
A good example of an associated object is how AFNetworking adds a property for an image request operation in its UIImage
category.
LLDB View Hierarchy Dump
Rob Mayoff responded with an obscure and powerful incantation to make debugging views a delight. Create .lldbinit
in your home directory, if it doesn’t already exist, and add the following:
~/.lldbinit
command regex rd 's/^[[:space:]]*$/po [[[UIApplication shared Application] key Window] recursive Description]/' 's/^(.+)$/po [%1 recursive Description]/'
Now you can get a recursive hierarchy of any view in your iOS application with the LLDB debugger. You can try this for yourself by setting a breakpoint in a view controller, and type rd self.view
. You may be surprised by what’s under the hood with some of the built-in UI controls!
CGPath Ref
LLDB Print Contents of a While we’re on the subject of LLDB, Rob Mayoff sent in a useful incantation for printing out the contents of a CGPath
from the debugger:
p (void)CGPath Print(path Ref, 0)
If you’re doing any kind of complex Core Graphics drawing, be sure to keep this one handy.
+initialize
, Not +load
Use Vadim Shpakovski wrote in with some advice about class loading and initialization. There are two magical class methods in Objective-C: +load
and +initialize
, which are automatically called by virtue of the class being used. The difference between the two methods, however, has significant performance implications for your application.
Mike Ash has a great explanation of this:
+load
is invoked as the class is actually loaded, if it implements the method. This happens very early on. If you implement+load
in an application or in a framework that an application links to,+load
will run beforemain()
. If you implement+load
in a loadable bundle, then it runs during the bundle loading process.
The
+initialize
method is invoked in a more sane environment and is usually a better place to put code than+load
.+initialize
is interesting because it’s invoked lazily and may not be invoked at all. When a class first loads,+initialize
is not called. When a message is sent to a class, the runtime first checks to see if+initialize
has been called yet. If not, it calls it before proceeding with the message send.
tl;dr: Implement +initialize
, not +load
, if you need this automatic behavior.
Xcode Snippets
Maximilian Tagher gave a shout-out to the benefits of Xcode Snippets.
Great developers take pride in knowing their tools, and being able to use them to maximum effect. For better or for worse, this means knowing Xcode like the back of our hand. Verbose as Objective-C is, “do more by typing less” rings especially true as a productivity mantra, and Xcode Snippets are one of the best ways to do this.
If you’re looking for a place to start, try downloading and forking these Xcode Snippets.
Macro for Measuring Execution Time
Here’s a helpful macro for easily measuring the elapsed time for executing a particular block of code, sent in from @alextud:
NS_INLINE void MVCompute Time With Name And Block(const char *caller, void (^block)()) {
CFTime Interval start Time Interval = CACurrent Media Time();
block();
CFTime Interval now Time Interval = CACurrent Media Time();
NSLog(@"%s - Time Running is: %f", caller, now Time Interval - start Time Interval);
}
#define MVCompute Time(...) MVCompute Time With Name And Block(__PRETTY_FUNCTION__, (__VA_ARGS__))
Block Enumeration Methods
Joel Parsons submitted a great tip about using -enumerate
in NSArray
and other collection classes. By passing the NSEnumeration
option, you can get significant performance benefits over NSFast
’s for...in
-style enumeration by executing the block concurrently.
However, be warned! Not all enumerations lend themselves to concurrent execution, so don’t go around replacing all of your for...in
blocks with NSEnumeration
willy-nilly, unless random crashing is something you like in an app.
NSString
Equality Methods
Reverse-Engineered Implementation of Displaying his characteristic brilliance and familiarity of Cocoa internals Cédric Luthi submitted a reverse-engineered implementation of the NString
equality methods. Fascinating!
NSLayout Constraint.constant
Animate This one goes out to all you fans of Cocoa Auto Layout, from Vadim Shpakovski:
view Constraint.constant = <#Constant Value From#>;
[view layout If Needed];
view Constraint.constant = <#Constant Value To#>;
[view set Needs Update Constraints];
[UIView animate With Duration:Constant Animation Duration animations:^{
[view layout If Needed];
}];
Attentive readers may have already noted this, but the code above would make an excellent Xcode Snippet, by the way.
NSCache
Usage
Printing Finishing up this batch of tips and tricks is Cédric Luthi again, this time unearthing the private method cache_print
as a way to get some visibility into NSCache
:
extern void cache_print(void *cache);
- (void) print Cache:(NSCache *)cache {
cache_print(*((void **)(__bridge void *)cache + 3));
}
This code sample has only been tested on iOS, and should only be used for debugging (i.e. take this out before submitting to Apple!).
Thanks again to everyone for their submissions this time around. We’ll definitely be doing this again, so feel free to send your favorite piece of Objective-C trivia, framework arcana, hidden Xcode feature, or anything else you think is cool to @NSHipster!
And thank you, dear reader, for your support of NSHipster over these last wonderful months. We have a lot of insanely great things planned for NSHipster in 2013, and we look forward to being able to share it all with all of you.