[JavaScript] setTimeout() and Closures

If you’ve done any amount of non-trivial JavaScript coding then you have probably come across the ‘setTimeout()‘ function before. In case you haven’t, this function takes a JavaScript expression and evaluates/executes it after a set delay (specified in milliseconds). For instance:

setTimeout("alert('test')", 5000);

…will display an alert box containing the text “test” after a 5-second delay. Simple enough. But what you may not know is that ‘setTimeout()‘ also supports an alternate and much more useful usage mode where instead of literal javascript text you can pass a function reference/closure and a variable number of parameters, like so:

setTimeout(alert, 5000, "test");

This will produce the same results as the first call, but the syntax is much more readable and much less prone to errors when coding. Why this variant is barely even mentioned in the documentation I cannot say, but anyone who has worked with Flash/ActionScript is probably familiar with this syntax.

There is one major limitation to this approach, however; it does not work correctly in Internet Explorer. That’s a pretty serious drawback, considering Internet Explorer’s market share. But we can fix it by overriding the default ‘setTimeout()‘ implementation as follows:

window._oldSetTimeout = window.setTimeout;
window.setTimeout = function(closureOrText, delay) {
    if (arguments.length <= 2 || typeof closureOrText != "function") {
        _oldSetTimeout(closureOrText, delay);
    }
    else {
        var funcArgs = new Array(); 
        for (var index = 2; index < arguments.length; index++) {
            funcArgs.push(arguments[index]);
        }
        _oldSetTimeout(_timeoutCallback(closureOrText, funcArgs), delay);
    }
};
window._timeoutCallback = function(closure, argArray) {
    return function() {
        closure.apply(this, argArray);
    };
};

Now the behavior will be consistent between Internet Explorer and other browsers. Note that although the default implementation only needs to be overridden in Internet Explorer, the above code will work correctly in other browsers as well. Also note that there is one more caveat to be aware of here as well. In Internet Explorer, functions provided by the system (such as ‘alert()‘, ‘escape()‘, ‘parseInt()‘, etc.) are of a different type than user-defined functions. Now this wouldn’t be a huge problem, except that whatever type Internet Explorer uses for its system functions does not support the ‘apply()‘ method (thanks, Microsoft).

So even with the above code, if you use ‘setTimeout()‘ with a system function in Internet Explorer, you will still get incorrect behavior. We can fix this by revising the code as follows:

window._oldSetTimeout = window.setTimeout;
window.setTimeout = function(closureOrText, delay) {
    var funcArgs = new Array(); 
    for (var index = 2; index < arguments.length; index++) {
        funcArgs.push(arguments[index]);
    }
    if (arguments.length <= 2 || typeof closureOrText != "function") {
        if (arguments.length <= 2 || typeof closureOrText == "string") {
            _oldSetTimeout(closureOrText, delay);
        }
        else {
            //hack for IE system functions
            _oldSetTimeout(_timeoutCallbackForSystemFunction (closureOrText, funcArgs), delay);
        }
    }
    else {
        _oldSetTimeout(_timeoutCallback(closureOrText, funcArgs), delay);
    }
};
window._timeoutCallback = function(closure, argArray) {
    return function() {
        closure.apply(this, argArray);
    };
};
window._timeoutCallbackForSystemFunction = function(closure, argArray) {
    return function() {
        if (argArray.length == 1) {
            closure(argArray[0]);
        }
        else if (argArray.length == 2) {
            closure(argArray[0], argArray[1]);
        }
        else {
            alert("WARN:  Too many arguments passed to system function; timeout callback not executed!");
        }
    };
};

This code will produce the correct behavior in Internet Explorer, and is still compatible with all other major browsers. In Internet Explorer it is limited to supporting system functions with a maximum of 2 parameters, but I’m not aware of any that require more than that anyways. An alternate workaround is to define your own function that wraps the system function, and then use the wrapper function in the ‘setTimeout()‘ call, like so:

var myAlert = function(text) {
    alert(text);
};
setTimeout(myAlert, "5000", "test");

It’s a bit less convenient to do it this way if you will be working with several different system functions, but this approach will also yield correct behavior in Internet Explorer (and is compatible with other browsers as well).

In any case, either approach can be used to get the closure-based ‘setTimeout()‘ syntax working consistently across all major browsers. And once you have that, there is very little reason to ever prefer the text-based version.

Posted in coding, javascript | Tagged , | Leave a comment

[JavaScript] parseInt() Quirks

Here’s a subtle little feature of JavaScript’s ‘parseInt()‘ function that I recently stumbled across. The radix used in the parse will default to 8 (octal) if the string you are parsing includes a leading zero. For instance:

parseInt("07");

…will return 7, as expected, but:

parseInt("08");

…will return 0, because 8 is not a valid digit in a base-8 number system. Technically the ‘parseInt()‘ function is behaving to spec in each instance, but this can lead to some very confusing errors if you’re not expecting this behavior. A simple fix exists, however:

parseInt("08", 10);

…will return 8, as expected. Explicitly specifying a radix of 10 overrides the function’s quirky default behavior (which as near as I can tell is a relic carried over from C and Java, which both use a leading zero to specify octal literals…why they didn’t opt for something more clear, like suffixing the value with ‘o’, I cannot say). Here is a quick example comparing the two.

An alternate fix to this issue is to simply override the parseInt() function, like so:

			window._oldParseInt = window.parseInt;
			window.parseInt = function(str, rad) {
				if (! rad) {
					return _oldParseInt(str, 10);
				}
				return _oldParseInt(str, rad);
			};

This will change the behavior of ‘parseInt()‘ to match that of Java’s ‘Integer.parseInt()‘ function (the radix used is always 10 unless the developer explicitly overrides it), which provides for a much more consistent and predictable experience.

Note, however, that this quirky behavior is a documented (albeit deprecated) feature of the ‘parseInt()‘ function; though it is certainly not something that one intuitively expects. Interestingly enough, its deprecation means that browser implementors are free to drop support for this confusing default at any time. However, complete removal of this feature seems unlikely to happen, as doing so would break existing websites that rely upon it.

Perhaps a reasonable middle ground would be to preserve the default behavior when the string being parsed includes only valid base-8 numbers, and default to base-10 if any other numbers are present in the string. That would help in simple cases like the ones shown above, but still may generate confusing output for inputs like “0107” and the like.

Really the problem here is one of a poorly designed spec. Instead of picking the most common usage mode and making that the default, like Java does in its ‘Integer.parseInt()‘ function, someone decided to try to infer the desired usage mode based upon the input parameter. And that may be fine when there are no ambiguous mappings from input parameters to usage modes, but that is not the case here. Whenever such ambiguous mappings exist, it is always better to just make the most common usage mode the default, and let the developer override it if they want something different.

Otherwise you produce code that can yield confusing and unexpected behavior, and you dig everyone else into a hole where they must continue both supporting and working around your poor design decision.

Posted in coding, javascript | Tagged , | 3 Comments

[Status] Upgrade Delays

I just found out that the Atom CPU and mainboard that I ordered is on backorder won’t be getting here until the end of the month (why did the site I purchased it from list it as “In Stock” when it wasn’t really in stock?). So now we play the waiting game, and hope that this old server is able to keep holding up.

Posted in status | Leave a comment

[Objective-C + Cocoa] UIScrollView and contentSize

So here’s a simple one. For whatever reason, a UIScrollView instance only behaves correctly if you programmatically set its contentSize when you use it. This is fairly silly because in most cases the contentSize is simply the total size of the UIScrollView’s subview(s). Why the UIScrollView class doesn’t provide at least the option of automatically determining its own contentSize based upon its current subviews is beyond me, but here is some simple code to approximate this behavior:

@interface UIScrollView(auto_size)
- (void) adjustHeightForCurrentSubviews: (int) verticalPadding;
- (void) adjustWidthForCurrentSubviews: (int) horizontalPadding;
- (void) adjustWidth: (bool) changeWidth andHeight: (bool) changeHeight withHorizontalPadding: (int) horizontalPadding andVerticalPadding: (int) verticalPadding;
@end

