0xced

Saturday, August 18, 2012

If you tried to debug your app with zombies enabled under ARC, you may have noticed that you don’t get zombies anymore. That’s most probably because of a bug in the Foundation framework that affects iOS 5 and OS X 10.7. This bug prevents ARC from "cleaning up" instance variables at deallocation-time according to Apple Technical Q&A QA1758. Apple strongly encourages you to run your app on an iOS 6+, or OS X 10.8+ system when debugging with Zombies.
In the meantime, you can workaround this bug if you are using ARC and trying to debug zombies on older systems. Just compile this NSObject+ARCZombie.m file in your project and ivars will be automatically deallocated under ARC when zombies are enabled, as it should be.
Now you might think Hey, you are swizzling dealloc, isn't that dangerous? Well, yes it is, but note that the dealloc method is only swizzled when the NSZombieEnabled environment variable is enabled, so even if this code slips in your release build, il will cause no harm at all.

So I investigated, and found a pretty elegant solution. Without further ado, here is how to change the size of the iOS simulator in order to test your apps in resolutions never seen before.

Download File.txt into ~/Library/Application Support/iPhone Simulator (don't change the name of File.txt)

Edit /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Applications/iPhone Simulator.app/Contents/Resources/Devices/iPhone (Retina).deviceinfo/Info.plist and add the following keys:

Monday, February 14, 2011

Xcode 3.2.3 documentation browser was quite broken: ⌘ + ⌥ + double click on a method or function would open the documentation browser but would not scroll to the method you just asked documentation for. So I started to write an Xcode plugin to open Ingredients instead. Then Xcode 3.2.5 was released (I skipped version 3.2.4) and this annoying bug was fixed, so I did not bother to publish my plugin. But recently, I readontwitterthat [RED4CTED] documentation browser was even more broken, so I decided it was time to release my Xcode-Ingredients plugin.

Note that the plugin is written for Xcode 3, so things have probably changed in [RED4CTED] (which I have not yet tried). Ifyouarebraveenoughtouse [RED4CTED], that you know a bit about Xcode plugins (@kodz, @rentzsch, @tjw?) and that you would like to use Ingredients for reading the documentation, please have a look and adapt the plugin for [RED4CTED]. In order to discover the searchForAPIString: method, I just set a breakpoint on makeKeyAndOrderFront: and looked at the backtrace, but I'm not sure if this technique is still applicable.

I will update this blog post as soon as someone can get the plugin to work with [RED4CTED].

Wouldn't it be nice if instead you could use [aLocation distanceFromLocation:anotherLocation] everywhere in your code, while still retaining backward compatibility? Enter the Objective-C runtime! In your main.m file, first #import <objc/runtime.h> and at the very beginning of your main function, add this:

Here you go, -[CLLocation distanceFromLocation:] available at runtime in any iOS version with only two lines of code. Note that on iOS 3, class_addMethod will add the distanceFromLocation: method to the CLLocation class using getDistanceFrom: implementation. On iOS 4, class_addMethod will do nothing as the method already exists.

Friday, July 02, 2010

In iPhone SDK 4 Apple has removed all 3.x SDKs. I think that was not a very good idea to say the least. I can understand Apple wants developers to adopt the new features of iOS 4, but breaking zillions of Xcode projects is probably not a good way to win the hearts of those developers. Well, this is probably not Apple's goal anyway.

Sure there is a way to get your projects working again with the iPhone SDK 4, just set the Base SDK of your project to iOS 4:

and set the Deployment Target to iOS 3:

This will get your projects compile and run again on both iOS 3 and 4. That's fine. Well, except when you want to make sure your project do not use any new iOS 4 only API.

The problem is, code like this will now compile without any warning or error:

It's easier than you think to use a shiny new API without even noticing you are using something not available on iOS 3.﻿ This setURL:forKey: method is new in iOS 4 so this code will fail with an unrecognized selector exception when run on iOS 3﻿. Unfortunately, the only way to catch this kind of oversight﻿ is to run it on an iOS 3 device. Or you can read the documentation of every single method and function you are calling to make sure it was not introduced in iOS 4. But this does not seem very reasonable, does it? The tools should help us detecting these problems at compile time, not runtime!

Here is how to get Xcode 3.2.3 and iPhone SDK 4﻿ working with SDK 3.1.3.

Add a User-Defined Setting GCC_OBJC_ABI_VERSION﻿ and set its value to 1 (one)

Select GCC_OBJC_ABI_VERSION and choose Add Build Setting Condition from the gear pop-up button at the lower left of the window.

Select Any iPhone Simulator and Any Architecture

There you go! By selecting the SDK 3.1.3 Check configuration, you get compile time check for misusing iOS 4 only APIs when targeting iOS 3.

Note that compiling with the iPhone Simulator 3.1.3 SDK ﻿and running in Xcode 3.2.3 simulator is not supported. What you get with this hack is just a compile time check of the APIs you are using. So do not expect to run your app on a 3.1.3 simulator.

Notice how the 'F' of MyField was lowercased, that sucks! If the HTTP server you are trying to communicate with is case-sensitive, you are screwed. I filed radar #8029516, which is a duplicate of radar #3131623, which means there is almost no chance to have this fixed anytime soon.

When this happens, you should contact the server administrator asking for a case-insensitive implementation. But in the meantime, if you badly need to preserve the case of your headers, read on.

The investigation

First, let's fire otx to disassemble the Foundation framework in order to have a look at setValue:forHTTPHeaderField: implementation. I strongly suggest you to use otx version from subversion trunk, as it is better at resolving symbols in tail call optimizations (fixed in r553).

_CFCapitalizeHeader looks like a very good candidate for being the bastard changing the case of our headers. A quick search reveals the source code of CFNetwork that was open source long time ago. Although the open source implementation does not exactly match what we see in the disassembly (it is now using toupper instead of ch + 'A' - 'a' for example), we are now absolutely sure that _CFCapitalizeHeader is the function responsible for messing with our headers.

Now, let's check is if there is a path that will not call _CFCapitalizeHeader and if we can somehow influence the condition that would avoid the call to _CFCapitalizeHeader. This is quickly checked, especially if you enabled the Separate logical blocks option of otx (-b option for cli).

We see that there is no path that avoid the call to _CFCapitalizeHeader. So we are left with the last resort solution: patching _CFCapitalizeHeader. With APE Lite, function patching is very easy. You first use APEFindSymbol() to find the address of a non-exported symbol (i.e. __CFCapitalizeHeader), then APEPatchCreate() to replace a function implementation with your own, while still keeping a reference to the original implementation. On iPhone OS, you can use APE Lite+arm (my implementation of the APE Lite API using MobileSubstrate).