Dynamic Dispatch Discrepancy


*Update*
By the way I unsuccessfully tried overriding the methods() method and almost went as far as overriding send on my mock wrapping ObjCMock class. My experience in MOP tells me that overriding send is likely rooted in trouble so I decided against it.

Yesterday’s Ruby drama ended with my finding a hack around the dropped parameters. If I was more familiar with Ruby, had more time to kill, or if it were Groovy instead then my solution would be much cleaner.

The problem:
Ruby Mocks under RSpec (have no idea if the problem persists in other Ruby scripting environments) do not receive parameters correctly from native Cocoa objects.

The hack:
Use a home-grown mock to intercept the parameter and pass along to the Ruby mock.

The code: (bowling_controller_spec.rb)

require File.dirname(__FILE__) + ‘/test_helper’

require “BowlingController.bundle”
OSX::ns_import :BowlingController
OSX::ns_import :BowlingProtocol
require ‘delegate’

include OSX

class ObjCMock
attr_accessor :delegate
def roll(num)
@delegate.roll(num)
end

# def method_missing(m, *args)
# puts “Forwarding message”
# @delegate.send(m, args)
# end

def initialize(theMock)
@delegate = theMock
end
end

describe BowlingController do
before(:each) do
@controller = BowlingController.new
@bowling = mock(‘BowlingProtocol’)
@text_field = mock(‘Pins’)
@text_field.stub!(:intValue).and_return(10)
@controller.pins = @text_field
end

it “should roll a ball” do
@controller.roll
end

it “should roll a ball and get the value from the pins outlet” do
@text_field.should_receive(:intValue).and_return(0)
@controller.roll
end

it “should be an OSX::NSObject” do
@controller.is_a?(OSX::NSObject).should == true
end

it “should have an outlet to a bowling object” do
@controller.bowling = @bowling
end

it “should send the pin value to the bowling object” do
@controller.bowling = ObjCMock.new(@bowling)
# @controller.bowling = @bowling
@bowling.should_receive(:roll).with(NSNumber.numberWithInt(10))

@controller.roll
end
end

I left in the commented out method_missing magic that doesn’t work the way I’d expect it. Ideally I’d like to tuck this wrapping mock to the side without needing to define the specific signature I’m mocking. Ideally method_missing would give me the ability to answer any message. Alas, I get “does not reckognize selector” errors from NSProxy when I enable it. I have a feeling that NSProxy probably looks through the defined methods on it’s delegate and throws this error without 1st trying to invoke the method… effectively killing any dynamic magic ability from objects passed into Cocoa land. I also have a feeling that there are a few other holes in the proxy approach with Ruby/Cocoa. If anybody happens to know the details feel free to chime in.

RSpec for iPhone development


So last night I did some laundry, taught my oldest daughter how to write code, took both of my girls shoe shopping, washed the little one’s hair, raked the leaves cooked dinner, then finally got a start on trying RSpec against a Cocoa Touch controller class. Yep, I was kinda sorta busy. Had I a little more time I wouldn’t be stumped where I am now but since my schedule is as packed as a VW beetle carrying a family of 6 on a Ski vacation for 2 months there’s little wiggle room for discovery and surprises, so instead I ask you, my humble readers (all three of you) to figure out the hard stuff for me. I will gladly repay you by splattering your hard work across the internets next to a picture of my smiling eyebrows and deep claims referencing my name exclusively.

I’m working through a Bowled over by Ruby/Cocoa example written against Mac OSX Cocoa APIs and trying to adapt it to CocoaTouch. The first challenge comes from trying to use RubyCocoa to access a controller bound to certain CocoaTouch APIs like UIKit. It doesn’t work. So instead I was clever enough to side step the problem and generate my BowlingController as a vanilla NSObject using the “script/generate model” command from Dr. Nic’s RBiPhoneTest. I use rbiPhoneTest to setup the project test structure as well so now I have a rake file in my root that I use to build the necessary BowlingController.bundle for RSpec (or any Ruby code) to access. I will plan a write up on all of this after I get through the entire tutorial. Long story lengthened I have RSpec loading and running my BowlingController, I’ve made several iterations even into creating IBOutlet/IBAction bindings for Interface Builder. (I’m not sure if they work b/c I haven’t gotten that far yet.) I get stuck at the part where my controller is supposed to pass the pins value from the text field into the bowling model object. My Ruby bowling stub reports that roll has been invoked with no arguments even though I am most definitely passing a value into the method. I keep getting, “Spec::Mocks::MockExpectationError in ‘OSX::BowlingController should send the pin value to the bowling object’
Mock ‘Bowling’ expected :roll with (10) but received it with (no args)
./test/bowling_controller_spec.rb:38:”
I’ve even went as far as passing a literal 10 into the call. At this point it looks like some sort of Ruby/Cocoa alignment issue or something.

bowling_controller_spec.rb

require File.dirname(__FILE__) + ‘/test_helper’

require “BowlingController.bundle”
OSX::ns_import :BowlingController

include OSX