@implementation UIScrollView(auto_size) 
- (void) adjustWidth: (bool) changeWidth andHeight: (bool) changeHeight withHorizontalPadding: (int) horizontalPadding andVerticalPadding: (int) verticalPadding {
    float contentWidth = horizontalPadding;
    float contentHeight = verticalPadding;
    for (UIView* subview in self.subviews) {
        [subview sizeToFit];
        contentWidth += subview.frame.size.width;
        contentHeight += subview.frame.size.height;
    }
    
    contentWidth = changeWidth ? contentWidth : self.superview.frame.size.width;
    contentHeight = changeHeight ? contentHeight : self.superview.frame.size.height;
    
    NSLog(@"Adjusting ScrollView size to %fx%f, verticalPadding=%d, horizontalPadding=%d", contentWidth, contentHeight, verticalPadding, horizontalPadding);
    self.contentSize = CGSizeMake(contentWidth, contentHeight);
}

- (void) adjustHeightForCurrentSubviews: (int) verticalPadding {
    [self adjustWidth:NO andHeight:YES withHorizontalPadding:0 andVerticalPadding:verticalPadding];
}

- (void) adjustWidthForCurrentSubviews: (int) horizontalPadding {
    [self adjustWidth:YES andHeight:NO withHorizontalPadding:horizontalPadding andVerticalPadding:0];
}
@end

This code allows a UIScrollView to internally determine its contentSize based upon its current subviews; all you have to do is call one of the three interface methods at an appropriate time (like from within your parent view-controller’s ‘viewDidLoad:‘ implementation). Note that while auto-sizing based upon both width and height is supported, you will only get a correct result for width if all of the UIScrollView’s subviews span the entire height of the view, and you will only get a correct result for height if all of your subviews span the entire width of the view. For instance, if you add a thumbnail image to the UIScrollView and then drag a UILabel next to it then both of them will count towards the computed height even though they are logically on the same row.

You can work around this limitation either by using the ‘…padding’ parameters to adjust the final contentSize, or by adding a UIView that spans the width of the UIScrollView and placing both your thumbnail image and UILabel as subviews of that UIView instead of the UIScrollView. The latter option of using a nested UIView to contain the content of the row is a better/more maintainable way to build an interface anyways (and also building a UI in Android basically requires you to follow this pattern, so best to get used to it). But I did try various approaches to solve this problem automatically in the code, such as keeping track min and max x/y coordinates of every subview in the UIScrollView, but this gave inconsistent results between the initial time the view was displayed and subsequent times.

Posted in coding, objective-c | Tagged , , | 8 Comments

[Cocoa + iPhone] UITableViewCell: It’s Broken!

I present for your consideration the following screenshot:

UITableViewCell is broken!

It shows a basic table-view, in which each cell has been assigned the same image (using its built-in ‘imageView‘ property). The source image is 20 pixels square, and the imageView’s ‘contentMode‘ property has not been changed (not that changing it makes any difference). The image for each row is also being scaled to 50% and rendered at the orientation stated in the cell text. The code for the table controller is as follows:

#import "UITableViewTestViewController.h"

static NSString* rowNames[8] = {@"UIImageOrientationUp", @"UIImageOrientationDown", @"UIImageOrientationLeft", @"UIImageOrientationRight", 
                                @"UIImageOrientationUpMirrored", @"UIImageOrientationDownMirrored", @"UIImageOrientationLeftMirrored", 
                                @"UIImageOrientationRightMirrored"};

#define IMAGE_NAME @"testImage.png"

@implementation UITableViewTestViewController

- (void)dealloc {
    [super dealloc];
}

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
}

#pragma mark - View lifecycle
- (int) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 8;  //number of elements in the enumeration
}

- (int) numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString* cellIdentifier = @"TestCell";
    
    //return a basic cell with the icon in it and some text
    UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"StationCell"];
    if (cell == nil) {
        //init cell
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:cellIdentifier] autorelease];
    }
    
    cell.accessoryType = UITableViewCellAccessoryNone;
    cell.textLabel.text = rowNames[indexPath.row];          //enum starts from 0, so indexPath.row matches the orientation that we are going to apply
    cell.textLabel.font = [cell.textLabel.font fontWithSize:12.0];
    cell.textLabel.textColor = [UIColor darkGrayColor];
    cell.imageView.image =  [UIImage imageWithCGImage:[UIImage imageNamed:IMAGE_NAME].CGImage scale:0.5 orientation:indexPath.row];  //the scale operation will be ignored for UIImageOrientationUp; because something is broken
    
    return cell;
}

- (void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    //it makes no difference if we set the image here
    //cell.imageView.image =  [UIImage imageWithCGImage:[UIImage imageNamed:IMAGE_NAME].CGImage scale:0.5 orientation:indexPath.row];
}
@end

It’s not doing anything all that special, but as you can see in the screenshot the image in the first cell is rendered differently than all the others. More specifically, it is being stretched to the full size of its container so that it just looks kind of sad, and no amount of programmatic scale operations will fix it.

This can be one of the most maddening aspects about working with table-cells and images. If you want an image that is slightly smaller than its container in the table-cell, or that is centered away from the top/side, then the only consistent way to do so is to create a custom table-cell. And while it is not difficult to create a custom table-cell that implements the desired behavior, it needlessly clutters the source-tree with code that replicates functionality that Apple is supposed to be providing out of the box.

The problem, as exposed by this example code, is that when an image is scaled using UIImageOrientationUp (which is what most developers would use, given that they generally store their images in the orientation they want them displayed at) the UITableViewCell completely ignores the scaling operation. I can only speculate as to the reason for this odd behavior, because at the very least I would expect the output to be the same no matter what UIImageOrientation is used (i.e. I would think that scaling would either consistently not work or consistently work, but this is manifestly not the case).

In any case, this behavior is very clearly a bug, and a particularly inconvenient one at that. But it does expose a potential workaround that generates less source-clutter than creating a custom table-cell implementation every time you want to have cell images that actually work. Just store your images upside-down (or preprocess them so that they are upside-down prior to adding to the table) and then invert them back to the proper orientation when you scale them to the size you want for your table.

It’s dodgy as all hell to do it that way, but still arguably better than reimplementing functionality that Apple is supposed to be providing out of the box.

Project source code is available here: http://codethink.no-ip.org/UITableViewTest.zip

Posted in coding, objective-c | Tagged , , , | Leave a comment

[Cocoa + iPhone] Unraveling Apple’s Pagecurl

First off, I encourage anyone that’s unfamiliar of this topic to read through this short but very sweet blog post on the subject (and to take a quick look at his sample code). We’ll be picking up where Steven left off.

In any case, to summarize the current situation; there exists a private and undocumented API in the iPhone SDK which Apple uses to great effect in their iBook application. The way to interface with this private API has been discovered and even fairly well documented. Using the private API is pretty straightforward but for one small problem: if you use the private API in your application then Apple will reject your app. For whatever non-specified reason (probably to keep potential iBook competitors in check), Apple does not want to open up their private API to developers or to play nice with developers who bend the rules and use the private API.

So our goal is clear. If Apple isn’t going to play nice and open the API up to developers, then perhaps we can do some digging to figure out how Apple’s implementation actually works and create our own implementation that does the same thing. It’s a pretty standard exercise in reverse-engineering, really. The core of the private page-curl API is used like so:

		filter = [[CAFilter filterWithType:kCAFilterPageCurl] retain];
		[filter setDefaults];
		[filter setValue:[NSNumber numberWithFloat:((NSUInteger)fingerDelta)/100.0] forKey:@"inputTime"];
		
		CGFloat _angleRad = angleBetweenCGPoints(currentPos, lastPos);
		CGFloat _angle = _angleRad*180/M_PI ; // I'm far more comfortable with using degrees ;-)
					
		if (_angle < 180 && _angle > 120) {// here I've limited the results to the right-hand side of the paper. I'm sure there's a better way to do this
			if (fingerVector.y > 0)
				[filter setValue:[NSNumber numberWithFloat:_angleRad] forKey:@"inputAngle"];
			else
				[filter setValue:[NSNumber numberWithFloat:-_angleRad] forKey:@"inputAngle"];

			_internalView.layer.filters = [NSArray arrayWithObject:filter];
		}

