Blended Cocoa

Adventures in Objective-C, Swift and Cocoa

__FILE__, __FUNCTION__, __LINE__ and __COLUMN__ as Defaults in Swift

I was experimenting with methods of debug logging in Swift and came across an interesting find. If you use the __FILE__, __FUNCTION__, __LINE__ and __COLUMN__ “macros” as default function parameter values in Swift the value is set to the file, function etc of the caller rather than the details of the called functions location.

This means you can automatically pass information about a calling function by providing default values. For example:

1
2
3
4
5
6
7
func DLog(message: String, file: String = __FUNCTION__, function: String = __FILE__, line: Int = __LINE__, column: Int = __COLUMN__) {
  println("\(file) : \(function) : \(line) : \(column) - \(message)")
}

func applicationDidFinishLaunching(aNotification: NSNotification?) {
  DLog("App Did Finish Launching")
}

This will display a message of the form:

1
.../AppDelegate.swift : applicationDidFinishLaunching : 16 : 14 - App Did Finish Lauching

Clearly __FILE__, __FUNCTION__, __LINE__ and __COLUMN__ are not implemented using a preprocessor but then we knew that already.

Supporting Swift in CodeRunner

I like to use CodeRunner to test out quick snippets of code. I have added Swift as a supported language. I did this by adapting the Objective-C compile.sh script that is used by CodeRunner to compile Objective-C with clang. Hopefully the next release of CodeRunner will support Swift out of the box with a better compiler script and syntax highlighting.

In the CodeRunner Preferences add a new language, check the Language uses compilation script checkbox and edit the script to contain the following:

compile.shView Gist
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#!/bin/bash
# This is a CodeRunner compilation script. Compilation scripts are used to
# compile code before being run using the run command specified in CodeRunner
# preferences. This script should have the following properties:
#
# Launch directory ($PWD):      Will be the same as the file being run
#
# Exit status:                  Should be 0 on success (will cause CodeRunner
#                               to continue and execute the run command)
#
# Output (stdout):              On success, one line of text which can be accessed
#                               using the $compiler variable in the run command
#
# Output (stderr):              Anything outputted here will be displayed in
#                               the CodeRunner console
#
# Arguments:                    $1      Filename of the source file
#                               $2      Encoding of the source file
#                               $3      Compilation flags set in CodeRunner preferences
#                               $4      Path of a temporary directory (without / suffix)
#
# The encoding argument may be used to specify to the compiler which encoding
# the source file uses. It will be one of the integers in the following array:

enc[4]=""       # UTF-8
enc[10]=""      # UTF-16
enc[5]=""       # ISO Latin 1
enc[9]=""       # ISO Latin 2
enc[30]=""      # Mac OS Roman
enc[12]=""      # Windows Latin 1
enc[3]=""       # Japanese (EUC)
enc[8]=""       # Japanese (Shift JIS)
enc[1]=""       # ASCII

compname=`echo "$1" | sed 's/\(.*\)\..*/\1/'`

xcrun swift -sdk $(xcrun --show-sdk-path --sdk macosx) "$1" -o "$compname" $3

status=$?
if [ $status -eq 0 ]
then
echo $compname
exit 0
elif [ $status -eq 127 ]
then
echo -e "\nTo run code in this language, you need to have compilers installed. These are bundled with Xcode 6 or later which can be downloaded through the Mac App Store or Apple's developer site."
fi
exit $status

The Run Command needs to be set to ./$compiler.

I set my Code Template to the following to include all the Cocoa goodness and set the file extension to swift:

Template
1
2
3
import Cocoa

%@

Finally, you’ll probably want to change New Language to Swift by right-clicking in the Language list.

Here is a screenshot of the settings:

In order to allow the xcrun command in the compilation script to find swift you need to ensure that xcode-select is pointing at a version of Xcode that includes the swift compiler, e.g.

1
sudo xcode-select -s /Applications/Xcode6-Beta.app/Contents/Developer

Mavericks as an iBeacon

With the release of Mavericks, Apple have brought the capabilities of Core Bluetooth in line with those of iOS. In particular Mavericks can now function as a peripheral thanks to the inclusion of the CBPeripheralManager class.