describe BowlingController do
before(:each) do
@controller = BowlingController.new
@bowling = mock(‘Bowling’)
@text_field = mock(‘Pins’)
@text_field.stub!(:intValue).and_return(10)
@controller.pins = @text_field
end

it “should roll a ball” do
@controller.roll
end

it “should roll a ball and get the value from the pins outlet” do
@text_field.should_receive(:intValue).and_return(0)
@controller.roll
end

it “should be an OSX::NSObject” do
@controller.is_a?(OSX::NSObject).should == true
end

it “should have an outlet to a bowling object” do
@controller.bowling = @bowling
end

it “should send the pin value to the bowling object” do
@controller.bowling = @bowling
@bowling.should_receive(:roll).with(10)

@controller.roll
end
end

BowlingController.h

#import

@class UITextField;
@class Bowling;

@interface BowlingController : NSObject {
UITextField* pins;
Bowling* bowling;
}
@property (nonatomic, retain) UITextField* pins;
@property (nonatomic, retain) Bowling* bowling;

-(void) roll;
@end

BowlingController.m

#import “BowlingController.h”
#import “Bowling.h”

@implementation BowlingController
@synthesize pins;
@synthesize bowling;

-(void) roll{
[self.bowling roll:[self.pins intValue]];
}

@end

// This initialization function gets called when we import the Ruby module.
// It doesn’t need to do anything because the RubyCocoa bridge will do
// all the initialization work.
// The rbiphonetest test framework automatically generates bundles for
// each objective-c class containing the following line. These
// can be used by your tests.
void Init_BowlingController() { }

Bowling.h

#import

@interface Bowling : NSObject {

}
– (void) roll:(int) pins;
@end

Bowling.m

#import “Bowling.h”

@implementation Bowling
– (void) roll:(int) pins{
}

@end

// This initialization function gets called when we import the Ruby module.
// It doesn’t need to do anything because the RubyCocoa bridge will do
// all the initialization work.
// The rbiphonetest test framework automatically generates bundles for
// each objective-c class containing the following line. These
// can be used by your tests.
void Init_Bowling() { }

Note to self: I gotta find better analogies. These are really starting to suck.

Murder me!


I’m spending too much time trying to figure out how to assert that a string is properly URL encoded! I refuse to take the traditional approach of running code in the application and peeking through debugger goggles or some obscure log output. Unit tests are made for this sorta thing! I’e had some tips that suggest how to perform URL encoding but experience teaches me not to trust any particular tip unless I can confirm it in a learning unit test. That’s how I self-educated myself on Ruby over the weekend. I wrote a bunch of learning tests to confirm every code snippet I read online. So why is it that when I assert @”ab%%c” == @”ab c” the error message echoes back “ab != ab c”??? I’m using Google tools for Mac to run iPhone unit test suites.

One other thing… could somebody please invent/has somebody invented an “automatic add new files to svn” plugin for XCode? If I botch one more commit my team is going to cut off my water supply and replace my contact lens solution with Windsor’s clear hot sauce. I love the clear stuff because I can dump a whole bunch on my meal without my wife nagging me about my heartburn.

Objective C, Almost closures?


I’m about toe deep in ObjC and Cocoa and I’m starting to get that warm fuzzy feeling everytime I message and object without needing a type cast. I get really warm/fuzzy when I use @selector() to throw extra executable logic from a long running process back onto the main thread. Now I’m thinking about the @selector idea and wondering how close it is to closures. I’d like to be able to define a method on an interface… *ahem* …protocol that takes a selector as a form of callback. Though Cocoa makes extensive use of delegates I don’t want to haff to set a delegate instance variable and define another protocol for something as simple as an inlined callback operation. In other words I wanna say: “Mr. Object, do dis stuff when your data becomes available.” Back into the manuals to learn about @selectors.

XCode refactoring sux!


The title may be slightly exaggerated but when you grow used to a tool like IntelliJ Idea you start to become spoiled by everything it gives you. First off refactoring support in Idea is second to NONE! Honestly, there are no front runners. When you code in Idea you can literally feel the text of the source code under your finger tips. Now going from Idea to, say Eclipse isn’t too bad because Eclipse supports 98% of the refactorings that Idea has out of the box but it likes to torture you with dialog boxes. (I don’t like to be prompted or questioned when I know what I’m doing. If I ask to extract a method, or variable or rename something don’t give me some damned options just do what I ask!!!) Now going from Idea to something like visual C++.. phew!!! Talk about shell shock! Let’s just say it’s nice and toasty in the Idea house while somebody in the MSDN camp forgot to pay PPL. Ok now let’s move from Idea to XCode. Not as frigid as it was when we bolted butt-naked into Redmond with VS but still pretty darned cold. Why am I complaining?