This is an excerpt straight out of Steven Troughton-Smith’s example. The example includes additional code related to tracking touch positions and interpolating the angle and distance between them, but this is really the core of the private API right here. All of the heavy-lifting is handled by the CAFilter class (private), which has a type of ‘kCAFilterPageCurl‘ (private constant, just the string @”pageCurl”, other filter types also exist), and which takes just a small number of input parameters (‘inputTime‘ and ‘inputAngle‘) and then works its magic behind the scenes.

So given that CAFilter seems to be doing pretty much all the work, it would follow that by constructing our own class that exposes the same interface as CAFilter we can supplant the private-API class with one of our own making (ah, the joys of reflection and weak-typing), thus interfacing with the underlying platform without breaking any of the rules. But what exactly is a CAFilter? Is it as onerous as a UIView with its hundreds of methods and properties? Does it extend another obscure private-API class that will also need to be reverse-engineered? Well thanks to the ‘printObject:toDepth:‘ routine discussed in a previous post we can see that a CAFilter is exactly:

@interface CAFilter : NSObject {
	unsigned int _type;
	NSString* _name;
	unsigned int _flags;
	void* _attr;
	void* _cache;
}

//Constructors
- (id) initWithType:  (NSString*) arg0;
- (id) initWithName:  (NSString*) arg0;

//NSCoding
- (NSObject*) initWithCoder:  (NSCoder*) arg0;
- (void) encodeWithCoder:  (NSCoder*) arg0;

//NSKeyValueCoding
- (void) setValue: (id) arg0 forKey: (NSString*) arg1;
- (id) valueForKey:  (NSString*) arg0;

//NSCopying and NSMutableCopying
- (NSObject*) mutableCopyWithZone:  (NSZone*) arg0;
- (NSObject*) copyWithZone:  (NSZone*) arg0;

//interface methods
- (void) setDefaults;
- (bool) isEnabled;
- (struct UnknownAtomic*) CA_copyRenderValue;

//garbage collection (doesn't need to be declared here)
- (void) dealloc;

//property accessors (don't need to be declared here)
- (bool) enabled;
- (void) setEnabled:  (bool) arg0;
- (bool) cachesInputImage;
- (void) setCachesInputImage:  (bool) arg0;
- (NSString*) name;
- (void) setName:  (NSString*) arg0;
- (NSObject*) type;

//properties
@property(nonatomic, readonly) NSString* type;
@property(nonatomic, retain) NSString* name;
@property(nonatomic) bool enabled;
@property(nonatomic) bool cachesInputImage;

@end

Nineteen methods and a handful of fields. Not bad, not bad at all, particularly when many of the methods are simply implementing various publicly-documented protocols such as NSCoding, NSCopying, and NSKeyValueCoding. As an added bonus, the superclass of CAFilter is NSObject, so the problem has now been reduced to the implementation of a single unknown class (which may still be a Herculean task, but at least now there are clearly-defined boundaries).

But the above code includes some methods that do not need to be part of the publicly declared interface. Let’s clean it up, rename it so that it doesn’t conflict with the existing private-API class, and add the proper definition of the ‘…Atomic‘ struct:

#import <Foundation/Foundation.h>

struct RenderValueResult { 
	int (**x1)(); 
	struct MyAtomic { 
		struct { 
			NSInteger x; 
		} _v; 
	} x2; 
} *_filterResult;

@interface MyCAFilter : NSObject<NSCoding, NSCopying, NSMutableCopying> {
	unsigned int _type;
	NSString* _name;
	unsigned int _flags;
	void* _attr;
	void* _cache;
}

//Constructors
- (id) initWithType:  (NSString*) arg0;
- (id) initWithName:  (NSString*) arg0;

//NSKeyValueCoding
- (void) setValue: (id) arg0 forKey: (NSString*) arg1;
- (id) valueForKey:  (NSString*) arg0;

//interface methods
- (void) setDefaults;
- (bool) isEnabled;
- (struct RenderValueResult*) CA_copyRenderValue;

//properties
@property(nonatomic, readonly) NSString* type;
@property(nonatomic, retain) NSString* name;
@property(nonatomic) bool enabled;
@property(nonatomic) bool cachesInputImage;

@end

Looking better already. That ‘RenderValueResult‘ struct will prove to be a nasty one, but more on that later.

Now that we know the interface, and before we go flying off randomly trying to replicate functionality that we still don’t fully understand, let’s take a simpler step. Let’s create a simple class that exposes the CAFilter interface, wraps an actual CAFilter instance, and logs each method call, parameters, and result, like so:

//MyCAFilter.h (modified to include 'delegate' field)
#import <Foundation/Foundation.h>

struct RenderValueResult {
    int (**x1)();
    struct MyAtomic {
        struct {
            NSInteger x;
        } _v;
    } x2;
} *_renderValueResult;

@class CAFilter;  //private-API

@interface MyCAFilter : NSObject<NSCoding, NSCopying, NSMutableCopying> {
    unsigned int _type;
    NSString* _name;
    unsigned int _flags;
    void* _attr;
    void* _cache;
    
    CAFilter* delegate;  //private-API
}

//Constructors
- (id) initWithType:  (NSString*) arg0;
- (id) initWithName:  (NSString*) arg0;

//NSKeyValueCoding
- (void) setValue: (id) arg0 forKey: (NSString*) arg1;
- (id) valueForKey:  (NSString*) arg0;

//interface methods
- (void) setDefaults;
- (bool) isEnabled;
- (struct RenderValueResult*) CA_copyRenderValue;

//properties
@property(nonatomic, readonly) NSString* type;
@property(nonatomic, retain) NSString* name;
@property(nonatomic) bool enabled;
@property(nonatomic) bool cachesInputImage;

@end

//MyCAFilter.m
#import "MyCAFilter.h"

@implementation MyCAFilter

@dynamic name, cachesInputImage, type, enabled;

- (id) initWithType: (NSString*) theType {
	NSLog(@"initWithType: type='%@'", theType);
    if ((self = [super init])) {
        delegate = [[CAFilter alloc] initWithType: theType];    //TODO:  remove delegate
    }
	return self;
}

- (id) initWithName: (NSString*) theName {
	NSLog(@"initWithName: name='%@'", theName);
    if ((self = [super init])) {
        delegate = [[CAFilter alloc] initWithName: theName];    //TODO:  remove delegate
    }
	return self;
}

- (id) initWithCoder: (NSCoder*) coder {
	NSLog(@"initWithCoder: coder=%@", coder);
	if ((self = [super init])) {
        delegate = [[CAFilter alloc] initWithCoder: coder];     //TODO:  remove delegate
    }
    return self;
}

- (void) setDefaults {
	NSLog(@"setDefaults");
	[delegate setDefaults];  //TODO:  remove delegate
}

- (void) encodeWithCoder: (NSCoder*) encoder {
	NSLog(@"encodeWithCoder:  coder=%@", encoder);
	[delegate encodeWithCoder:encoder];  //TODO:  remove delegate
}

- (id) mutableCopyWithZone: (NSZone*) zone {
    id result = [delegate mutableCopyWithZone:zone];   //TODO:  remove delegate
	NSLog(@"mutableCopyWithZone: zone=%@; result=%@", zone);
	return result;
}

- (id) copyWithZone: (NSZone*) zone {
    id result = [delegate copyWithZone:zone];  //TODO:  remove delegate
	NSLog(@"copyWithZone:  zone=%@; result=%@", zone, result);
	return result;
}

- (void) setValue: (id) value forKey: (NSString*) key {
	NSLog(@"setValue:  key=%@, value=%@", key, value);
	[delegate setValue:value forKey:key];	//TODO:  remove delegate
}

- (id) valueForKey:(id) key {
    id result = [delegate valueForKey:key];  //TODO:  remove delegate
	NSLog(@"valueForKey:  key=%@; result=%@", key, result);
	return result;
}

- (bool) isEnabled {
    bool result = [delegate isEnabled]; //TODO:  remove delegate
	NSLog(@"isEnabled; result=%d", result);
	return result; 
}

- (void) dealloc {
	NSLog(@"dealloc");
	[delegate release];		//TODO:  remove delegate
	[super dealloc];
}