However, Apple have chosen not to include support for iBeacons in Mavericks. iBeacon support on iOS 7 has been implemented as part of the Core Location framework but Core Location on Mavericks doesn’t contain any iBeacon support.

iBeacons are built on top of Core Bluetooth so I wondered whether it would be possible to use a MacBook running Mavericks to create an iBeacon.

iOS as an iBeacon

On iOS we can use a Bluetooth 4 enabled device to create an iBeacon using the following code:

BLCAppDelegate.mlink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//
//  BLCAppDelegate.m
//  Beacon
//
//  Created by Matthew Robinson on 15/09/13.
//  Copyright (c) 2013 Blended Cocoa. All rights reserved.
//

#import "BLCAppDelegate.h"

#import <CoreLocation/CoreLocation.h>
#import <CoreBluetooth/CoreBluetooth.h>

@interface BLCAppDelegate () <CBPeripheralManagerDelegate>

@property (strong,nonatomic) CBPeripheralManager *peripheralManager;

@end

@implementation BLCAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    _peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self
                                                                 queue:nil];
    return YES;
}

- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
    if (peripheral.state == CBPeripheralManagerStatePoweredOn) {

        NSUUID *proximityUUID = [[NSUUID alloc] initWithUUIDString:@"A7C4C5FA-A8DD-4BA1-B9A8-A240584F02D3"];

        CLBeaconRegion *region = [[CLBeaconRegion alloc] initWithProximityUUID:proximityUUID
                                                                         major:1
                                                                         minor:1000
                                                                    identifier:@"com.blendedcocoa.beacon"];

        NSDictionary *proximityData = [region peripheralDataWithMeasuredPower:nil];


        [peripheral startAdvertising:proximityData];
    }
}

- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error {
    NSLog(@"Started advertising");
}

Creating an iBeacon on iOS is quite simple. First we create a CLBeaconRegion with the required proximityUUID, major & minor and then use the peripheralDataWithMeasuredPower: method to get an NSDictionary containing the advertising data to be passed to Core Bluetooth.

Advertisement Data

It occurred to me that it may be possible to take this NSDictionary and pass it to a CBPeripheralManager instance on Mavericks.

Using NSKeyedArchiver and NSKeyedUnarchiver it was easy to transfer the NSDictionary to an OS X app running on Mavericks and passing the dictionary to a CBPeripheralManager resulted in an iBeacon that could be detected by an iOS device.

Obviously it isn’t very convenient to create the adverisement data on iOS and then transfer the archived data to an OS X app so the next step was to investigate to see exactly what was being stored in the dictionary.

1
2
3
{
    kCBAdvDataAppleBeaconKey = <a7c4c5fa a8dd4ba1 b9a8a240 584f02d3 00040fa0 c5>;
}

The dictionary key is the NSString literal kCBAdvDataAppleBeaconKey. The value is an NSData object contaning the proximityUUID, major, minor and the calibrated measured power (in 2’s complement). It wasn’t very hard to take this information and create a class to generate the advertisement dictionary directly on OS X.

BLCBeaconAdvertisementData.hlink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//
//  BLCBeaconAdvertisementData.h
//  BeaconOSX
//
//  Created by Matthew Robinson on 1/11/2013.
//  Copyright (c) 2013 Blended Cocoa. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface BLCBeaconAdvertisementData : NSObject

@property (strong,nonatomic) NSUUID *proximityUUID;
@property (assign,nonatomic) uint16_t major;
@property (assign,nonatomic) uint16_t minor;
@property (assign,nonatomic) int8_t measuredPower;

- (id)initWithProximityUUID:(NSUUID *)proximityUUID
                      major:(uint16_t)major
                      minor:(uint16_t)minor
              measuredPower:(int8_t)power;


- (NSDictionary *)beaconAdvertisement;

@end
BLCBeaconAdvertisementData.mlink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//
//  BLCBeaconAdvertisementData.m
//  BeaconOSX
//
//  Created by Matthew Robinson on 1/11/2013.
//  Copyright (c) 2013 Blended Cocoa. All rights reserved.
//

#import "BLCBeaconAdvertisementData.h"

@implementation BLCBeaconAdvertisementData

