Advertising

NSMutableDictionary without automatic retaining of contained objects

There 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 subclassing NSObject
  • 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.


rest of the post
About me

Developing Objective-C apps for Android using Mac OS X

** Unpaid mini-ad (Oct 31st 2012): ** Check out Yeecco's StellaSDK. From my experiments with Stella and from interaction with the company, they may be a good choice if you need an easy-to-use solution right now, with as little work as possible. The PDF has not been maintained, and I have not experimented with improving the procedure; it may be good for improving the understanding of the problems, but if you need something that'll work right now, talk to Yeecco -- especially if you want to easily port a Cocos2d game. ** End of mini-ad **

** Unpaid mini-ad #2 (May 27th 2013): ** Another company that provides an SDK for easier porting of iOS apps to Android is Apportable. They have a free starter SDK (check out their plans) -- and that's the extent of my familiarity with their product, for now :-) ** End of mini ad #2 **

** Clang in Android NDK! (Mar 20th 2013): ** Android NDK is now shipping with Clang. Additionally, there's also some work on getting GNUstep Base to build for Android. Sweet! I'll update this post with a link to additional information once this is proven to work okay.

** Rebuilding GCC (Jan 8th 2013): ** Instead of downloading prebuilt GCC, try rebuilding it. - Instructions blogpost (with various reference links) - Great presentation by Jackie Gleason ** End of rebuilding GCC **

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.

SMALL UPDATE, May 8th 2012: I have not tested this, but here's the CrystaX .NET Improved Android NDK. Thanks to jeffamaphone for pointing it out. I did not test it, but r7 ships with GCC 4.6+.

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!

Donation choice

You can also send me other amounts directly via PayPal to address: ivucica@gmail.com


rest of the post

What I'm missing in Xcode4?

I'm a big fan of Xcode3. Xcode4 is a step in the right direction for me, though. Not so much as it would be when I started with Mac and iOS development, but still, it's ok.