- (bool) enabled {
    bool result = [delegate enabled];		//TODO:  remove delegate
	NSLog(@"enabled; result=%d", result);
	return result;
}
- (void) setEnabled: (bool) val {
	NSLog(@"setEnabled: value=%d", val);
	[delegate setEnabled:val];		//TODO:  remove delegate
}

- (void) setCachesInputImage: (bool) val {
	NSLog(@"setCachesInputImage: val=%d", val);
	[delegate setCachesInputImage:val];		//TODO:  remove delegate
}
- (bool) cachesInputImage {
    bool result = [delegate cachesInputImage];		//TODO:  remove delegate
	NSLog(@"cachesInputImage; result=%d", result);
	return result;
}

- (id) name {
    id result = [delegate name];		//TODO:  remove delegate
	NSLog(@"name; result=%@", result);
	return result;
}

- (void) setName: (NSString*) name {
	NSLog(@"setName: name='%@'", name);
	[delegate setName: name];		//TODO:  remove delegate
}

- (NSString*) type {
    NSString* result = [delegate type];		//TODO:  remove delegate
	NSLog(@"type; result=%@", result);
	return result;
}

- (struct RenderValueResult*) CA_copyRenderValue {
	struct RenderValueResult* result = [delegate CA_copyRenderValue];	//TODO:  remove delegate
    NSLog(@"CA_copyRenderValue; result=0x%08X, result.x1=0x%08X, result.x2=%d", result, result->x1, result->x2);
	return result;
}

@end

Using this class is a simple matter of editing ‘ReadPdfView.m‘ (working with Steven’s example project) to replace both instances of ‘[[CAFilter filterWithType:kCAFilterPageCurl] retain];‘ with ‘[[MyCAFilter alloc] initWithType: @”pageCurl”];‘. Note that it is also now safe to remove the ‘@class CAFilter;‘ and ‘extern NSString *kCAFilterPageCurl;‘ lines from this class.

Now obviously this still won’t fly with Apple, as it continues to use the private-API CAFilter class. But consider what we’ve accomplished; we’ve now inserted our own custom object into the rendering pipeline, and the core-animation framework is none-the-wiser. If we can now figure out how to get the same results without internally using the CAFilter instance, we will have cracked the page-curl animation.

Moving along, if we run this code through a complete page-curl animation, we see a very simple pattern emerge:

2011-02-09 00:59:11.694 PageCurlDemo[5501:207] initWithType: type='pageCurl'
2011-02-09 00:59:11.707 PageCurlDemo[5501:207] setDefaults
2011-02-09 00:59:11.711 PageCurlDemo[5501:207] setValue:  key=inputTime, value=0
2011-02-09 00:59:11.716 PageCurlDemo[5501:207] setValue:  key=inputAngle, value=-3.141593
2011-02-09 00:59:11.734 PageCurlDemo[5501:207] CA_copyRenderValue; result=0x04AF09E0, result.x1=0x00D96448, result.x2=65538
2011-02-09 00:59:11.767 PageCurlDemo[5501:207] valueForKey:  key=inputTime; result=0
2011-02-09 00:59:11.808 PageCurlDemo[5501:207] dealloc

This sequence of calls is repeated a number of times as the animation runs. None of the other methods that exist on the object are called. Every single one of these calls with the exception of ‘CA_copyRenderValue‘ originates in the example code; so now our task is constrained to the implementation of a single unknown method. But what a method it is. ‘CA_copyRenderValue‘ returns an instance of a fairly obtuse structure that has the following definition:

struct RenderValueResult { 
	int (**x1)(); 
	struct MyAtomic { 
		struct { 
			NSInteger x; 
		} _v; 
	} x2; 
} *_renderValueResult;

I’ve changed the name of the structure and its nested structure to avoid any issues with name collisions, but since the order and type of fields matches the private-API version there should be no issues in terms of compatibility between the different declared versions. At runtime this structure should be indistinguishable from the private-API version for all practical purposes (barring reflection, which could detect the difference in the naming).

Anyways, this structure contains two fields; ‘x1‘, which is a pointer to an array of functions that return integers, and ‘x2‘, which is simply an integer. Interestingly enough, the memory address of the returned data structure never differs by more than 256 bytes between calls, nor do the absolute values of ‘x1‘ or ‘x2‘ change. And here is where things start to get a bit murky. I’m going to forget about ‘x2‘ for a moment, as it is a simple type and its value never seems to vary. ‘x1‘ is not so easy.

By inspecting the value of ‘x1‘, I’ve determined that it references no more than 11 distinct functions (the 12th element in the result returned by CAFilter is NULL, and I assume that the NULL indicates the probable end of the meaningful data in the array). Moreover, the addresses of the functions returned do not appear to vary, even between independent runs of the application. Which implies to me that perhaps the result being returned is simply referencing some pre-existing object in memory.

But this is all speculation on my part. What’s needed here is more digging, so let’s create our own callback functions and see what we can discover about the way this data structure is being used by the core-animation framework. We can do that by adding the following to the MyCAFilter implementation:

int (**originalFuncs)();  //cache for the actual function pointers

//copy/paste this 11 times, incrementing both '0's each time...it's inelegant but it works
int callback0( id firstParam, ... ) {
	int myIndex = 0;
	NSLog(@"callback%d invoked, stack=%@", myIndex, [NSThread callStackSymbols]);
	
	va_list args;
	va_start(args, firstParam);
	int originalResult = originalFuncs[myIndex](firstParam, args);  //pass any params we recieved on to the original function; not sure if this is the correct way to do this
	
	NSLog(@"callback%d will return result:  %d", myIndex, originalResult);
	
	return originalResult;
}

And then by revising ‘CA_copyRenderValue‘ like so:

void* myCallbacks[11] = {&callback0, &callback1, &callback2, &callback3, &callback4, &callback5, &callback6, &callback7, &callback8, &callback9, &callback10};

- (struct RenderValueResult*) CA_copyRenderValue {
	struct RenderValueResult* result = [delegate CA_copyRenderValue];	//TODO:  remove delegate
	struct RenderValueResult* myResult = malloc(sizeof(struct RenderValueResult));
	myResult->x2 = result->x2;  //just copy the integer component of the result; 65538?
	
	//see how many functions there are before we encounter a NULL
	int funcIndex = 0;
	while (result->x1[funcIndex] != NULL) {
		funcIndex++;
		if (funcIndex >= 11) {
			NSLog(@"CA_copyRenderValue;  NULL sigil not found, assuming max number of functions is 11!");
			break;
		}
	}
	NSLog(@"CA_copyRenderValue;  found %d functions in delegate's result...", funcIndex);
	
	myResult->x1 = malloc(sizeof(int*) * (funcIndex + 1));		//we return this to the CA framework
	originalFuncs = malloc(sizeof(int*) * (funcIndex));			//we keep references to the original functions to use in our callbacks
	for (int index = 0; index < funcIndex ; index++) {
		originalFuncs[index] = result->x1[index];		//cache the original function pointers
		myResult->x1[index] = myCallbacks[index];     //put dummy callbacks into the result
	}
	myResult->x1[funcIndex] = NULL;
	
    NSLog(@"CA_copyRenderValue; result=0x%08X, result.x1=0x%08X, result.x2=%d", result, result->x1, result->x2);
	for (int index = 0; index < funcIndex; index++) {
		NSLog(@"CA_copyRenderValue; result->x1[%d]=0x%08X", index, result->x1[index]);
	}
	return myResult;
}

Now if we run the application, we get the following output:

2011-02-09 23:50:25.508 PageCurlDemo[10453:207] callback3 invoked, stack=(
	0   PageCurlDemo                        0x00006c2b callback3 + 50
	1   QuartzCore                          0x00d63347 CACopyRenderArray + 188
	2   QuartzCore                          0x00cc373e -[CALayer(CALayerPrivate) _copyRenderLayer:layerFlags:commitFlags:] + 1667
	3   QuartzCore                          0x00cc30b4 CALayerCopyRenderLayer + 55
	4   QuartzCore                          0x00cc11d2 _ZN2CA7Context12commit_layerEP8_CALayerjjPv + 122
	5   QuartzCore                          0x00cc10e1 CALayerCommitIfNeeded + 323
	6   QuartzCore                          0x00cc1069 CALayerCommitIfNeeded + 203
	7   QuartzCore                          0x00cc1069 CALayerCommitIfNeeded + 203
	8   QuartzCore                          0x00caf7b9 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 1395
	9   QuartzCore                          0x00caf0d0 _ZN2CA11Transaction6commitEv + 292
	10  QuartzCore                          0x00cdf7d5 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 99
	11  CoreFoundation                      0x00ef8fbb __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 27
	12  CoreFoundation                      0x00e8e0e7 __CFRunLoopDoObservers + 295
	13  CoreFoundation                      0x00e56bd7 __CFRunLoopRun + 1575
	14  CoreFoundation                      0x00e56240 CFRunLoopRunSpecific + 208
	15  CoreFoundation                      0x00e56161 CFRunLoopRunInMode + 97
	16  GraphicsServices                    0x0184c268 GSEventRunModal + 217
	17  GraphicsServices                    0x0184c32d GSEventRun + 115
	18  UIKit                               0x002d242e UIApplicationMain + 1160
	19  PageCurlDemo                        0x00002904 main + 102
	20  PageCurlDemo                        0x00002895 start + 53
)
2011-02-09 23:50:25.514 PageCurlDemo[10453:207] callback3 will return result:  9
2011-02-09 23:50:25.519 PageCurlDemo[10453:207] callback3 invoked, stack=(
	0   PageCurlDemo                        0x00006c2b callback3 + 50
	1   QuartzCore                          0x00ce5d12 _ZN2CA6Render7Encoder13encode_objectEPKNS0_6ObjectE + 30
	2   QuartzCore                          0x00ce670d _ZNK2CA6Render5Array6encodeEPNS0_7EncoderE + 113
	3   QuartzCore                          0x00ce5f24 _ZNK2CA6Render5Layer6encodeEPNS0_7EncoderE + 458
	4   QuartzCore                          0x00ce5cdb _ZN2CA6Render17encode_set_objectEPNS0_7EncoderEmjPNS0_6ObjectEj + 91
	5   QuartzCore                          0x00cc1215 _ZN2CA7Context12commit_layerEP8_CALayerjjPv + 189
	6   QuartzCore                          0x00cc10e1 CALayerCommitIfNeeded + 323
	7   QuartzCore                          0x00cc1069 CALayerCommitIfNeeded + 203
	8   QuartzCore                          0x00cc1069 CALayerCommitIfNeeded + 203
	9   QuartzCore                          0x00caf7b9 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 1395
	10  QuartzCore                          0x00caf0d0 _ZN2CA11Transaction6commitEv + 292
	11  QuartzCore                          0x00cdf7d5 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 99
	12  CoreFoundation                      0x00ef8fbb __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 27
	13  CoreFoundation                      0x00e8e0e7 __CFRunLoopDoObservers + 295
	14  CoreFoundation                      0x00e56bd7 __CFRunLoopRun + 1575
	15  CoreFoundation                      0x00e56240 CFRunLoopRunSpecific + 208
	16  CoreFoundation                      0x00e56161 CFRunLoopRunInMode + 97
	17  GraphicsServices                    0x0184c268 GSEventRunModal + 217
	18  GraphicsServices                    0x0184c32d GSEventRun + 115
	19  UIKit                               0x002d242e UIApplicationMain + 1160
	20  PageCurlDemo                        0x00002904 main + 102
	21  PageCurlDemo                        0x00002895 start + 53
)
2011-02-09 23:50:25.527 PageCurlDemo[10453:207] callback3 will return result:  9
2011-02-09 23:50:25.536 PageCurlDemo[10453:207] callback3 invoked, stack=(
	0   PageCurlDemo                        0x00006c2b callback3 + 50
	1   QuartzCore                          0x00ce5d34 _ZN2CA6Render7Encoder13encode_objectEPKNS0_6ObjectE + 64
	2   QuartzCore                          0x00ce670d _ZNK2CA6Render5Array6encodeEPNS0_7EncoderE + 113
	3   QuartzCore                          0x00ce5f24 _ZNK2CA6Render5Layer6encodeEPNS0_7EncoderE + 458
	4   QuartzCore                          0x00ce5cdb _ZN2CA6Render17encode_set_objectEPNS0_7EncoderEmjPNS0_6ObjectEj + 91
	5   QuartzCore                          0x00cc1215 _ZN2CA7Context12commit_layerEP8_CALayerjjPv + 189
	6   QuartzCore                          0x00cc10e1 CALayerCommitIfNeeded + 323
	7   QuartzCore                          0x00cc1069 CALayerCommitIfNeeded + 203
	8   QuartzCore                          0x00cc1069 CALayerCommitIfNeeded + 203
	9   QuartzCore                          0x00caf7b9 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 1395
	10  QuartzCore                          0x00caf0d0 _ZN2CA11Transaction6commitEv + 292
	11  QuartzCore                          0x00cdf7d5 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 99
	12  CoreFoundation                      0x00ef8fbb __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 27
	13  CoreFoundation                      0x00e8e0e7 __CFRunLoopDoObservers + 295
	14  CoreFoundation                      0x00e56bd7 __CFRunLoopRun + 1575
	15  CoreFoundation                      0x00e56240 CFRunLoopRunSpecific + 208
	16  CoreFoundation                      0x00e56161 CFRunLoopRunInMode + 97
	17  GraphicsServices                    0x0184c268 GSEventRunModal + 217
	18  GraphicsServices                    0x0184c32d GSEventRun + 115
	19  UIKit                               0x002d242e UIApplicationMain + 1160
	20  PageCurlDemo                        0x00002904 main + 102
	21  PageCurlDemo                        0x00002895 start + 53
)
2011-02-09 23:50:25.566 PageCurlDemo[10453:207] callback3 will return result:  9
2011-02-09 23:50:25.578 PageCurlDemo[10453:207] callback4 invoked, stack=(
	0   PageCurlDemo                        0x00006cca callback4 + 50
	1   QuartzCore                          0x00ce670d _ZNK2CA6Render5Array6encodeEPNS0_7EncoderE + 113
	2   QuartzCore                          0x00ce5f24 _ZNK2CA6Render5Layer6encodeEPNS0_7EncoderE + 458
	3   QuartzCore                          0x00ce5cdb _ZN2CA6Render17encode_set_objectEPNS0_7EncoderEmjPNS0_6ObjectEj + 91
	4   QuartzCore                          0x00cc1215 _ZN2CA7Context12commit_layerEP8_CALayerjjPv + 189
	5   QuartzCore                          0x00cc10e1 CALayerCommitIfNeeded + 323
	6   QuartzCore                          0x00cc1069 CALayerCommitIfNeeded + 203
	7   QuartzCore                          0x00cc1069 CALayerCommitIfNeeded + 203
	8   QuartzCore                          0x00caf7b9 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 1395
	9   QuartzCore                          0x00caf0d0 _ZN2CA11Transaction6commitEv + 292
	10  QuartzCore                          0x00cdf7d5 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 99
	11  CoreFoundation                      0x00ef8fbb __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 27
	12  CoreFoundation                      0x00e8e0e7 __CFRunLoopDoObservers + 295
	13  CoreFoundation                      0x00e56bd7 __CFRunLoopRun + 1575
	14  CoreFoundation                      0x00e56240 CFRunLoopRunSpecific + 208
	15  CoreFoundation                      0x00e56161 CFRunLoopRunInMode + 97
	16  GraphicsServices                    0x0184c268 GSEventRunModal + 217
	17  GraphicsServices                    0x0184c32d GSEventRun + 115
	18  UIKit                               0x002d242e UIApplicationMain + 1160
	19  PageCurlDemo                        0x00002904 main + 102
	20  PageCurlDemo                        0x00002895 start + 53
)

Followed by a crash. Something causes the attempt to invoke the fourth callback function to die; probably related to the questionable way that I’m passing arguments to it. Not knowing what the proper signature for the callback functions is, I’ve made them all accept a variable number of ‘id’ parameters, which should cover most cases. However the best way to pass these arguments on to the original implementation is not clear.