- (id)initWithProximityUUID:(NSUUID *)proximityUUID major:(uint16_t)major minor:(uint16_t)minor measuredPower:(int8_t)power {
    self = [super init];

    if (self) {
        self.proximityUUID = proximityUUID;
        self.major = major;
        self.minor = minor;
        self.measuredPower = power;
    }

    return self;
}


- (NSDictionary *)beaconAdvertisement {
    NSString *beaconKey = @"kCBAdvDataAppleBeaconKey";

    unsigned char advertisementBytes[21] = {0};

    [self.proximityUUID getUUIDBytes:(unsigned char *)&advertisementBytes];

    advertisementBytes[16] = (unsigned char)(self.major >> 8);
    advertisementBytes[17] = (unsigned char)(self.major & 255);

    advertisementBytes[18] = (unsigned char)(self.minor >> 8);
    advertisementBytes[19] = (unsigned char)(self.minor & 255);

    advertisementBytes[20] = self.measuredPower;

    NSMutableData *advertisement = [NSMutableData dataWithBytes:advertisementBytes length:21];

    return [NSDictionary dictionaryWithObject:advertisement forKey:beaconKey];
}

@end

Mavericks as an iBeacon

Finally, we are now able to create an iBeacon on OS X using the following code which is similar to the iOS version but uses BLCBeaconAdvertisementData instead of a CLBeaconRegion to create the adverisement.

BLCAppDelegate.mlink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//
//  BLCAppDelegate.m
//  BeaconOSX
//
//  Created by Matthew Robinson on 1/11/2013.
//  Copyright (c) 2013 Blended Cocoa. All rights reserved.
//

#import "BLCAppDelegate.h"

#import <IOBluetooth/IOBluetooth.h>

#import "BLCBeaconAdvertisementData.h"

@interface BLCAppDelegate () <CBPeripheralManagerDelegate>

@property (nonatomic,strong) CBPeripheralManager *manager;

@end

@implementation BLCAppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {

    _manager = [[CBPeripheralManager alloc] initWithDelegate:self
                                                       queue:nil];
}

- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {

    if (peripheral.state == CBPeripheralManagerStatePoweredOn) {

        NSUUID *proximityUUID = [[NSUUID alloc] initWithUUIDString:@"A6C4C5FA-A8DD-4BA1-B9A8-A240584F02D3"];

        BLCBeaconAdvertisementData *beaconData = [[BLCBeaconAdvertisementData alloc] initWithProximityUUID:proximityUUID
                                                                                                     major:5
                                                                                                     minor:5000
                                                                                             measuredPower:-58];


        [peripheral startAdvertising:beaconData.beaconAdvertisement];
    }
}

@end

That is all that is needed to create an iBeacon using a Bluetooth 4 enabled Mac running Mavericks. Fortunately, the Core Bluetooth framework on Mavericks understands what to do with the iBeacon advertisement data even though iBeacon support hasn’t been added to Core Location.

Source code is available on GitHub at https://github.com/mttrb/BeaconOSX.

Failing to Update the `pt_deny_attach` Kernel Module for Mountain Lion

Update 13th April 2013: This now works on Mountain Lion (10.8.3). Thanks to a tip, I have now worked around the problem of write-protected memory pages. See the GitHub repository for the working code.

Note: From the title of this post you will see that I ultimately failed to get the pt_deny_attach kernel module to work in Mountain Lion. This was due to write protected memory in the kernel. However, I did manage to work around the issues presented by Kernel Address Space Layout Randomisation (KASLR). The journey that I took to my ultimate failure may be useful and interesting to others.

I recently answered a question on Stack Overflow that led me to investigate the P_LNOATTACH process flag in Mountain Lion (OS X 10.8).

The P_LNOATTACH flag is a process flag which stops other processes (debuggers, DTrace etc) attaching to the flagged process. The flag is set by calling ptrace(PT_DENY_ATTACH). This feature was almost certainly added to OS X to stop/hinder reverse engineering the DRM used by iTunes. iTunes is certainly the oft-cited example of the use of PT_DENY_ATTACH.