However, there are large omissions and important bugs that are heavily influencing my productivity.

  1. Removed Right-click, Find In Documentation. (Update on April 1st 2011, 16:42 CET: Alt+left-click is a replacement for this.)
  2. Removed Command+shift+up to switch between header and source. Assistant views are not a replacement since I work on Macbook, which doesn't have all that much screen real-estate, especially, when you have the File Navigator on the left. (Update on July 12th 2011, 16:21 CET: Use Ctrl+cmd+up, or three-fingers-down-to-up touchpad gesture.)
  3. No ability opening multiple Get Info dialogs on the screen for different project Targets. In fact, Get Info was removed and replaced with (admittedly superior) way of editing build settings.
  4. When autocomplete lists tons of options, Page-down (Fn+Down) does not work. That's right, you can't scroll over a screenful of symbols at a time.
  5. Command+shift+b has been reassigned to … get this … Build & Analyze. Ok, that needed a shortcut (maybe), but Command+shift+b used to be the shortcut to open "build progress" output dialog.
  6. Build progress is now assigned a navigator; that is, hit Command+7 to get it. However… the Editor view does not automatically focus on latest build progress and.
  7. Closely related to previous item: there is no obvious shortcut for switching focus between Editor and Navigator. I really want to quickly choose a file, to quickly choose a build log, and to quickly choose an issue from the list. While this is not something that used to exist in Xcode3 (or at least I couldn't find it) it is still something that would be highly useful. Open Quickly - Command+Shift+O - is not a substitute.
  8. I really miss the old "Groups & Files" view. Not a big deal, but having that as an alternative to the new Navigators view would be excellent.
  9. While autocomplete got even better, Command+doubleclick is extremely dumbed down and cannot guess that in [[NSString alloc] initWithString:@"something"]; attempting to find initWithString in header probably means NSString's -initWithString:, right? Well, if you have another initWithString: in another class, Xcode4 will ask you which one you refer to (despite [NSString alloc] being declared to return NSString, thus there being no dillema whose -initWithString: needs to be used).
  10. Despite introducing tabs, they are next to useless: hard to open, and with no obvious keyboard shortcuts to switch tabs or close tabs.
  11. added March 18 2011, 14:12 Oh. Right-click, Add Files to "projectname.xcodeproj" does not take into account parent group path anymore. That means, despite configuring that pesky Window Systems/iOS group to point to path "relative to group" and pointing to "windowsystem/iOS" filesystem folder, Add Files dialog will no longer default to that folder. Meaning I nevertheless have to dig around the filesystem to find the relevant files.
  12. added March 18 2011, 14:40 You can no longer easily access full path to a currently open file by right-clicking on the titlebar. This is important in case error log refers to system-wide installed header file, which you go and happily change without affecting header file that you should be changing -- the one in a subproject.

These are just some omissions that significantly reduce my productivity compared to Xcode3. I sincerely hope they will be patched by Apple, otherwise I'll simply have to do without them. There's no other way: iOS devs (and to some extent Mac devs) are hostages of the latest SDK which ships only with the latest IDE.


rest of the post

Upotreba Objective-C u igrama; moje mišljenje

A short Croatian language opinion post on use of Objective-C in games

Dobio sam nedavno pitanje o tome da li se isplati učiti Objective-C (u kontekstu igara).

Moje je mišljenje da Objective-C ima deset puta logičniju internu strukturu nego C++, te da je svojom kombinacijom karakteristika dinamičnih i statičnih jezika izuzetno pogodan za pisanje igara. Primjerice, evo stvaranje kapitalnog svemirskog broda koristeći string, i spremanje istog u SvemirskiBrod:

NSString* nekaj = @"KapitalniSvemirskiBrod"; 
SvemirskiBrod *nekiBrod =  [[NSClassFromString(nekaj) alloc] initWithPosition:CGPointMake(10, 10)];

Ovo je jako dobro jer se ovakva XML specifikacija mape:


  

može lako učitati s primjerice (ako imamo teoretsku klasu XMLNode i varijablu trenutniNode):

  NSString *tipBroda = [trenutniNode atribut:tip];
  CGPoint pozicija = CGPointMake([trenutniNode atribut:x], [trenutniNode atribut:y]);
  Class klasaBroda = NSClassFromString(tipBroda);
  SvemirskiBrod *brod = [[klasaBroda alloc] initWithPosition:pozicija];

Tu su i druge mogućnosti, tipa lakše pozivanje funkcije nad objektom u nekom mini-skriptnom jeziku koji smisliš (bez razmišljanja o statičkim funkcijama, callbackovima, member-function-pointerima i slično, kao u C i C++).

Pozivanje nepostojeće funkcije (npr [svemirskaStanica udjiUHyperspace];) je samo warning, što znači da se tebi vjeruje da će ta funkcija postojati u toj varijabli. Ako ne postoji, onda će se program skršiti. Postoji provjera da li neka varijabla sadrži neku funkciju.

Ekvivalent std::vectora je NSArray koji unutra može spremati bilo kakve Objective-C objekte. Znači ne moraš templateove slagati i forsirati da se unutra nalaze samo, primjerice, NSStringovi, nego se unutra mogu nalaziti i SvemirskaStanica i SvemirskiBrod i KapitalniSvemirskiBrod.

Uglavnom, iz svega navedenog vidi se da smatram da je Objective-C odličan jezik koji spaja najbolje stvari iz Pythona i C/C++. S obzirom da se u Objective-C može pisati i za Windowse i za Linux koristeći Cocotron i GNUstep, te konačno za Mac i iPhone koristeći Appleove Cocoa i Cocoa Touch, mislim da je to dovoljno portabilan jezik za razvoj igara.

Što se tiče aplikacija, odlično je i za izradu toga, ali relativno manje portabilno ako gledamo naprednije mogućnosti frameworka.

Dakle, da, isplati se :-)


rest of the post

GKTapper - Apple's buggy example

I've caught a few mistakes in GKTapper. Read on to see how the sample performs invalid caching of achievements and to see how it shows incorrect string as leaderboard description in one place. Invalid caching of achievements

Achievements in -submitAchievement:percentComplete: will never be cached. Also, the comment makes little sense; a sentence appears to be missing.