For what it’s worth, I tried a number of alternate ways to invoke this function, all of which resulted in a crash. Skipping the invocation and just returning a hard-coded value from my callback prevented the crash, but didn’t result in any more callbacks being invoked. Presumably core-animation noticed that my hard-coded return value didn’t match what it was expecting, and decided to abort the rest of its rendering transaction.

And unfortunately, here is where I need to leave this interesting little diversion for now, unless/until I can figure out a way to move it forward. If you have any suggestions please don’t hesitate to let me know. I feel like I’m getting close to the answer here, but it’s still quite a ways away.

Update

If anyone is interested, you can download a complete XCode project containing the latest revision of my code. If you decide to take a crack at solving this problem, I wish you luck, and please do consider reporting back with your results.

Posted in coding, objective-c | Tagged , , , | 10 Comments

[Site Status] Server Instability

The 10-year-old PC that I’m currently using to host this site seems to be going through an end-of-life crisis. If you’re reading this then it must currently be up (or you accessed Google’s cached version), but there’s no guarantee it will stay that way. So don’t be surprised if this blog is intermittently down for the next few days. If it does go down, Google’s cache of the entire Internet is a good place to look.

And no worries, I’ve already ordered the parts for a replacement box. It will be an Atom D510 based server, with a meager 60 GB SSD, and it should be anywhere from 2-3 times as fast as the current aging server while using less than one-third as much power and running in complete silence. Progress is awesome sometimes.

I had really wanted to wait and build an AMD Brazos based server (would be about 50% faster than the Atom D510 while using a comparable amount of energy), but the replacement needs to be put in place sooner rather than later, and there are currently no Brazos chips/boards available here in Australia. Oh well. I’ll still have a bit of fun assembling, testing, and configuring the Atom box. Maybe I’ll add a quick post documenting the process, as well.

Posted in status | Leave a comment

[Objective-C + Cocoa] NSAutoreleasePool and Threads

If you have a multithreaded iPhone/iPad/Cocoa application, you are probably aware that for each thread you create you need to set up an auto-release pool for that thread. If you don’t do this then you’ll get some nice messages in your debugger log informing you that your app is leaking memory (for shame!). Personally I think that any boilerplate code that must be added to each thread should be handled automatically by the SDK/runtime environment, but that’s completely beside the point here. The point here is that the standard example given for this boilerplate code is generally something along the lines of:

- (void) myThreadEntryPoint {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];  //set up a pool
	// [do work here]
	[pool drain];
}

And this is all well and good for simple use-cases, but often a developer wants to have a thread that runs forever (or for the lifetime of the application), in which case the ‘[do work here]‘ section might look like:

while (! [self shouldTerminate]) {
	//[do some stuff]
	[NSThread sleepForTimeInterval:10.0s];  //sleep for a bit
}

If we insert this code into the standard boilerplate example, we get the following:

- (void) myThreadEntryPoint {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];  //set up a pool
	while (! [self shouldTerminate]) {
		//[do some stuff]
		[NSThread sleepForTimeInterval:10.0s];  //sleep for a bit
	}
	[pool drain];
}

And now we have a problem, one that’s particularly easy for new developers to create. Technically this code is following the standard example, but it is also creating a slow memory leak, assuming that any amount of non-trivial work is being performed in the ‘[do some stuff]‘ section. The problem is that the auto-release pool is never drained until the thread is ready to terminate, meaning that all the objects that are in the pool do not get released. They simply accumulate in memory. If you write code like what’s shown above, a crash is inevitable; it’s only a question of when.

In an environment as memory-constrained as an iPhone, anything accumulating in memory is a Very Bad Thing™. Doubly so in this case because the issue will not be detected in debugging tools like Leaks or Allocations. You will not get any console messages nagging you about memory leaks. You have an auto-release pool in place, after all, and you’re releasing it properly, so how is the compiler or any other tool to know that there’s an issue (in fact, in order to detect that there is an issue in the above code the compiler would have to be able to solve the halting problem)?

If you code like this, your application will just slowly consume more and more memory until it eventually crashes. And you can’t even count on getting a reliable stack-trace when it does crash, because the allocation that finally brings the thing crashing down might be nowhere near the code associated with the actual leak.

Luckily, this error is simple to avoid. Just change the code so that it’s like this:

- (void)myThreadEntryPoint {	
	while (![self shouldTerminate]) {
		NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
		
		//[do work here]

		[pool drain];  
		[NSThread sleepForTimeInterval:10.0s];  //sleep for a bit
	}
}

And problem solved. The auto-release pool is released and reset on each iteration of the loop, as soon as we are done doing our actual work. Objects are released, memory is freed, everyone is happy, and on the next loop iteration the process starts over again. It’s a very simple fix, but the issue that it addresses is easy to overlook, and difficult to track down once overlooked.

Note that this is mentioned in Apple’s official documentation on the subject, which states:

If your application or thread is long-lived and potentially generates a lot of 
autoreleased objects, you should periodically drain and create autorelease pools 
(like the Application Kit does on the main thread); otherwise, autoreleased 
objects accumulate and your memory footprint grows. If, however, your detached 
thread does not make Cocoa calls, you do not need to create an autorelease pool.

So if you missed it in the official documentation before, now you know; and hopefully you also know why it’s important to pay attention to this little piece of advice. If you don’t want your application to crash and die randomly, that is.

Posted in coding, objective-c | Tagged , | 1 Comment

[C (and variants)] Pointer Declarations: You’re Doing it Wrong

It happens on occasion, in the programming world, that bad coding conventions become the norm. Consider for instance the following two lines of code:

int* pointer1;
int *pointer2;

Both lines are equivalent as far as the compiler is concerned, but when a human being parses these lines intuitively, they say slightly different things. The first line parses as “create a variable called ‘pointer1‘ that is of type ‘int*‘”, while the second line comes across as “create a variable called ‘*pointer2‘ that is of type ‘int‘”. One of these interpretations matches the semantics of the language and what the compiler actually does when it processes this code, and the other does not. If you are familiar with pointer types, then you know which is which, and if not then I’ll just tell you that it’s the first version. The first line of code is the correct way to declare a pointer.

Sadly, current coding conventions actually favor the second line. The wikipedia page on this subject even attempts to justify this backwards way of declaring a pointer with the explanation that “when the program dereferences the pointer, it has the type of the object to which it points”. That may hold true in simple cases such as the above example, but now consider the following:

int *functionThatReturnsAPointer();

Am I declaring here a function such that “when the program dereferences [it], it has the type [int]”? No. Even if that made the slightest bit of sense semantically given that trying to dereference a function with the unary ‘*‘ operator is incorrect, it is not what I’m doing. I’m declaring a function that has a return type of ‘int*‘. Not a return type of ‘magic value that if I put a star in front of it will give me an int‘. If we move to a language with slightly different syntax, like Objective-C, the problem with the convention becomes even more pronounced. For instance:

- (NSString*) toString;   //this is a valid function declaration in Objective-C
- (NSString) *toString2;  //this will make the Objective-C compiler very, very sad

- (void) functionWithAString: (NSString*) string;   //also valid
- (void) functionWithAString2: (NSString) *string;  //not a chance

Objective-C does an excellent job of highlighting the problem with the standard convention because in Objective-C the return and parameter types in function declarations must be enclosed within parenthesis. And the asterisk must be included in the parenthesis with the name of the type, because it is part of the type of the object, and not part of its name as the standard convention tries to imply.

And that’s the other half of the justification that is used to defend the standard convention; that by “hiding” the pointer type so that it looks like part of the variable name developers (in particular, novice developers) can use pointer types without having to really understand or think about pointer types. This is bad for a couple of reasons. First off, it’s trying to twist the semantics of the language in a way that is not accurate. And more importantly, it discourages (and/or delays) developers from taking the time to understand what pointer types are and how they work. Yes, pointers can be confusing, but if you’re going to work in a language that includes them then you need to understand how they work. How they really work; not just how a confusing coding convention tries to make it seem like they work.