In order to circumvent the protection provided by P_LNOATTACH a kernel module was created which intercepts the call to trace with the PT_DENY_ATTACH parameter. The most recent version of this module is for Lion (OS X 10.7.1) and is available on GitHub. The project page on GitHub says Snow Leopard but the code has been updated to Lion (10.7.1) as can be seen from the following code snippet from pt_deny_attach.c.

pt_deny_attach.cView on GitHub
1
2
3
4
5
6
7
8
9
10
11
/* This value is for OSX 10.7.1.  The exact _nsysent offset can be found
 * via:
 *
 *   nm -g /mach_kernel | grep _nsysent
 *
 * Due to a bug in the kext loading code, it's not currently possible
 * to link against com.apple.kernel to let the linker locate this.
 *
 * http://packetstorm.foofus.com/papers/attack/osx1061sysent.txt
 */
#define _NSYSENT_OSX_10_7_1_  0xffffff8000846eb8

The comment suggests a “bug in the kext loading code” stops you linking against com.apple.kernel. I believe this is intentional in order to make kernel hacks like this one more difficult.

The Lion module was based on an earlier version for Leopard. The above link provides additional background information about the ptrace flag and the kernel module.

The obvious first step to updating this kernel module to Mountain Lion was to download the version for Lion and load it into the kernel. This resulted in the first of many kernel panics. This was unsurprising as the layout of the kernel would almost certainly have changed between 10.7.1 (the version the module was targeted at) and 10.8.2 (the version I am currently running).

How does the Lion version work

Before we can start to update the kernel module to Mountain Lion we should really understand what the module is trying to do. Basically, the module is using pre-discovered offsets to find the sysent data structure which contains the function pointers to all of the syscall functions in the kernel. Once it has found the sysent table it changes the pointer to the ptrace function to point to a function, our_ptrace, supplied by the kernel module which checks to see if the parameter is PT_DENY_ATTACH.

pt_deny_attach.cView on GitHub
1
2
3
4
5
6
7
8
9
10
static int our_ptrace (struct proc *p, struct ptrace_args *uap, int *retval)
{

  if (uap->req == PT_DENY_ATTACH) {
      printf("[ptrace] Blocking PT_DENY_ATTACH for pid %d.\n", uap->pid);
      return (0);
  } else {
      return real_ptrace(p, uap, retval);
  }
}

If the parameter is PT_DENY_ATTACH it does nothing and returns success, return (0), otherwise it calls the original ptrace function passing the supplied parameters. This causes PT_DENY_ATTACH to be ignored but other calls to ptrace continue to function as expected.

In order to make this more difficult in Lion, Apple don’t export the location of the sysent table. In order to find the sysent table the module has the location of the nsysent variable hard-coded.

pt_deny_attach.cView on GitHub
1
#define _NSYSENT_OSX_10_7_1_  0xffffff8000846eb8

nsysent contains the number of entries in the sysent table and is stored close to the sysent table. As the comment above suggests, it is possible to find the memory address of the nsysent variable using the command nm -g /mach_kernel | grep _nsysent. On 10.8.2 this gives:

1
2
3
macbook-air:~ matt$ nm -g /mach_kernel | grep _nsysent
ffffff8000839818 D _nsysent
ffffff80008db150 S _nsysent_size_check

If you try to find the sysent table directly you will get results but not the address of sysent itself:

1
2
3
4
5
6
7
macbook-air:~ matt$ nm -g /mach_kernel | grep _sysent
ffffff80002ce010 T _hi64_sysenter
ffffff80002ce8f0 T _hndl_sysenter
ffffff80002ce010 T _idt64_sysenter
ffffff80008e0688 S _systrace_sysent
ffffff80002b5c30 T _x86_sysenter_arg_store_isvalid
ffffff80002b5c20 T _x86_toggle_sysenter_arg_store

So, having found the 10.8.2 location of nsysent the next obvious step is to update the module with the new location and reload it… kernel panic!

The kernel module makes an assumption about the location of the sysent table. It assumes that the sysent table is immediately before the nsysent variable. It uses the value of nsysent and the sizeof(struct sysent) to calculate the size of the sysent table, table_size, and then subtracts this from the memory address of nsysent to give a pointer to sysent in table.

pt_deny_attach.cView on GitHub
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*
 * nsysent is placed directly before the hidden sysent, so skip ahead
 * and sanity check that we've found the sysent array.
 *
 * Clearly, this is extremely fragile and not for general consumption.
 */