Typical use case:
Dev guy writes a method. Dev guy later sees that he picked a dumb name for said method. (The method name should be a generic toggleView not a specific gotoTheVerySpecificUseCaseViewThatIThoughtOfAt10pmWhileIWasDrinkingPabstBeer) Now in Idea I use a hot-key on the method name type the new name and click run. (Nobody taps me on the shoulder to ask me if I’d like fries with that refactoring as they do in Eclipse land.) I just tried that fancy recipe in XCode and when I run I get an error. I didn’t even look at the details when GDB started to grind and slow the vent handling. I already knew from the last time I tried right-click and refactor a method name from the implementation. The refactorer doesn’t get those tough to reach spots. You know how when you ask you daughter to clean her room and she only gets the visible areas but you really wanted her to clean the back of that nasty closet? Or like when you buy one of those store brand toothbrushes with the labeling “Each! Compare to Reach toothbrush…” In other words, there were remnants left behind from the refactoring. This is completely unacceptable! As much as Apple pays attention to detail in their products I’d expect nothing less than perfection in their IDE! I don’t think this is an area where I’m misusing the tool either. refactor/rename should understand the Cocoa environment well enough to understand that I need IBOutlets referenced in nib files updated as well. I guarantee you that the folks at JetBrains would not have left this stone unturned.

What’s the difference?


It”s late and I’m stumped. Trying to figure out what’s the difference between

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    static NSString *CellIdentifier = @"Cell";
	UITableViewCell *cell = [tableView
							 dequeueReusableCellWithIdentifier:CellIdentifier];
	if (cell == nil) {
		cell = [[[UITableViewCell alloc]
				 initWithFrame:CGRectZero
				 reuseIdentifier:CellIdentifier] autorelease];
	}
	cell.text = [teams objectAtIndex:indexPath.row];
	return cell;
}

and:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
	UITableViewCell *cell = [tableView
							 dequeueReusableCellWithIdentifier:@"Cell"];
	if (cell == nil) {
		cell = [[[UITableViewCell alloc]
				 initWithFrame:CGRectZero
				 reuseIdentifier:@"Cell"] autorelease];
	}
	cell.text = [teams objectAtIndex:indexPath.row];
	return cell;
}

I’ve seen this pattern twice, once in an iPhone SDK book and once in code generated by XCode. That’s enough to make me feel like there’s a reason we use a temp variable here. (Incidentally I didn’t notice the redundancy in my refactoring until I pasted/posted the code snippet. If the motivation is merely to cut down redundancy then I’m fine with that, I just wanna make sure I’m not missing something important.)

The phone’s ringing but nobody’s home


You know what I like about Objective C? When you send a message to nil. You know what I hate about objective C? The phone ring’s but nobody’s home. Think about this a minute. Imagine if you were to dial your loved one and mistake a “4” for a “5”. The house you dial has no answering machine/service/secretary. You have no definite way of knowing that you dialed the wrong number or that nobody’s is there. All you know is that you always call at exactly 12:15pm during lunch after taking the 5th bite of your usual Chicken Cordon Bleu sandwich which you order daily from the same fast food provider (paid for by pulling what appears to be the same $10 bill creased in exactly the same corner out of your trusty tattered wallet then after reaching the door doubling back as you always do because you left your keys on the counter). You know your loved one always answers on the second ring and has no reason/desire/ability to suddenly dodge your usual 12:15, “How’s your day going? No, how’s YOUR day going sweetie?” exchange. You hit redial a few times to the same non-response and rather than accept the obvious fact you begin to panic. Something must be wrong! You verify every other point of failure except for the most obvious, calling other family/friends, contacting your building admin (y’all did pay the bill right?), even going as far as arguing with your communications provider(s) in a frantic attempt to resolve an inconsistency or ripple in your otherwise pathetically inevitable daily episode. (You were born with a slight undiagnosed touch of autism which not only hobbles you with predictability and inability to handle subtle changes in your routine but gives you a certain advantage in the field of software design which is roughly 90% about patterns.) After becoming Ralph Tresvant (featured in the clip below) and losing to the unlimited powers of the Verizon network (how come they always get over?) it dawns on you to finally attempt manually punching the memorized phone number into the keypad after which you finally get your much anticipated response, “How’s your day going?”

That’s kinda a little bit sorta like how I might have felt today after I spent 2 hours, 37 minutes and 17 seconds chasing an error where I forgot to set an instance variable in my Objective C class. A creature of habit, I like my runtime to yell at me when I access a null pointer! I love the freedom from these ambiguous errors but hate the flip side. You can go on willy nilly sending all kinds of messages to null pointers, non-objects, empty stuff and not know until three months after you’ve been workforce-reduced after slipping 5 months past a dead line while you spent 4 months and 28 days chasing a production day behavior oddity that just seemed impossible.

Bill Dudney said something like, “It’s not the learning curve that hurts it’s the unlearning curve” and I totally agree. I love/hate Objective-C and I could spit bullets at Steve Jobs for his take on Java and the arrogance of insisting his favorite programming toy should be everyone’s favorite programming toy. As much as I hated picking up yet another language/framework/way-to-do-things I forced myself on because to get hung up in any one tool-set or programming paradigm is to slowly reduce yourself to a collection of the basic carbons and minerals that make jelly-fish wiggle in the ocean. Lesson learned overall. Now I stand as I speak, “My name is Cliff, and I made a call to an empty residence.”