So at the end of the day, the asterisk character in C and C-like languages is part of the type being declared and not part of its name, and it’s time to start putting it where it belongs; with the type. A bad convention should not be allowed to stand just because it is the convention. Write code that intuitively matches its semantic meaning in the language, not code that is designed to trick people who don’t really understand how pointers work into being able to work with them anyways.

Lastly, and only somewhat tangentally, the following are all examples of very bad coding style, and you should not write code like this no matter where you put your asterisks:

int x, y;    //no
int *x, y;   //bad
int* x, y;   //not any better
int *x, *y;  //you get the idea... 
int* x, *y;
int x, *y;

Instead, simply do like so:

int x;
int* y;

It’s not like you pay by the newline when you write code. Limit your variable declarations to one-per-line. Trust me, you’ll write more readable code that way.

Posted in c, coding | Tagged , | 5 Comments

[Objective-C + Cocoa] Object Inspection

I’m a big fan of reflection. Always have been since I was first exposed to it in Java. For those not familiar with the concept, reflection (or introspection as it is alternately called) allows one to inspect and/or access the properties and methods of an object instance at runtime, without needing any specific details about its declared type or fields. Though it may seem like a fairly minor feature, reflection is used to great effect in the Java world by the likes of Spring and EasyMock, to name just a few.

So it’s a bit puzzling to me, then, that reflection seems to have been long forgotten in the realm of Objective-C. Apple’s official documentation on this topic even mildly discourages its use (“You typically do not need to use the Objective-C runtime library directly when programming in Objective-C”). Granted, the performSelector: method sees fairly frequent use in many Cocoa applications, but this is a minor concession in a language where virtually every method-call resolves to a table lookup to find the implementation of the method being called (and you can even swap method implementations around at runtime by mucking with the lookup table).

So as a demonstration of some of the neat things that can be done using reflection in Objective-C, I’ve put together some code that will “deconstruct” an arbitrary object. This code will:

  • Print the signature of any methods that exist on an object.
  • Print the name and type of any properties declared on the object
  • Print the name and type of any instance-level fields declared as members of the object.
  • Optionally recurse through any non-primitive non-nil field/property types.
  • Optionally recurse through the object’s superclasses until the root class (NSObject, typically) is reached.
  • Attempt to track and return the real amount of memory allocated to the object (not fully accurate).

The code is packaged as a category on NSObject, meaning that if you include it in your project you can simply call ‘[obj printObject:obj toDepth:0]‘ in order to print the details of any object you are interested in. Anyways, here is the code that works all the magic:

#import <objc/objc-class.h>
#import <malloc/malloc.h>

@implementation NSObject(object_print)

- (NSString*) appendTo: (NSString*) base with: (NSString*) rest {
	return [NSString stringWithFormat:@"%@%@", base, rest];
}

- (int) printObjectInternal:(id)anObject printState: (NSMutableArray*)state friendlyName: (NSString*) objName withIndent: (NSString*)indent fromDepth: (int)currentDepth toDepth: (int)maxDepth {
	if (anObject == nil || anObject == NULL || currentDepth > maxDepth) {
		//nothing to do
		return 0;
	}
	
	[state addObject:anObject];
	
	//process properties for the class and its superclass(es)
	int totalSize = 0;
	int mySuperclassDepth = currentDepth;
	Class processingClass = [anObject class];
	while (processingClass != nil && processingClass != [NSObject class] && mySuperclassDepth <= maxDepth) {
		unsigned int numFields = 0;
		
		//methods
		Method* methods = class_copyMethodList(processingClass, &numFields);
		NSLog(@"[%@] - %@  Printing object:  type=%@ : %@ ...", objName, indent, processingClass, class_getSuperclass(processingClass));
		NSLog(@"[%@] - %@  Printing object methods:  type=%@, numMethods=%d", objName, indent, processingClass, numFields);
		for (int index = 0; index < numFields; index++) {
			unsigned int numArgs = method_getNumberOfArguments(methods[index]);
			const char* name = sel_getName(method_getName(methods[index]));
			NSString* argString = @"";
			char* copyReturnType = method_copyReturnType(methods[index]);
			for (int argIndex = 0; argIndex < numArgs; argIndex++) {
				char* argType = method_copyArgumentType(methods[index], argIndex);
				if (argIndex > 2) {
					argString = [argString stringByAppendingFormat:@" argName%d: (%@) arg%d", argIndex - 2, [self codeToReadableType: argType], argIndex - 2]; 
				}
				else if (argIndex > 1) {
					argString = [argString stringByAppendingFormat:@" (%@) arg%d", [self codeToReadableType: argType], argIndex - 2];
				}
				free(argType);
			}
			
			if (numArgs <= 2) {
				NSLog(@"[%@] - %@ (%@)  - (%@) %s;", objName, indent, processingClass, [self codeToReadableType: copyReturnType], name);
			}
			else {
				NSLog(@"[%@] - %@ (%@)  - (%@) %s %@;", objName, indent, processingClass, [self codeToReadableType: copyReturnType], name, argString);
			}
			free(copyReturnType);
		}
		
		//properties (i.e. things declared with '@property')
		objc_property_t* props = class_copyPropertyList(processingClass, &numFields);
		NSLog(@"[%@] - %@  Printing object properties:  type=%@, numFields=%d", objName, indent, processingClass, numFields);
		for (int index = 0; index < numFields; index++) {
			objc_property_t prop = props[index];
			const char* fieldName = property_getName(prop);
			const char* fieldType = property_getAttributes(prop);
			NSLog(@"[%@] - %@ (%@) @property %@ %s;", objName, indent, processingClass, [self codeToReadableType: fieldType], fieldName);
			
			@try {
				id fieldValue = [anObject valueForKey:[NSString stringWithFormat:@"%s", fieldName]];
				totalSize += malloc_size(fieldValue);
				NSString* typeString = [NSString stringWithFormat:@"%s", fieldType];
				NSRange range = [typeString rangeOfString:@"T@\""];
				if (range.location == 0 && fieldValue && ! [state containsObject:fieldValue]) {
					//the field is an object-type, so print its size as well
					NSLog(@"[%@] - %@ (%@)\t  Expanding property [%s]:", objName, indent, processingClass, fieldName);
					totalSize += [self printObjectInternal: fieldValue printState: state friendlyName: objName withIndent: [NSString stringWithFormat:@"%@\t", indent] fromDepth: mySuperclassDepth + 1 toDepth: maxDepth];
				}
			}
			@catch (id ignored) {
				//couldn't get it with objectForKey, so try an alternate way
				void* fieldValue = NULL;
				object_getInstanceVariable(anObject, fieldName, &fieldValue);
				if (fieldValue != NULL && fieldValue != nil) {
					totalSize += malloc_size(fieldValue);
				}
			}
		}
		
		//ivars (i.e. declared instance members)
		Ivar* ivars = class_copyIvarList(processingClass, &numFields);
		NSLog(@"[%@] - %@ (%@) Printing object ivars:  type=%@, numFields=%d", objName, indent, processingClass, processingClass, numFields);
		for (int index = 0; index < numFields; index++) {
			Ivar ivar = ivars[index];
			id fieldValue = object_getIvar(anObject, ivar);
			
			const char* fieldName = ivar_getName(ivar);
			const char* fieldType = ivar_getTypeEncoding(ivar);
			
			NSLog(@"[%@] - %@ (%@) %@ %s;", objName, indent, processingClass, [self codeToReadableType: fieldType], fieldName);
			int mSize = malloc_size(fieldValue);
			totalSize += mSize;
			
			@try {
				NSString* typeString = [NSString stringWithFormat:@"%s", fieldType];
				NSRange range = [typeString rangeOfString:@"@"];
				if (range.location == 0 && (! [state containsObject:fieldValue]) && mSize > 0) {
					//the field is an object-type, so print its size as well
					NSLog(@"[%@] - %@ (%@)\t  Expanding ivar [%s]:", objName, indent, processingClass, fieldName);
					totalSize += [self printObjectInternal: fieldValue printState: state friendlyName: objName withIndent: [NSString stringWithFormat:@"%@\t", indent] fromDepth: mySuperclassDepth + 1 toDepth: maxDepth];
					
					//see if it's a countable type, just for fun
					if ([fieldValue respondsToSelector:@selector(count)]) {
						//if we can count it, print the count
						NSLog(@"[%@] - %@ (%@)\t\t  Container Count:  name=%s, type=%s, count=%d", objName, indent, processingClass, fieldName, fieldType, [fieldValue count]);
					}
				}
			}
			@catch (id ignored) {
				//couldn't print it
			}
		}
		
		//process indexed ivars (extra bytes allocated at end of object; no name available, just size)
		void* extraBytes = object_getIndexedIvars(anObject);
		NSLog(@"[%@] - %@ (%@) Printing object indexedIvars:  type=%@, extraBytes=%d", objName, indent, processingClass, processingClass, malloc_size(extraBytes));
		
		//process superclass
		NSLog(@"[%@] - %@ (%@) Superclass of %@ is %@", objName, indent, processingClass, processingClass, class_getSuperclass(processingClass));
		processingClass = class_getSuperclass(processingClass);
		mySuperclassDepth++;
	}
	
	return totalSize;
}