- (void) submitAchievement: (NSString*) identifier percentComplete: (double) percentComplete
{
	//GameCenter check for duplicate achievements when the achievement is submitted, but if you only want to report
	// new achievements to the user, then you need to check if it's been earned
	// before you submit.  Otherwise you'll end up with a race condition between loadAchievementsWithCompletionHandler
	// and reportAchievementWithCompletionHandler.  To avoid this, we fetch the current achievement list once,
	// then cache it and keep it updated with any new achievements.
	if(self.earnedAchievementCache == NULL)
	{
		[GKAchievement loadAchievementsWithCompletionHandler: ^(NSArray *scores, NSError *error)
		{
			if(error == NULL)
			{
				NSMutableDictionary* tempCache= [NSMutableDictionary dictionaryWithCapacity: [scores count]];
				for (GKAchievement* score in tempCache)
				{
					[tempCache setObject: score forKey: score.identifier];
				}
				self.earnedAchievementCache= tempCache;
				[self submitAchievement: identifier percentComplete: percentComplete];
			}
			else
			{
				//Something broke loading the achievement list.  Error out, and we'll try again the next time achievements submit.
				[self callDelegateOnMainThread: @selector(achievementSubmitted:error:) withArg: NULL error: error];
			}

		}];
	}
	else
}

Where's the bug? Code iterates over the just-created, empty dictionary "tempCache". Corrected:

				for (GKAchievement* score in scores) // this used to read tempCache

Leaderboard description invalid

Code in method mappedPlayerIDToPlayer:error: contains a small bug:

		self.leaderboardHighScoreDescription= @"GameCenter Scores Unavailable";
		self.leaderboardHighScoreDescription=  @"-";

It should probably read:

		self.leaderboardHighScoreDescription= @"GameCenter Scores Unavailable";
		self.leaderboardHighScoreString=  @"-";


rest of the post

Getting Objective-C 2.0 to work on Debian's GNUstep with clang

If you are a Cocoa or Cocoa Touch developer, you may have attempted to use features such as properties in GNUstep, only to be surprised that these don't seem to be supported. This is because these are Objective-C 2.0 features.

To get the new features, the only way is to use a different compiler called clang. You may have seen this compiler used in newer releases of Xcode. This is a compiler that targets a virtual machine called LLVM before producing native code.

UPDATE May 4th 2011: GCC 4.6 has got the Objective-C 2.0 treatment, and since Debian includes GCC 4.6, I'd recommend you to try compiling your software that way. Not because it's a better compiler -- I have no idea which one works better -- but because it's there. Also, consider compiling GNUstep from trunk using GCC 4.6; it's rather easy to do. (CC=gcc-4.6 ./configure, whenever compiling a component of GNUstep).

Let's presume you managed to run an Objective-C program with GNUstep; that is, let's presume you are aware of Project Center, or GNUmakefiles. If you are didn't use GNUmakefiles, you should know that Project Center generates these in order to build your app.

Now you want to switch to clang, and you want to do so on your favorite operating system, Debian GNU/Linux. Step 1:

apt-get install clang

Step 2: Since some GNUstep files refer to , we need to have that file installed as well. Sadly it's not installed as part of any runtime; it is, instead, installed as part of gobjc-x.x package. This may change by the time you use these instructions; see: http://packages.debian.org/file:objc.h for a list of packages that contain this file. As of Dec 6th 2010, this is the package that needs to be installed:

apt-get install gobjc-4.3

You could also install gobjc-4.2, but let's go with 4.3 for now.

Step 3: We need to tell GNUstep to use the new compiler. We'll need to edit one of template makefiles that GNUmakefile files use.

sudo $EDITOR /usr/share/GNUstep/Makefiles/config.make

Yes, that's right: the easiest way I could find involves changing stuff in /usr (meaning upon package upgrade this might get overwritten -- go figure).

Step 4: Just below the license text, you should find the section "Binary and compile tools" which sets various variables such as CC, OBJCFLAGS, et cetera. Edit the relevant lines accordingly!

CC = clang
OBJCFLAGS = -I/usr/lib/gcc/i486-linux-gnu/4.3/include
CPP = clang -E

(Note the fugly path that we add to include path by setting OBJCFLAGS: We need to give clang the path to objc/objc.h; if one of them is different, edit the architecture name and gobjc version accordingly)

That's it! @property, @synthesize and other friends you made while using Objective-C 2.0 under OS X are back in da hood, makin' programming enjoyable once again. Plus, you get the extra build speed that clang provides, being somewhat faster than GCC.


rest of the post