static struct sysent *find_sysent () {
  unsigned int table_size;
  struct sysent *table;

  table_size = sizeof(struct sysent) * *(_nsysent);
  table = (struct sysent *) ( ((char *) _nsysent) - table_size );

  printf("[ptrace] Found nsysent at %p (count %d), calculated sysent location %p.\n", _nsysent, *_nsysent, table);

Obviously some things have changed within the kernel. Simply updating the location of nsysent doesn’t fix the problem.

Getting it to work on Mountain Lion

After some experimentation (and many kernel panics) it became obvious that I hadn’t actually found the correct location of nsysent in the kernel address space. Simply trying to read the value of nsysent (which should contain a count of sys calls) was causing a kernel panic.

After some Internet research it became clear that the major difference between the Lion and Mountain Lion kernel was the introduction of Kernel Address Space Layout Randomisation (KASLR). When the kernel is loaded into memory KASLR causes the addresses of all the symbols (variables) to be randomised. The nm command shows the location of the nsysent variable before the randomisation has taken place. Simply changing the value of _NSYSENT_OSX_10_7_1_ is not going to work.

KASLR works by generating a random number and then sliding the symbol addresses in memory by this random value. Basically the kernel slide value is added to all the addresses in the kernel when it is loaded into memory. The value of the kernel slide can be accessed by a privileged process using a new syscall on OS X (this syscall was briefly available in iOS 6 betas but was removed before release). Check out kextstat_kaslr for more details.

The value of the slide is stored in the kernel in a variable called vm_kernel_slide, we can see this in the kernel source code. The kernel source is open source and available at http://opensource.apple.com/source/xnu/xnu-2050.18.24/.

Unfortunately, Apple have not exported this variable for use by kernel modules. We are also in a chicken and egg situation because vm_kernel_slide has also been moved by the kernel slide. Everything I tried failed to give me the value of the kernel slide. It is ironic that a user process running as root can access this value using the kas_info syscall but a kernel module running in the kernel doesn’t seem to be able to. (It is entirely possible that I missed something obvious at this point.)

After pondering the problem it occurred to me that the value of vm_kernel_slide can be inferred by a kernel module. We can use nm to get the address of an exported function in the kernel. Our kernel module can get the function pointer of a function in the running kernel. If we subtract one from the other we get the value of the kernel slide.

I updated the kernel module with code to do this. The module contains the hard-coded location of printf which was found using nm. The module then gets a pointer to the printf function and uses this to calculate the slide.

pt_deny_attach.c
1
#define _PRINTF_OSX_10_8_2_   0xffffff8000229090  // Used to calculate the KASLR slide
pt_deny_attach.c
1
2
3
4
5
6
7
8
9
/*
 * vm_kernel_slide doesn't appear to be available to kexts
 * but we can calculate it by getting the address of a known
 * function, e.g. printf, and then comparing that to the
 * address returned by nm -g /mach_kernel
 */
static vm_offset_t calculate_vm_kernel_slide(void) {
    return (vm_offset_t)&printf - _PRINTF_OSX_10_8_2_;
}

Using the calculated slide value it is now possible to calculate the correct location of nsysent using the following code:

pt_deny_attach.c
1
2
3
4
5
6
7
kern_return_t pt_deny_attach_start (kmod_info_t *ki, void *d) {

    slide = calculate_vm_kernel_slide();
    printf("[ptrace] KASLR kernel slide is 0x%lx\n", slide);

    _nsysent = (int *)(_NSYSENT_OSX_10_8_2_ + slide);

Reading the value of _nsysent at this point gave the correct value for the number of syscalls that Mountain Lion provides. Finally we are getting somewhere.

The next step is to find the sysent table based on the location of nsysent. This took a little bit of hunting as it has moved quite a way in Mountain Lion. I simply searched the memory around nsysent checking it using the sanity checks from the Lion version of the module. I eventually found it after a few more kernel panics with an offset of 0x1c028 from nsysent.

Having found the location of sysent it was easy to confirm that the location of the ptrace function in the syscall table matched the location returned by nm (plus the kernel slide).

Updating the kernel module and loading it into the kernel finally… caused a kernel panic.

This is were I am now stuck and I can’t see an easy way around the problem. As far as I can tell the sysent table is now located in a read-only memory page. Attempting to replace the value of the function pointer to ptrace with a pointer to our_ptrace causes a kernel panic.

The kernel source code declares the sysent table as follows:

init_sysent.c
1
2
3
4
__private_extern__ const struct sysent sysent[] = {
        {0, 0, 0, (sy_call_t *)nosys, NULL, NULL, _SYSCALL_RET_INT_T, 0},                           /* 0 = nosys indirect syscall */
        {AC(exit_args), 0, 0, (sy_call_t *)exit, munge_w, munge_d, _SYSCALL_RET_NONE, 4},           /* 1 = exit */
        {0, 0, 0, (sy_call_t *)fork, NULL, NULL, _SYSCALL_RET_INT_T, 0},                            /* 2 = fork */

I assume the fact it is declared const means that the table has been placed into a protected memory page. Feel free to correct me if I am wrong.

One last try

Given that it doesn’t seem to be possible to modify the sysent table I wondered if it would be possible to hot-patch the ptrace function itself. Unfortunately attempting to write to the memory associated with the function causes…

…yes, you guessed it, a kernel panic!

The source code for a kernel module that works up to the point of actually modifying the sysent table is available on GitHub at https://github.com/mttrb/pt_deny_attach.

Bash Prompt With Git Branch

For a while I have been using a Bash Terminal Prompt which is normally green but turns red if the last process failed. I have just discovered a prompt which includes the current git branch and I have updated my prompt to include both of these functions.

My terminal prompt now looks like the following screen capture (I’m currently using the Solarized Light theme):

You can add the following snippet to your .bash_profile, .profile or .bashrc file to get the same prompt:

.bash_profileView Gist
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function parse_git_branch {
        git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ \[\1\]/'
}

function prompt {
        local DEFAULT="\[\033[0m\]"
        local RED="\[\033[0;31m\]"
        local GREEN="\[\033[0;32m\]"
        local BLUE="\[\033[0;34m\]"

        PS1="\`if [ \$? == '0' ]; then echo '$GREEN'; else echo '$RED'; fi\`\h:\W \u$BLUE\$(parse_git_branch)$DEFAULT\$ "
}

prompt

The most up-to-date version of this prompt is available as a Gist at https://gist.github.com/4122721

Objective-C Literals: Negative Array Subscripts

Several new features were added to the Apple LLVM Compiler 4.0 to allow the use of Objective-C Literals and object subscripts.

This means that it is now possible to use the following shorthands to create and access elements of an NSArray.

Objective-C Literal and Subscripting Syntax
1
2
3
NSArray *array = @[ @"World", @"Hello" ];

NSLog(@"%@ %@", array[1], array[0]);

During compilation the subscripted array access is converted to a call to the objectAtIndexedSubscript: method. So the above NSLog statement would be converted to the following before compilation.

Objective-C Subscripting Syntax
1
2
3
NSLog(@"%@ %@",
      [array objectAtIndexedSubscript:1],
      [array objectAtIndexedSubscript:0]);

This addition to the Objective-C language is useful and certainly will make some code less verbose and easier to read.

However, many languages (particularly scripting languages) allow the use of negative indexes. Negative indexes are counted from the end of the array rather than the beginning. This led me to wonder if you can use negative indexes with the new Objective-C syntax.

Unfortunately, a quick look at the method definition for objectAtIndexedSubscript: shows that the index parameter is an NSUInteger, an unsigned integer. Therefore, as implemented, the new subscript indexes must be positive.

objectAtIndexedSubscript: method definition
1
- (id)objectAtIndexedSubscript:(NSUInteger)idx;

So, is it possible to add functionality to NSArray to allow the use of negative indexes? The answer is Yes. The Objective-C runtime allows us to add the functionality to make it possible.

In order to do this we need to create our own version of the objectAtIndexedSubscript: method. The new method, BLC_objectAtIndexedSubscript: will look like:

The new objectAtIndexedSubscript: method
1
2
3
4
5
6
7
- (id)BLC_objectAtIndexedSubscript:(NSInteger)idx {
    if (idx < 0) {
        idx = [self count] + idx;
    }

    return [self BLC_objectAtIndexedSubscript:idx];
}

You will notice that the parameter is now an NSInteger rather than the unsigned NSUInteger used by the original method.

You may also have noticed what appears to be a recursive call in line 6. As things stand at the moment this is a recursive call but the next step is to perform a method swizzle.

Method swizzling involves swapping two implementations of a method, in our case objectAtIndexedSubscript: and BLC_objectAtIndexedSubscript:. We perform the method swizzle using the method_exchangeImplementations function of the Objective-C runtime, declared in the objc/runtime.h header file.

The method swizzle is performed as follows:

Method Swizzling objectAtIndexedSubscript:
1
2
3
4
method_exchangeImplementations(
    class_getInstanceMethod(self, @selector(objectAtIndexedSubscript:)),
    class_getInstanceMethod(self, @selector(BLC_objectAtIndexedSubscript:))
);

Now that the method implementations have been swizzled that recursive call in BLC_objectAtIndexedSubscript: has become a call to the original method. The original method implementation is now called using the BLC_objectAtIndexedSubscript: selector.

Once we have swapped the methods we are able to use negative indexes as follows:

Using negative indexes
1
2
3
NSArray *array = @[ @"World", @"Hello" ];

NSLog(@"%@ %@", array[-1], array[-2]);

The full implementation of the new method and the method swizzling is contained in a category on NSArray. The full listing is shown below:

NSArray+NegativeIndexes.mView Gist
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//
//  NSArray+NegativeIndexes.m
//  Allow Negative Array Literals
//
//  Created by Matthew Robinson on 25th October 2012.
//  Copyright (c) 2012 Matthew Robinson. All rights reserved.
//
//  WARNING: This is a proof of concept and should not be used
//           in production code.  This could break at anytime.

#import <Foundation/Foundation.h>

#if __has_feature(objc_subscripting)

#import <objc/runtime.h>

@implementation NSArray (NegativeIndexes)

+ (void)load {
    method_exchangeImplementations(
        class_getInstanceMethod(self, @selector(objectAtIndexedSubscript:)),
        class_getInstanceMethod(self, @selector(BLC_objectAtIndexedSubscript:))
    );
}

- (id)BLC_objectAtIndexedSubscript:(NSInteger)idx {
    if (idx < 0) {
        idx = [self count] + idx;
    }

    return [self BLC_objectAtIndexedSubscript:idx];
}

@end

@implementation NSMutableArray (NegativeIndexes)

+ (void)load {
    method_exchangeImplementations(
        class_getInstanceMethod(self, @selector(setObject:atIndexedSubscript:)),
        class_getInstanceMethod(self, @selector(BLC_setObject:atIndexedSubscript:))
    );
}

- (void)BLC_setObject:(id)anObject atIndexedSubscript:(NSInteger)idx {
    if (idx < 0) {
        idx = [self count] + idx;
    }

    [self BLC_setObject:anObject atIndexedSubscript:idx];
}

@end

#endif

As you can see the above implementation also contains a category on NSMutableArray which swizzles a replacement method for setObject:atIndexedSubscript: to allow negative indexes in array assignment.

There is no associated header file for this category as the category does not add any new public methods to the NSArray (or NSMutableArray class). Simply compiling the above .m file into a binary will add the functionality to the array classes.

This category is available as a Gist at: https://gist.github.com/4076357

WARNING: This functionality is a hack and relies on the clang compiler converting array subscripts into the associated call to objectAtIndexedSubscript: without checking the positiveness of the index.

Dress Classy, Dance Cheesy!

I’m sure you’ve always wanted to be able to create a UITableView with the following code:

UITableView initWithFrame:… style:…
1
2
UITableView *tableView = [[UITableView alloc] initWithFrame:frame
                                                      style:UITableViewStyleGangnam];

UITableViewStyleGangnam

I was inspired to write the following after my trip to /dev/world last month. Gangnam Style was one of the memes during the conference. Feel free to do something even more cheesy with it.

The following source code and an example iPhone project are available on GitHub at: https://github.com/mttrb/UITableView-GangnamStyle

UITableView+GangnamStyle.hView GitHub
1
2
3
4
5
6
7
8
9
10
11
//
//  UITableView+GangnamStyle.h
//  Gangnam Style
//
//  Created by Matthew Robinson on 20th October 2012.
//  Copyright (c) 2012 Matthew Robinson. All rights reserved.
//

#import <UIKit/UIKit.h>

#define UITableViewStyleGangnam ((UITableViewStyle)1000)
UITableView+GangnamStyle.mView GitHub
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//
//  UITableView+GangnamStyle.m
//  Gangnam Style
//
//  Created by Matthew Robinson on 20/10/12.
//  Copyright (c) 2012 Matthew Robinson. All rights reserved.
//

#import "UITableView+GangnamStyle.h"
#import <objc/runtime.h>


@interface PSYGangnam : NSObject <UITableViewDataSource>

+ (id)sharedGangnam;

@end


@implementation UITableView (GangnamStyle)

+ (void)load {
    method_exchangeImplementations(
        class_getInstanceMethod(self, @selector(initWithFrame:style:)),
        class_getInstanceMethod(self, @selector(PSY_initWithFrame:style:))
    );
}

- (id)PSY_initWithFrame:(CGRect)frame style:(UITableViewStyle)style {
    // Call the original initWithFrame:style: which is now
    // called PSY_initWithFrame:style: This is NOT recursive!
    // If the style is Gangnam actually use plain

    self = [self PSY_initWithFrame:frame
                             style:style == UITableViewStyleGangnam ? UITableViewStylePlain : style];


    // Set the data source if Gangnam style
    if (self && style == UITableViewStyleGangnam) {
        [self setDataSource:[PSYGangnam sharedGangnam]];
    }

    return self;
}

@end



@interface PSYGangnam () {
    NSArray *_lyrics;
}

@end

@implementation PSYGangnam

+ (id)sharedGangnam {
    static PSYGangnam *sharedInstance = nil;
    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{
        sharedInstance = [[PSYGangnam alloc] init];
    });

    return sharedInstance;
}


- (id)init {
    self = [super init];

    if (self) {
        _lyrics = [[NSArray alloc] initWithObjects:
                   @"Oppan Gangnam Style",
                   @"Gangnam Style",
                   @"",
                   @"Na je nun ta sa ro un in gan jo gin yo ja",

                   // ...

                   @"Oppan Gangnam Style",
                   @"",
                   @"© Sony/ATV Music Publishing LLC",
                   nil
                   ];
    }

    return self;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [_lyrics count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *PSYGangnamCellIdentifier = @"PSYGangnamCellIdentifier";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:PSYGangnamCellIdentifier];

    if (nil == cell) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                       reuseIdentifier:PSYGangnamCellIdentifier] autorelease];