- (int) printObject: (id)anObject toDepth: (int) maxDepth {
	if (! anObject) {
		anObject = self;
	}
	if (maxDepth < 0) {
		maxDepth = 0;
	}
	NSMutableArray* state = [[NSMutableArray alloc] initWithCapacity: 1024];
	int result = [self printObjectInternal:anObject printState: state friendlyName: [[anObject class] description] withIndent: @"" fromDepth: 0 toDepth: maxDepth];
	[state release];
	
	return result;
}

@end

One minor omission from the above code is the codeToReadableType: function. This method simply takes an Objective-C type code (things like “^^f” and “C” and “:”) and parses it back into a human-readable format. It’s rather verbose for what it does, so it’s available at the end of this post.

Anyways, this code imbues any type derived from NSObject with a ‘printObject:toDepth:‘ method which does pretty much what its name implies. For a given object, it will print information about its methods, properties, and fields, and if you specify a depth greater than 0 it will also recurse through any non-primitive property or field and also the object’s superclass(es) to the specified depth limit. Want to know all 327 methods that exist on a UIView instance, including the ones that Apple doesn’t tell you about? Then invoke this method on a UIView instance, or on an instance of anything that extends UIView using a ‘toDepth:‘ of 1 or greater.

Also note that this method is designed to handle circular references in the object hierarchy and avoid getting trapped in a cycle; if you paid close attention to the code you will have noticed the ‘state‘ array, which keeps track of every object instance that has been encountered in the hierarchy. Before recursing through a new object instance the method first checks this array to make sure that instance hasn’t already been encountered, and avoids recursing through the object if it has already been seen. So you don’t have to worry about circular references killing the ‘printObject:toDepth:‘ routine.

All-told, this code can be quite fun to play with if you’re curious about your Objective-C runtime environment. It lets you see in a human-readable way what the object hierarchy looks like in memory. Note that while this code also attempts to keep track of the number of bytes allocated by each object it traverses, it does not do a complete job of it, and the returned value shouldn’t be assumed to be an accurate representation of the size of the object instance. It may work for simple types, but you certainly shouldn’t rely on it for anything significant.

Lastly, here is the codeToReadableType: implementation. It could almost certainly be more compactly written using regular expressions and pattern matching, but this will get the job done. Just include it as part of the category, and you’ll be all set.

- (NSString*) codeToReadableType: (const char*) code {
	NSString* codeString = [NSString stringWithFormat:@"%s", code];
	NSString* result = [NSString stringWithString:@""];
	
	bool array = NO;
	NSString* arrayString;
	//note:  we parse our type from left to right, but build our result string from right to left
	for (int index = 0; index < [codeString length]; index++) {
		char nextChar = [codeString characterAtIndex:index];
		switch (nextChar) {
			case 'T':
				//a placeholder code, the actual type will be specified by the next character
				break;
			case ',':
				//used in conjunction with 'T', indicates the end of the data that we care about 
				//we could further process the character(s) after the comma to work out things like 'nonatomic', 'retain', etc., but let's not
				index = [codeString length];
				break;
			case 'i':
				//int or id
				if (index + 1 < [codeString length] && [codeString characterAtIndex:index + 1] == 'd') {
					//id
					result = [self appendTo: (array ? @"id[" : @"id") with: result];
					index++;
				}
				else {
					//int
					result = [self appendTo: (array ? @"int[" : @"int") with: result];
				}
				break;
			case 'I':
				//unsigned int
				result = [self appendTo: (array ? @"unsigned int[" : @"unsigned int") with: result];
				break;
			case 's':
				//short
				result = [self appendTo: (array ? @"short[" : @"short") with: result];
				break;
			case 'S':
				//unsigned short
				result = [self appendTo: (array ? @"unsigned short[" : @"unsigned short") with: result];
				break;
			case 'l':
				//long
				result = [self appendTo: (array ? @"long[" : @"long") with: result];
				break;
			case 'L':
				//unsigned long
				result = [self appendTo: (array ? @"unsigned long[" : @"unsigned long") with: result];
				break;
			case 'q':
				//long long
				result = [self appendTo: (array ? @"long long[" : @"long long") with: result];
				break;
			case 'Q':
				//unsigned long long
				result = [self appendTo: (array ? @"unsigned long long[" : @"unsigned long long") with: result];
				break;
			case 'f':
				//float
				result = [self appendTo: (array ? @"float[" : @"float") with: result];
				break;
			case 'd':
				//double
				result = [self appendTo: (array ? @"double[" : @"double") with: result];
				break;
			case 'B':
				//bool
				result = [self appendTo: (array ? @"bool[" : @"bool") with: result];
				break;
			case 'b':
				//char and BOOL; is stored as "bool", so need to ignore the next 3 chars
				result = [self appendTo: (array ? @"BOOL[" : @"BOOL") with: result];
				index += 3;
				break;
			case 'c':
				//char?
				result = [self appendTo: (array ? @"char[" : @"char") with: result];
				break;
			case 'C':
				//unsigned char
				result = [self appendTo: (array ? @"unsigned char[" : @"unsigned char") with: result];
				break;
			case 'v':
				//void
				result = [self appendTo: @"void" with: result];
				break;
			case ':':
				//selector
				result = [self appendTo: @"SEL" with: result];
				break;
			case '^':
				//pointer
				result = [self appendTo: @"*" with: result];
				break;
			case '@': {
				//object instance, may or may not include the type in quotes, like @"NSString"
				if (index + 1 < [codeString length] && [codeString characterAtIndex:index + 1] == '"') {
					//we can get the exact type
					int endIndex = index + 2;
					NSString* theType = @"";
					while ([codeString characterAtIndex:endIndex] != '"') {
						theType = [NSString stringWithFormat:@"%@%c", theType, [codeString characterAtIndex:endIndex]];
						endIndex++;
					}
					theType = [self appendTo: theType with: @"*"];
					result = [self appendTo: theType with: result];
				
					index = endIndex + 1;
				}
				else {
					//all we know is that it's an object of some kind
					result = [self appendTo: @"NSObject*" with: result];
				}
				break;
			}
			case '{': {
				//struct, we don't fully process these; just echo them
				index++;
				int numBraces = 1;
				NSString* theType = @"{";
				while (numBraces > 0) {
					char next = [codeString characterAtIndex:index];
					theType = [NSString stringWithFormat:@"%@%c", theType, next];
					if (next == '{') {
						numBraces++;
					}
					else if (next == '}') {
						numBraces--;
					}
					
					index++;
				}
				result = [NSString stringWithFormat:@"struct %@%@", theType, result];
				
				index--;
				break;
			}
			case '?':
				//IMP and function pointer
				result = [self appendTo: @"IMP" with: result];
				break;
			case '[':
				//array type
				array = YES;
				arrayString = @"";
				result = [self appendTo: @"]" with: result];
				break;
			case ']':
				//array type
				array = NO;
				break;
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
				//for a statically-sized array, indicates the number of elements
				if (array) {
					arrayString = [NSString stringWithFormat:@"%@%c", arrayString, nextChar];
				}
				break;
			default:
				break;
		}
	}
	
	return result;
}
Posted in coding, objective-c | Tagged , | 3 Comments