Archive for the ‘Mac OS X’ Category
NSMutableDictionary without automatic retaining of contained objects
Wednesday, January 25th, 2012There may arise a situation where you absolutely can’t do something without either doing ugly hacks with overriding -release (which you should never, ever do), or using non-Objective-C constructs such as C++’s std::map (shudder), or rolling out your own key-value storage data structure (evil NIH syndrome strikes again).
The Reason
The only valid reason I can think of for doing this is to avoid a cyclic reference. For example, an object must be stored in a dictionary, but should be automatically removed from it upon being -dealloc‘ed. This is exactly what I’m doing in a game I’m slowly working on in order to cache OpenGL textures. I tried hacking this by overriding -release and monitoring -retainCount. Any Objective-C developer worth the name will know that's a pretty dumb thing to do: -retainCount is a pretty shaky thing to depend on, due to various compiler optimizations, and overriding -release can only cause issues. (Yes, that includes overriding -release to force yourself and your teammates never to destroy a singleton. I actually saw that being done in production code. Pretty nasty stuff.)
Pretty much invalid reasons are:
- storing non-Objective-C pointers or data types. If you're making use of
NSMutableDictionary, you're probably making use of Objective-C and Foundation. So make use of the reference counting mechanism built into the framework by wrapping the non-Objective-C data type into something that can be swallowed by the framework nice and easy. Create a thin wrapper around this data by subclassingNSObject - not wanting to deal with the reference counting system. Oh, so you're one of those people who don't like to use
-retain/-release/-autorelease? You find them abhorrent? Go and cry to your mommy; the reference counting system is one of the most powerful mechanisms enabled by Objective-C and provided by Foundation. Shunning it is no good.
Oh, and if you're one of the ARC-loving pansies developers, sorry; I have no idea what effect this'll have on your funny-colored little world. Because we're about to dive into the mean world of Core Foundation.
This also, sadly, means I have no idea how this'll work with GNUstep.
The Explanation
So you may have heard that Core Foundation equivalents of Foundation classes are "toll-free bridged". What does this mean?
This means that if you create a CFArray, you can use the resulting pointer as an NSArray, and vice versa. This is pretty handy if you're writing code that interacts with Mac OS X's kernel. When writing something that talks to Bluetooth subsystem (say, a new Bluetooth service), you will use C functions that accept and return CFDictionary instances. Oh, sir, yes they do.
So to clean up your code of all those nasty CFDictionary*() function calls, and make it look all nice and Objective-C-ish, what can you do? You just pass the resulting CFDictionary pointer as the first thing in the brackets (you know, where you usually put an Objective-C message target?) and you use plain old Foundation message sends to do operations with the dictionary. To get rid of the warning, you can cast it either prior to the message send or in-line when performing the send.
CFDictionaryRef dict; // same as CFDictionary * // . . . initialize it here . . . [((NSDictionary*)dict) valueForKey:@"someKey"]; // ...or alternatively: NSDictionary * theDict = (NSDictionary*)dict; [dict valueForKey:@"someKey"];
And you can also do the opposite thing! You can create an NSDictionary and pass it off as a CFDictionary.
NSDictionary * dict = [[NSDictionary alloc] initWithObjectsAndKeys:@"value", @"key", nil]; // . . . use it here . . . // now we'd have to do [dict release]. // or, we could have autoreleased the object right after // initializing it. // but let's be fancy. CFDictionaryRef cfDict = (CFDictionaryRef)dict; CFRelease(cfDict);
The Solution
So how do we actually create a NSMutableDictionary whose objects won't be retained nor released?
It turns out to be wonderfully simple. You see, CFDictionaryCreateMutable() is a C function. And C doesn't have a concept of reference counting built deep down into its core. So when you create a dictionary for use with C code, in a C-only program, you probably don't want the dictionary to try to send messages to pointers which are not really Objective-C objects.
And as we have demonstrated each CFDictionary is actually an NSDictionary.
If you are using a C function, it's a good idea to actually default to C behavior: no retaining and no releasing. It might also be a good idea to allow one to use a third-party reference counting mechanism?
That's exactly what was done here. When calling CFDictionaryCreateMutable(), you feed it an allocator which can be used to allocate memory instead of the default one, the default capacity (just like -initWithCapacity:), and two pointers which describe just how the dictionary should behave when retaining, releasing, describing, copying, hashing and comparing values and keys.
First thing I did, and that seems to work quite well, is just pass NULL for the last two pointers. That is, it works quite well when your keys are constant strings which won't be released that easily. I haven't experienced a crash even when they aren't, but let's not risk it.
So let's see.
NSMutableDictionary * ourDictionary = (NSMutableDictionary*)CFDictionaryCreateMutable(nil, 0, NULL, NULL);
Good, but let's improve it by passing a pointer to a default structure for copying and releasing keys. Note that NSMutableDictionary also copies its keys. Exploring why it does so should be an exercise for the reader.
NSMutableDictionary * ourDictionary = (NSMutableDictionary*)CFDictionaryCreateMutable(nil, 0, &kCFCopyStringDictionaryKeyCallBacks, NULL);
Now our keys are copied and released where appropriate, while the values are left untouched.
Optionally, explore using kCFTypeDictionaryKeyCallBacks in situations where your keys may be other CFType-derived objects. (That is, not just CFStrings/NSStrings.) Don't use this if there is even a remote chance of your key being a mutable object.
Autosaving Core Data managed object context
Tuesday, October 4th, 2011This has nothing to do with Lion autosaving. It has everything to do with the fact that Apple’s template for Core Data saves the managed object context only at exit.
I’m proposing the following solution. Note that while you’re at it, you may want to move most of Core Data related code that Apple’s “shoebox” Core Data template puts in the AppDelegate. Put it in a singleton class called Database. Expose +sharedDatabase, and #define DB [Database sharedDatabase] in the header. This was somewhat unrelated, but it’s worth mentioning.
Back to autosaving.
We’ll use NSNotificationCenter and we’ll observe for the notification that our managed object context has changed.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleManagedObjectContextChange:)
name:NSManagedObjectContextObjectsDidChangeNotification
object:managedObjectContext];
Then we’ll save after some time, but ignore any errors. Delay is important because notification occurs while the context is dirty, and you cannot really save it at that time. Scheduling a timer is a good way to delay saving a bit to occur later when the execution comes to the runloop. Plus, it also allows us to group several changes by letting us cancel the timer in case another change flows in.
I added an NSTimer *saveDelayTimer to instance variables of my Database class. If you are keeping the managed object context in your app delegate, you can add it there, too.
So let’s take a look at implementations of notification handler, and timer handler.
-(void)handleManagedObjectContextChange:(NSNotification*)note
{
/*
NSSet *updatedObjects = [[note userInfo] objectForKey:NSUpdatedObjectsKey];
NSSet *deletedObjects = [[note userInfo] objectForKey:NSDeletedObjectsKey];
NSSet *insertedObjects = [[note userInfo] objectForKey:NSInsertedObjectsKey];
*/
[saveDelayTimer invalidate];
[saveDelayTimer release];
saveDelayTimer = [[NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:@selector(quickSave:)
userInfo:nil
repeats:NO] retain];
}
-(void)quickSave:(id)userInfo
{
// save without UI-displayed errors
NSError *error = nil;
[managedObjectContext save:&error];
if(!error)
NSLog(@"Quicksave successful");
else
NSLog(@"Quicksave failed: %@", error);
[saveDelayTimer invalidate];
[saveDelayTimer release];
saveDelayTimer = nil;
}
UPDATED Oct 5th 2011, 13:16
However, this is bad.
Upon saving managedObjectContext, any currently-being-edited, but bound-via-Cocoa Bindings text fields will get unfocused. Let’s keep the focus, text selection and scroll offset!
-(void)quickSave:(id)userInfo
{
// store focus and selection
IRAppDelegate *appDelegate = [NSApp delegate];
NSTextField * focusedTextField = nil;
NSTableView * focusedTableView = nil;
NSInteger focusedTableViewColumn = 0, focusedTableViewRow = 0;
NSRange selection;
NSRect visibleRect;
if([appDelegate.mainWindowController.window.firstResponder isKindOfClass:[NSText class]])
{
NSText * textBox = (NSText*)appDelegate.mainWindowController.window.firstResponder;
NSTextField * textField = [textBox parentTextField];
if(textField)
{
// there is a text field that's focused
focusedTextField = textField;
selection = [textBox selectedRange];
visibleRect = [textBox visibleRect];
}
NSTableView * tableView = [textBox parentTableView];
if(tableView)
{
// there is a table view that's focused
focusedTableView = tableView;
focusedTableViewColumn = [tableView editedColumn];
focusedTableViewRow = [tableView editedRow];
selection = [textBox selectedRange];
visibleRect = [textBox visibleRect];
}
}
[appDelegate.mainWindowController.window endEditingFor:nil];
// save without UI-displayed errors
NSError *error = nil;
[managedObjectContext save:&error];
if(!error)
NSLog(@"Quicksave successful");
else
NSLog(@"Quicksave failed: %@", error);
[saveDelayTimer invalidate];
[saveDelayTimer release];
saveDelayTimer = nil;
// restore selection
[focusedTextField becomeFirstResponder];
[[focusedTextField currentEditor] setSelectedRange:selection];
[[focusedTextField currentEditor] scrollRectToVisible:visibleRect];
[focusedTableView becomeFirstResponder];
[focusedTableView editColumn:focusedTableViewColumn row:focusedTableViewRow withEvent:nil select:YES];
[[focusedTableView currentEditor] setSelectedRange:selection];
[[focusedTableView currentEditor] scrollRectToVisible:visibleRect];
}
This uses a small category for finding the owner of NSText (the actual textbox that appears when you begin editing table view or a text field).
// NSText+IRFindParentTextOwner.h #import <AppKit/AppKit.h> @interface NSText(IRFindParentTextOwner) -(NSTextField*)parentTextField; -(NSTableView*)parentTableView; @end
// NSText+IRFindParentTextOwner.m
#import "NSText+IRFindParentTextOwner.h"
@implementation NSText(IRFindParentTextOwner)
-(NSTextField*)parentTextField
{
NSText *textBox = self;
for (NSResponder *parent = textBox.nextResponder; parent; parent = parent.nextResponder)
{
if([parent isKindOfClass:[NSTextField class]])
{
return (NSTextField*)parent;
}
}
return nil;
}
-(NSTableView*)parentTableView
{
NSText *textBox = self;
for (NSResponder *parent = textBox.nextResponder; parent; parent = parent.nextResponder)
{
if([parent isKindOfClass:[NSTableView class]])
{
return (NSTableView*)parent;
}
}
return nil;
}
@end
Getting year, month and day out of NSDate
Monday, September 26th, 2011For some reason, Apple didn’t provide an easy way to extract day, month and year from an NSDate. Probably because they think OS X shouldn’t be Western-centric and presume a Gregorian calendar (although they could have simply extracted this data from current locale). Here is a simple category that allows just that.
// NSDate+IVDateComponents.h #import <Cocoa/Cocoa.h> @interface NSDate (IVDateComponents) @property (nonatomic, readonly) NSInteger year; @property (nonatomic, readonly) NSInteger month; @property (nonatomic, readonly) NSInteger day; @end
// NSDate+IVDateComponents.m
#import "NSDate+IVDateComponents.h"
@implementation NSDate (IRDateComponents)
-(NSInteger)year
{
unsigned units = NSYearCalendarUnit;
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [calendar components:units fromDate:self];
return [components year];
}
-(NSInteger)month
{
unsigned units = NSMonthCalendarUnit;
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [calendar components:units fromDate:self];
return [components year];
}
-(NSInteger)day
{
unsigned units = NSDayCalendarUnit;
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [calendar components:units fromDate:self];
return [components day];
}
@end
Note that this code is very Western-centric and always presumes the Gregorian calendar. Sue me.
Developing Objective-C apps for Android using Mac OS X
Friday, June 24th, 2011
I’m no fan of Java, and in fact, I’m not a fan of Android. When I originally heard Google is working on a Linux phone, I rejoiced. When I heard that Java would be the base of the userland, and that no existing program for Linux would be directly supported, my heart sank. In the meantime I became a big fan of Objective-C, Cocoa, Cocoa Touch, Mac, and all related technologies and projects.
So, I want to keep working in Objective-C. I sat down and studied my options. We have the Android SDK, we have the Android NDK, and a third party offering called Android NDK GCC 4.2.1 with Objective-C support.
Studying all this takes a while. Well, more than a while. I spent a day or two wrapping my head around all this, reading Android documentation. All this not counting stuff that I read, heard and discussed in previous months on this subject.
Android SDK is documented well enough, as long as you stick to Java. Android NDK is not particularly well documented, but solidly enough. Playing with the Objective-C is however a bit more complex, especially since Android NDK by itself does not come with Objective-C support turned on. Authors of the add-on compiler for Objective-C did not publicly document its proper use at all. Its use is nearly ungoogleable.
Since I’d hate to see you, my little lemon drops, spend as much time as I did on studying all this, here is something that will help you understand the complexities of the design of NDK, and how to combine all this with the Objective-C compiler.
Proficiency with GNU Make and Objective-C is highly recommended.
Proficiency with Java and Android is not required (I have none).
Not much in this article depends on Mac OS X apart from the paths, and the fact that there is no prebuilt Objective-C compiler for platforms other than Mac OS X. Parts that are about SDK and NDK should cleanly apply to Linux version of the Android SDK and NDK. It probably cannot easily apply to Windows.
Update on December 7, 2011: I just learned about a great presentation by Jackie Gleason (@LifeIsTooShort) on the same subject: Adding Objective-C Support to the Android NDK
If you wish to do so, you can donate me via PayPal for writing this PDF. Definitely not mandatory, though!
You can also send me other amounts directly via PayPal to address: ivucica@gmail.com
Avoiding memory leak in OpenAL and crash in OpenAL for Mac
Friday, June 10th, 2011UPDATE: We’re still seeing the crash on Mac. Procedure described does fix the memory leak, though.
UPDATE 2: Crash on Mac is caused by what appears to be a bug in Apple’s code relating to queueing commands for execution on dedicated audio thread, and mutex lock breaking down. Since mutex lock seems to stop working, it’s only natural that a threading-related crash occurs.
When calling alSourceStop(), you might forget to unbind a buffer from the source. Did you unbind it?
alSourcei(this->sourceId, AL_BUFFER, AL_NONE);
If you get a crash on Mac with call stack containing OALSource::Play() and/or ending with OALSource::PrepBufferQueueForPlayback(), this is most probably a good fix. Looks like OpenAL on Mac might have a race condition somewhere unless you do this.
See difference between revision 293 and 294 in libxal, in file audiosystems/OpenAL/OpenAL_Player.cpp.
Simple multiuser chat for POSIX systems
Wednesday, November 24th, 2010Here’s a little multiuser chat server written for various POSIX-compatible operating systems. Written and tested on Mac OS X 10.6, but it should work on your favorite Linux, too.
Placed in public domain, use it for whatever you want (since it’s so simple). 177 lines of pure C powah, dood.
Code follows after the break.
(more…)