        [[cell textLabel] setFont:[UIFont fontWithName:@"Helvetica-Bold" size:11.0]];
    }

    [[cell textLabel] setText:[_lyrics objectAtIndex:[indexPath row]]];

    return cell;
}

@end

/dev/world 2012 Presentation

I recently gave a presentation at the AUC’s /dev/world conference. The video recording of my presentation, Adventures in the Objective-C Runtime, is now available on the AUC website at http://auc.edu.au/2012/08/objective-c-runtime/.

I have included the presentation abstract below:

Adventures in the Objective-C Runtime

The Objective-C language is dynamic, many decisions are deferred from compile and link time to runtime. The dynamic nature of the language is implemented by the Objective-C runtime, which acts as a kind of operating system. Typically, you don’t need to interact with the runtime directly…

…but what can the runtime do for us if we choose to be atypical.

Categories are generally the first dynamic feature of the language that programmers new to Objective-C discover and this is where we will start. To understand how categories work we will look at message sending and method swizzling. From here we’ll be able to look at other features of the runtime.

In order to understand what the runtime is doing we will need the ability to inspect what it is doing internally, for this we will take a quick detour into DTrace profiling. We will also take a quick look at benchmarking to see if (and by how much) the dynamic nature of the language slows it down.

Messing around in the runtime has the potential to cause havoc, however, the runtime can be used to solve problems in novel and powerful ways. We will close by looking at some examples of using the runtime. Key Value Observing and Core Data are obvious examples from the Cocoa frameworks.