Key-Value Observing Done Right. Again.
May 21, 2010 04:36 PM
| Cocoa, Development, Mac
| Permalink
I love using KVO for all sorts of things: side-effect, flagging UI as dirty, resetting caches, …
Writing the observeValueForKeyPath:ofObject:change:context: method has long since become tedious. So I created a TextExpander snippet to write it for me.
Still, I don't quite like it relying of string comparisons (which I use for context). And I regularly find myself forgetting to unregister observers.
Mike Ash wrote a interesting blog post where he makes a couple of good points against the current KVO API. The most important of which, I think, is the fact unregistering an observer might actually break behavior upon which the superclass relies.
Andy Matuschak has proposed a very convenient API which uses blocks to handle observation callbacks. While I love this solution, I found it to still have a couple of drawbacks:
Inspired by Mike's and Andy's writings and work, I have come up with my own implementation: HHKeyValueObserver.
Retain cycles may be avoided by referring to the observed object by the way of observationInfo.observee.
Writing the observeValueForKeyPath:ofObject:change:context: method has long since become tedious. So I created a TextExpander snippet to write it for me.
Still, I don't quite like it relying of string comparisons (which I use for context). And I regularly find myself forgetting to unregister observers.
Mike Ash wrote a interesting blog post where he makes a couple of good points against the current KVO API. The most important of which, I think, is the fact unregistering an observer might actually break behavior upon which the superclass relies.
Andy Matuschak has proposed a very convenient API which uses blocks to handle observation callbacks. While I love this solution, I found it to still have a couple of drawbacks:
- Andy's KVO+Blocks requires Mac OS X Snow Leopard. It relies not only on blocks, but also on associated objects and Grand Central Dispatch.
- Code is not always easer to read when the callback block is written where it is registered. At times, I would prefer a traget+action setup
- KVO+Blocks make it all too easy to create retain cycles: referencing an instance variable from within the block retains the owning object.
Inspired by Mike's and Andy's writings and work, I have come up with my own implementation: HHKeyValueObserver.
- Works on both Leopard and Snow Leopard. Relies on HHAssociatedObjects
- Target action mechanism with the following signatures: actionWithInfo:change: or actionWithInfo:
- Uses blocks where available
- Unregisters automatically on dealloc of the observer
Retain cycles may be avoided by referring to the observed object by the way of observationInfo.observee.
Comments
HHAssociatedObjects
May 19, 2010 10:20 AM
| Cocoa, Development, Mac
| Permalink
Mac OS X 10.6 introduced associated objects by the means of objc_getAssociatedObject and objc_setAssociatedObject. These provide a very convenient method for attaching object values to random objects. Associated objects can conveniently share the lifespan of the object they are associated with.
One situation where associated objects come in especially handy is when writing categories. Associated objets may be used be category methods to store state. It's the closest thing we got to adding instance variables.
The catch being that many projects still need to support Leopard. HHAssociatedObjects implements the concept of associated objects to support Leopard and Snow Leopard. Where available, the objc_getAssociatedObject and objc_setAssociatedObject methods are used. Elsewhere a NSMapTable is used to store associate objects.
HHAssociatedObjects are made available as a category on NSObject. I settled on the same API as Andy Matuschak. On Snow Leopard the implementation is actually the same as Andy's. HHAssociatedObjects adds a Leopard compatible implementation.
One situation where associated objects come in especially handy is when writing categories. Associated objets may be used be category methods to store state. It's the closest thing we got to adding instance variables.
The catch being that many projects still need to support Leopard. HHAssociatedObjects implements the concept of associated objects to support Leopard and Snow Leopard. Where available, the objc_getAssociatedObject and objc_setAssociatedObject methods are used. Elsewhere a NSMapTable is used to store associate objects.
HHAssociatedObjects are made available as a category on NSObject. I settled on the same API as Andy Matuschak. On Snow Leopard the implementation is actually the same as Andy's. HHAssociatedObjects adds a Leopard compatible implementation.
HHAutoHidingWindow
May 5, 2010 10:06 PM
| Cocoa, Development, Mac
| Permalink
For the next version of HoudahSpot, I am implementing a window which docks to the side of the screen where it shows and hides as needed. After looking into several implementations, I came up with my own. Seeing this may come in handy to some, I'd like to share this bit of code with you.
HHSlidingWindow allows for docking a window at an edge of the main screen. From there it may slide in and out of visibility. Its subclass HHAutoHidingWindow
provides automatic showing of the window as the mouse hits the screen edge where the window is hiding. The implementation is actually pretty straightforward.
It relies solely on tracking areas. No polling is done.
HHSlidingWindow allows for docking a window at an edge of the main screen. From there it may slide in and out of visibility. Its subclass HHAutoHidingWindow
provides automatic showing of the window as the mouse hits the screen edge where the window is hiding. The implementation is actually pretty straightforward.
It relies solely on tracking areas. No polling is done.
Automatically releasing retained @property values
January 12, 2010 04:03 PM
| Cocoa, Development, Mac
| Permalink
I subscribe to the opinion, that setters should not be called in constructors nor in destructors. This negates some of the benefits of @synthesize accessors. Indeed, one needs to keep the dealloc method in sync with the setter semantics. Our dealloc methods thus look like this:
- (void)dealloc
{
// Retained properties
[_firstProperty release], _firstProperty = nil;
[_thirdProperty release], _thirdProperty = nil;
// Assigned properties
secondProperty = nil;
[super dealloc];
}
This is repetitive and error-prone. Luckily, we may generate @synthesize instructions as well as destructors.
The alternative, is to use introspection to dynamically determine how the various properties need to be freed. For this, I suggest a category on NSObject:
@implementation NSObject (PropertyDealloc)
- (void)deallocProperties
{
Class class = [self class];
unsigned int pCount;
objc_property_t *properties = class_copyPropertyList(class, &pCount);
for (unsigned int i = 0; i < pCount; i++) {
objc_property_t property = properties[i];
NSString *propertyAttributes = [[[NSString alloc] initWithUTF8String:property_getAttributes(property)] autorelease];
NSArray *propertyAttributeArray = [propertyAttributes componentsSeparatedByString:@","];
BOOL isRetained = NO;
for (NSString *string in propertyAttributeArray) {
isRetained = isRetained || [string isEqual:@"&"] || [string isEqual:@"C"];
}
if (isRetained) {
NSString *variableName = nil;
NSString *lastProperty = (NSString*)[propertyAttributeArray lastObject];
if ([lastProperty hasPrefix:@"V"]) {
variableName = [lastProperty substringFromIndex:1];
}
if (variableName != nil) {
Ivar ivar = class_getInstanceVariable(class, [variableName UTF8String]);
id value = object_getIvar(self, ivar);
object_setIvar(self, ivar, nil);
[value release];
}
}
}
free(properties);
}
@end
Usage is extremely simple:
- (void)dealloc
{
[self deallocProperties];
[super dealloc];
}
This post was inspired by Vincent Gable.
- (void)dealloc
{
// Retained properties
[_firstProperty release], _firstProperty = nil;
[_thirdProperty release], _thirdProperty = nil;
// Assigned properties
secondProperty = nil;
[super dealloc];
}
This is repetitive and error-prone. Luckily, we may generate @synthesize instructions as well as destructors.
The alternative, is to use introspection to dynamically determine how the various properties need to be freed. For this, I suggest a category on NSObject:
@implementation NSObject (PropertyDealloc)
- (void)deallocProperties
{
Class class = [self class];
unsigned int pCount;
objc_property_t *properties = class_copyPropertyList(class, &pCount);
for (unsigned int i = 0; i < pCount; i++) {
objc_property_t property = properties[i];
NSString *propertyAttributes = [[[NSString alloc] initWithUTF8String:property_getAttributes(property)] autorelease];
NSArray *propertyAttributeArray = [propertyAttributes componentsSeparatedByString:@","];
BOOL isRetained = NO;
for (NSString *string in propertyAttributeArray) {
isRetained = isRetained || [string isEqual:@"&"] || [string isEqual:@"C"];
}
if (isRetained) {
NSString *variableName = nil;
NSString *lastProperty = (NSString*)[propertyAttributeArray lastObject];
if ([lastProperty hasPrefix:@"V"]) {
variableName = [lastProperty substringFromIndex:1];
}
if (variableName != nil) {
Ivar ivar = class_getInstanceVariable(class, [variableName UTF8String]);
id value = object_getIvar(self, ivar);
object_setIvar(self, ivar, nil);
[value release];
}
}
}
free(properties);
}
@end
Usage is extremely simple:
- (void)dealloc
{
[self deallocProperties];
[super dealloc];
}
This post was inspired by Vincent Gable.
Comments (3)
HHDualShortcutButton updated
October 10, 2009 12:39 PM
| Cocoa, Development, Houdah, Mac
| Permalink
HHDualShortcutButton has been updated to be Cocoa only. No more Carbon code!
This makes it require Mac OS X 10.6 Snow Leopard.
This makes it require Mac OS X 10.6 Snow Leopard.
Sample use of HHBlockPerform
October 9, 2009 10:25 PM
| Cocoa, Development, Mac
| Permalink
HHPerformBlock SetterBlock(id inValue, NSString *inKey)
{
HHPerformBlock block = ^(id owner) {
[owner willChangeValueForKey:inKey];
[owner associateValue:inValue withKey:inKey];
[owner didChangeValueForKey:inKey];
};
return [[block copy] autorelease];
}
/* ... */
- (NSNumber*)progressMinValue
{
return [self associatedValueForKey:@"progressMinValue"];
}
- (void)setProgressMinValue:(NSNumber*)inValue
{
[self performOnMainThreadWait:YES block:SetterBlock(inValue, @"progressMinValue")];
}
{
HHPerformBlock block = ^(id owner) {
[owner willChangeValueForKey:inKey];
[owner associateValue:inValue withKey:inKey];
[owner didChangeValueForKey:inKey];
};
return [[block copy] autorelease];
}
/* ... */
- (NSNumber*)progressMinValue
{
return [self associatedValueForKey:@"progressMinValue"];
}
- (void)setProgressMinValue:(NSNumber*)inValue
{
[self performOnMainThreadWait:YES block:SetterBlock(inValue, @"progressMinValue")];
}
HHBlockPerform revisited
October 9, 2009 12:14 AM
| Cocoa, Development, Mac
| Permalink
The first post on the subject prompted Ben to comment: "Helpful tip: Just capture the object being messaged using the block's scope. No need for the extra "owner" parameters and whatnot." Right he is.
typedef void (^HHPerformBlock)();
@interface NSObject (HHBlockPerform)
- (void)performAfterDelay:(NSTimeInterval)delay block:(HHPerformBlock)block;
- (void)performOnMainThreadWait:(BOOL)wait block:(HHPerformBlock)block;
@end
@implementation NSObject (HHBlockPerform)
- (void)performAfterDelay:(NSTimeInterval)delay block:(HHPerformBlock)block
{
[self performSelector:@selector(runBlock:) withObject:[block copy] afterDelay:delay];
}
- (void)performOnMainThreadWait:(BOOL)wait block:(HHPerformBlock)block
{
[self performSelectorOnMainThread:@selector(runBlock:) withObject:[block copy] waitUntilDone:wait];
}
- (void)runBlock:(HHPerformBlock)block
{
block();
[block release];
}
@end
It does not get any simpler than this:
[self performAfterDelay:0.2f block:^ {
[containerView replaceSubview:currentNavigationBarView with:navigationBarView];
}];
[UPDATE October9, 2009] While I agree that the (id owner) argument is not strictly necessary, I do prefer that API and will keep using it. Indeed, it allows for reusable blocks which refer to owner much like you would refer to self in the delyed method call.
typedef void (^HHPerformBlock)();
@interface NSObject (HHBlockPerform)
- (void)performAfterDelay:(NSTimeInterval)delay block:(HHPerformBlock)block;
- (void)performOnMainThreadWait:(BOOL)wait block:(HHPerformBlock)block;
@end
@implementation NSObject (HHBlockPerform)
- (void)performAfterDelay:(NSTimeInterval)delay block:(HHPerformBlock)block
{
[self performSelector:@selector(runBlock:) withObject:[block copy] afterDelay:delay];
}
- (void)performOnMainThreadWait:(BOOL)wait block:(HHPerformBlock)block
{
[self performSelectorOnMainThread:@selector(runBlock:) withObject:[block copy] waitUntilDone:wait];
}
- (void)runBlock:(HHPerformBlock)block
{
block();
[block release];
}
@end
It does not get any simpler than this:
[self performAfterDelay:0.2f block:^ {
[containerView replaceSubview:currentNavigationBarView with:navigationBarView];
}];
[UPDATE October9, 2009] While I agree that the (id owner) argument is not strictly necessary, I do prefer that API and will keep using it. Indeed, it allows for reusable blocks which refer to owner much like you would refer to self in the delyed method call.
Xcode user script: PropertyFromInstanceVariable
September 20, 2009 09:14 PM
| Cocoa, Development, Mac, Xcode
| Permalink
Wrote some Perl today. No, I don't know Perl. Turns out it is good at handling strings.
I stumbled upon a blog post on Cocoa with Love. Matt Gallagher has devised this excellent Xcode user script to generate @property declarations and @synthesize statements from variable declarations. This is triple great:
1. The script will save me quite some typing
2. I had never noticed the Xcode script menu
3. Matt found a nifty way around limitations of those user scripts
So I grabbed the script. Installed it. Then I figured, I could improve upon the existing script. My changes include:
1. Generates the necessary statements in the dealloc method
2. Detects variable types and tries to be smart about setter semantics
3. Supports underbar variable names
Shortly after I had published my version of the code to my own Code page, Mike Schrag pointed me to his forked version of Matt's script. He had made many of the same enhancements. Moreover he had taught the script to handle multiple varaibles in one pass.
Merging my version and Mike's I created my very first project on github. The new version improves on Mike's by reducing the number of calls to AppleScript. It retains all my previous enhancements. And hopefully also retains Mike's changes. It is available on github, as well as on my own Code page. Enjoy!
I stumbled upon a blog post on Cocoa with Love. Matt Gallagher has devised this excellent Xcode user script to generate @property declarations and @synthesize statements from variable declarations. This is triple great:
1. The script will save me quite some typing
2. I had never noticed the Xcode script menu
3. Matt found a nifty way around limitations of those user scripts
So I grabbed the script. Installed it. Then I figured, I could improve upon the existing script. My changes include:
1. Generates the necessary statements in the dealloc method
2. Detects variable types and tries to be smart about setter semantics
3. Supports underbar variable names
Shortly after I had published my version of the code to my own Code page, Mike Schrag pointed me to his forked version of Matt's script. He had made many of the same enhancements. Moreover he had taught the script to handle multiple varaibles in one pass.
Merging my version and Mike's I created my very first project on github. The new version improves on Mike's by reducing the number of calls to AppleScript. It retains all my previous enhancements. And hopefully also retains Mike's changes. It is available on github, as well as on my own Code page. Enjoy!
Comments (1)
NSObject (HHBlockPerform): delayed block executions
September 15, 2009 04:53 PM
| Cocoa, Development, Mac
| Permalink
typedef void (^HHPerformBlock)(id owner);
@interface NSObject (HHBlockPerform)
- (void)performAfterDelay:(NSTimeInterval)delay block:(HHPerformBlock)block;
@end
@implementation NSObject (HHBlockPerform)
- (void)performAfterDelay:(NSTimeInterval)delay block:(HHPerformBlock)block
{
[self performSelector:@selector(runBlock:) withObject:[block copy] afterDelay:delay];
}
- (void)runBlock:(HHPerformBlock)block
{
block(self);
[block release];
}
@end
@interface NSObject (HHBlockPerform)
- (void)performAfterDelay:(NSTimeInterval)delay block:(HHPerformBlock)block;
@end
@implementation NSObject (HHBlockPerform)
- (void)performAfterDelay:(NSTimeInterval)delay block:(HHPerformBlock)block
{
[self performSelector:@selector(runBlock:) withObject:[block copy] afterDelay:delay];
}
- (void)runBlock:(HHPerformBlock)block
{
block(self);
[block release];
}
@end
Comments (1)
HHValidatedButton: User interface validation for NSButton
August 23, 2009 12:21 PM
| Cocoa, Development, Houdah, Mac
| Permalink
According to the Cocoa User Interface Validation documentation: "The protocols NSUserInterfaceValidations and NSValidatedUserInterfaceItem provide a standard way to validate user interface items—that is, to set their state as appropriate for the current application context".
Unfortunately, automatic user intreface validation is provided only for menu items and toolbar items. Wouldn't it be great if NSButton could also benefit from this set-up?
That's what HHValidatedButton implements. It is a drop-in subclass of NSButton. It channels validation through validateButton: and validateUserInterfaceItem:.
Unfortunately, automatic user intreface validation is provided only for menu items and toolbar items. Wouldn't it be great if NSButton could also benefit from this set-up?
That's what HHValidatedButton implements. It is a drop-in subclass of NSButton. It channels validation through validateButton: and validateUserInterfaceItem:.
Comments (2)
ObjectiveLocr 2.0 released
May 29, 2009 11:31 PM
| Cocoa-Touch, Development, Houdah, iPhone, locr
| Permalink
This project is based off the objectiveflickr project by Lukhnos D. Liu. Most of the credit goes to him!
Please refer to the objectiveflickr for framework documentation and to the locr.com web site for API documentation.
ObjectiveLocr is used by HoudahGeo (Mac photo geotagging), locrUpload (locr upload client for iPhone) and locrExplorer (full-featured locr client for iPhone).
Please refer to the objectiveflickr for framework documentation and to the locr.com web site for API documentation.
ObjectiveLocr is used by HoudahGeo (Mac photo geotagging), locrUpload (locr upload client for iPhone) and locrExplorer (full-featured locr client for iPhone).
Houdah iPhone Properties
March 26, 2009 12:43 AM
| Cocoa-Touch, Development, Houdah, iPhone
| Permalink
I just open-sourced a couple of classes to simplify working with UITableViewControllers to create property editing interfaces.
The controllers and views of this project may be used to easily create interfaces much like Apple's Settings applications. They make up most of the UI of the free LocrUpload application.
The PropertiesViewController may be used to modify attributes of any key-value-coding compliant container. Typically a container would be a NSMutableDictionary or a NSUserDefaults instance.
The controller is typically configured in a subclass by providing a set of descriptors. Descriptors are immutable objects describe the properties to expose and the UI elements (text field, switch, ...) to use. Seeing that descriptors are immutable, it should be easy to store them in a file rather than building them in code.
The SelectionViewController is used by the PropertiesViewController for multiple choice values.
The controllers and views of this project may be used to easily create interfaces much like Apple's Settings applications. They make up most of the UI of the free LocrUpload application.
The PropertiesViewController may be used to modify attributes of any key-value-coding compliant container. Typically a container would be a NSMutableDictionary or a NSUserDefaults instance.
The controller is typically configured in a subclass by providing a set of descriptors. Descriptors are immutable objects describe the properties to expose and the UI elements (text field, switch, ...) to use. Seeing that descriptors are immutable, it should be easy to store them in a file rather than building them in code.
The SelectionViewController is used by the PropertiesViewController for multiple choice values.
Houdah WO frameworks: Sample application
June 8, 2008 07:19 PM
| Development, Houdah, WebObjects
| Permalink
Right in time for the presentation at WOWODC 2008, I have created a demo / sample application for Houdah WebObjects Frameworks. HoudahMovies is a bare minimum demo application for HoudahAgileComponents. It does not use custom actions, controllers, views or cells. It does however show how a CRUD application may be created using a small set of rules and no code at all.
HoudahMovies should serve as a starting point for your exploration of the Houdah WebObjects Frameworks.
In the lower levels of the Houdah WebObjects Frameworks, you'll find a lot of cool goodies which are not demoed by HoudahMovies: custom qualifiers, rule-based EOValidation, ... Check the Wiki for more information on the various frameworks.
HoudahMovies should serve as a starting point for your exploration of the Houdah WebObjects Frameworks.
In the lower levels of the Houdah WebObjects Frameworks, you'll find a lot of cool goodies which are not demoed by HoudahMovies: custom qualifiers, rule-based EOValidation, ... Check the Wiki for more information on the various frameworks.
Comments (2)
HHDualShortcutButton
May 1, 2008 04:37 PM
| Cocoa, Development, Mac
| Permalink
Long time no blogging. Sorry about that.
I'll make it up to you by publishing a fresh bit of Cocoa code. HHDualShortcutButton is a NSButton subclass which may respond to 2 different keyboard shortcuts. You may know this from save confirmation sheet that pops up when you try to quit an application with dirty document. The Save button triggers its action on both Enter and command-S.
Moreover the HHDualShortcutButton will display one of its shortcuts as label when the command key is held down. This used to exist in save sheets. Now AppleWorks is the only example I can find. Currently this works only for alphanumeric shortcuts. I have not yet figured out how to represent special characters like the Return key, ...
Enjoy!
I'll make it up to you by publishing a fresh bit of Cocoa code. HHDualShortcutButton is a NSButton subclass which may respond to 2 different keyboard shortcuts. You may know this from save confirmation sheet that pops up when you try to quit an application with dirty document. The Save button triggers its action on both Enter and command-S.
Moreover the HHDualShortcutButton will display one of its shortcuts as label when the command key is held down. This used to exist in save sheets. Now AppleWorks is the only example I can find. Currently this works only for alphanumeric shortcuts. I have not yet figured out how to represent special characters like the Return key, ...
Enjoy!
Deep linking to downloads
February 20, 2008 08:03 PM
| Development, Houdah
| Permalink
Looking at the download stats for HoudahGeo and HoudahSpot, I noticed that I still get a lot of downloads on long since outdated versions of both products.
I suspect this to be the effect of download sites deep linking to the ZIP files. Thus I have set up the following rewrite rules to redirect any deep link to the latest download:
From the crontab I call:
This makes sure that the "HoudahSpot latest.zip" link always points to the latest version.
I suspect this to be the effect of download sites deep linking to the ZIP files. Thus I have set up the following rewrite rules to redirect any deep link to the latest download:
RewriteCond %{HTTP_REFERER} !.*houdah.com.* [NC]
RewriteCond %{REQUEST_FILENAME} !.*latest.zip
RewriteRule .* http://www.houdah.com/houdahSpot/download_assets/HoudahSpot\ latest.zip [R]
From the crontab I call:
cd /home/houdahco/www/houdahSpot/download_assets ; ln -sf "`ls -lrt | cut -c54- | grep -v \">\" | tail -1`" HoudahSpot\ latest.zip
This makes sure that the "HoudahSpot latest.zip" link always points to the latest version.
Release rush before Macworld
January 9, 2008 07:27 PM
| Development, Houdah, Mac
| Permalink
I am in an update mood. Next week I will be showing off HoudahGeo & HoudahSpot at Macworld. Obviously I want them to look their best.
This week already saw the release of HoudahGPS 2.0 (now 2.0.1, oops) as well as a point update to HoudahSpot 2.
The real big news will be the release of HoudahGeo 1.4. This comes with an arm-long release note. Mmmmh, many new goodies in there. You may grab HoudahGeo 1.4b1 (BETA) here.
I am left with two more days for testing and updating the documentation.
This week already saw the release of HoudahGPS 2.0 (now 2.0.1, oops) as well as a point update to HoudahSpot 2.
The real big news will be the release of HoudahGeo 1.4. This comes with an arm-long release note. Mmmmh, many new goodies in there. You may grab HoudahGeo 1.4b1 (BETA) here.
I am left with two more days for testing and updating the documentation.
HHusbObserver & HHserialObserver
December 31, 2007 10:00 AM
| Cocoa, Development, Mac
| Permalink
I have finally published my first bit of open source Cocoa code: HHusbObserver & HHserialObserver.
Previously I have published a series of WebObjects code snippets. Lately I have kicked off a major open source project: Houdah WebObjects Frameworks.
The fact remains that up to now I had not yet deemed any of my Cocoa code worthy of being published. HHusbObserver & HHserialObserver is not code to be particularly proud of. Writing it however required a lot of documentation reading and experimenting. Thus I hope for this to save other some valuable time.
Previously I have published a series of WebObjects code snippets. Lately I have kicked off a major open source project: Houdah WebObjects Frameworks.
The fact remains that up to now I had not yet deemed any of my Cocoa code worthy of being published. HHusbObserver & HHserialObserver is not code to be particularly proud of. Writing it however required a lot of documentation reading and experimenting. Thus I hope for this to save other some valuable time.