Taking Notes

cause i'll forget

Radio Dysentery

Everybody has a podcast these days, but way way back, in the before times, me and some of my friends got together and recorded a show that was ostensibly about sharing odd ball music we liked.

It was called Radio Dysentery.

Mostly it was just weirdos being weird.

My favorite episode is our fake This American Life about food poisoning:

13 – This American Dysentery

Here are the first few episodes (which are more representative of what the show was usually like):

1 – Man Grappling

2 – The Revenge

3 – Return to the Fold

4 – Jesus Overslept

Also, here’s some Perfect Strangers 9/11 fan fic

What I Get Paid

I think pay equity is a topic worth talking about and today Shanley published a great piece on it:

How Much Do You Get Paid?

I’m going to try to take a first step towards participating in this conversation and publish a short work history with salaries and how I negotiated for them and other random thoughts.

This subject is really interesting to me and while I’m not sure when, it’s something I’m hoping to write more about than just a list of numbers.

I’m hopeful that my friends in this community will try to find their own way to participate. I think we can make progress just by being willing to talk about it.

Update: Worthwhile post from Red Light Politics that talks about the problems with sharing salaries as an approach to dealing with the pay gap How to rebrand feminism and get women fired in the process

In the Muck

I think anyone who has done a decent amount of contracting has had a few (or a bunch of) toxic clients.

By toxic I mean a client who is behaviorally or institutionally self-destructive in a way that makes doing what they hired you to do nearly impossible.

For the most part learning how to handle these situations is about identifying clients that are dysfunctional and toxic before you engage and simply passing on the gig and then doing a good job communicating with the client after you’re in the door.

If you’re keeping your eyes open for it and you’re good with the human side of client services, toxic clients are unusual.

But, shit happens, you miss the signs ahead of time, you get gigantic check blindness*, and you’re in it now. You’re a month from shipping, there’s six months of work to do, the design is changing again, there are 4 managers on the client side all fighting each other over what this should be, some of them will be fired soon, but who knows which ones and you have too keep them all happy until that happens. You’re made mostly out of tears at this point and you’re not sure what to do.

*clients with ridiculously deep pockets often become toxic thanks to their willingness to pay people to just shut up and do what they’re told

Making Things That Make People’s Lives Better

Freshly back from WWDC I have a lot of thoughts rattling around about my profession and what the point is. Here’s one.

Apple debuted something of a mission statement in the form of a video in this years keynote.

Designed by Apple in California

I think the most important line in that piece for me was “Until every idea we touch enhances each life it touches.” On it’s face, it’s all kinds of saccharine, but really, it’s the right idea. It’s the right goal.

It didn’t make the keynote, but this point is made even more effectively in a video released after the keynote

Making a difference. One app at a time.

and with a very similar video that did make last years keynote

WWDC 2012 (Starts at about 7:30)

Not every app needs to make you spring a leak, but we should all be striving to solve real problems. Solving real problems is about empathy. It’s about understanding what people who aren’t you struggle with. That understanding won’t just make you a better designer or engineer, it’ll make you better at life.

I’m not saying don’t ever build frivolous or selfish things.

People includes you.

Build things that make you happy and when you can, help other people too.

Vesper: An Accessibility Audit

Vesper on iTunes

When the guys over at Q Branch first showed me Vesper, they already knew what my first question would be:

How's your VoiceOver support?

At the time it wasn’t great, but we’ve made some real strides. (Some of what I’ll be covering didn’t make the cut for 1.0, but will show up in the first bug fix release shortly.) I’m going to describe my process for auditing the app, providing feedback and how we implemented the basic accessibility features of the app.

Xcode Symbol Navigator and Callers

Xcode has a whole lot of UI and it’s not exactly all easy to discover. Here’s a cool trick you can do with the symbol navigator to find out all the places in your code that you’re calling a given method on a given class.

Iterate

The human side of this is profound

Appeal to the fact that they’re probably a decent human being

Say

I want to use your software

I want to be a fan

I want to be a person who gives you money and buys your software and follows what you do

There’s not a lot of opportunities in most people’s lives to do a trivial thing and make a bunch of people’s lives immensely better

There’s not many things like that and accessibility is one of those things

- Me rambling on the latest Iterate

Protocols

A fairly normal description of my side of most conversations with Rob Rix:

(1:00PM) That’s a terrible idea

(1:05PM) Ok, it’s not that terrible, but why would you want to do that?

(1:10PM) Ok, it’s ridiculous, but I’m intrigued

(1:15PM) Ok, I’ll be back in a few, I’m gonna build this

Today the discussion was the idea that you could implement a class cluster that only declares a protocol as it’s public interface.

Something like:

@protocol ClassClusterPublicInterface <NSObject>
@required
- (id)initWithTypeA:(id)a;
- (id)initWithTypeB:(id)b;
- (void)doSomething;
@end

and then if you could figure out a way to get instances out of that protocol declaration, you’d be in business. Turns out @protocol is implemented as the the object Protocol, defined in <objc/protocol.h> and since it’s an object, you can add to it via categories:

@interface Protocol (Alloc)
- (void)registerClass:(Class)class forInitializer:(SEL)initializer;
- (id)alloc;
@end

and now you can instantiate and use an object like this:

id clusterA = [[@protocol(ClassClusterPublicInterface) alloc] initWithTypeA:nil];
[clusterA doSomething];

2013-01-09 14:09:42.270 Protocol[3433:303] Do A

You can check out the details of the implementation on github but the core of it is storing the class to use for a given protocol, initializer combo in load:

+ (void)load
{
    if (self == [ClassCluster class])
    {
        [@protocol(ClassClusterPublicInterface) registerClass:[ConcreteSubclassA class] forInitializer:@selector(initWithTypeA:)];
        [@protocol(ClassClusterPublicInterface) registerClass:[ConcreteSubclassB class] forInitializer:@selector(initWithTypeB:)];
    }
}

and then using –forwardingTargetForSelector: to return an alloced instance of that class when that initializer is called on Protocol.

This doesn’t have much in the way of practical relevance, but it’s good fun and worth giving a look.

Coalescing

One of my favorite staples of Cocoa is –performSelector:withObject:afterDelay:. It’s a dead simple ‘do this later’ implementation and endlessly useful for scheduling simple tasks.

One of the things I use it for the most is to coalesce multiple calls into one call. A simple example would be reloading a table view everytime new data becomes available. If you’re getting new data 100 times a second, you don’t really want to reload the table every time.

Here’s a contrived example with a category on NSObject to formalize coalescing:

@interface NSObject (PerformCalescing)
- (void)rstl_coalescedPerformSelector:(SEL)sel;
@end
@implementation NSObject (Perform)
- (void)rstl_coalescedPerformSelector:(SEL)sel
{
    // Cancel any previous perform requests to keep calls from piling up.
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:sel object:nil];
    // Schedule the call, it will hit during the next turn of the current run loop
    [self performSelector:sel withObject:nil afterDelay:0.0];
}
@end
@interface Foo : NSObject
@end

@implementation Foo
- (void)foo
{
    NSLog(@"Foo");
}
@end

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        Foo *foo = [Foo new];
        [foo rstl_coalescedPerformSelector:@selector(foo)];
        [foo rstl_coalescedPerformSelector:@selector(foo)];
        [foo rstl_coalescedPerformSelector:@selector(foo)];
        CFRunLoopRun();
    }
    return 0;
}

If you ran this example, you’d see it only prints Foo once:

2013-01-03 19:37:50.560 Test[6405:303] Foo

A less contrived example might be something more like this:

@interface Foo : UIView
- (void)setNeedsRefresh;
- (void)refresh;
@property (nonatomic) bool refreshRelatedProperty;
@property (nonatomic) bool otherRefreshRelatedProperty;
@end

@implementation Foo
- (void)setRefreshRelatedProperty:(bool)refreshRelatedProperty
{
    if (refreshRelatedProperty != _refreshRelatedProperty)
    {
        _refreshRelatedProperty = refreshRelatedProperty;
        [self setNeedsRefresh];
    }
}
- (void)setOtherRefreshRelatedProperty:(bool)otherRefreshRelatedProperty
{
    if (otherRefreshRelatedProperty != _otherRefreshRelatedProperty)
    {
        _otherRefreshRelatedProperty = otherRefreshRelatedProperty;
        [self setNeedsRefresh];
    }
}
- (void)setNeedsRefresh
{
    [self rstl_coalescedPerformSelector:@selector(refresh)];
}
- (void)refresh
{
    NSLog(@"Refresh");
}
@end

...
foo.refreshRelatedProperty = true;
foo.otherRefreshRelatedProperty = true;
...

In this case you could assume that Foo is a user interface object of some sort and that a real implementation of refresh would do something expensive like draw or update a layer. I couldn’t speak to the actual implementation, but this is the basic design of -setNeedsDisplay and -setNeedsLayout on UIView.

This API is run loop based, so you won’t be able to use it on detached threads unless you manually run that threads run loop. In practice, it’s not an issue that comes up a lot.


At this point, some of you may have noticed the object argument on cancel and perform that is left out of the category above.

Something like:

@interface NSObject (PerformCalescing)
- (void)rstl_coalescedPerformSelector:(SEL)sel withObject:(id)obj;
@end
@implementation NSObject (Perform)
- (void)rstl_coalescedPerformSelector:(SEL)sel withObject:(id)obj
{
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:sel object:obj];
    [self performSelector:sel withObject:obj afterDelay:0.0];
}
@end

The answer to why that is the case is in the docs for +cancelPreviousPerformRequestsWithTarget:selector:object:

The argument for requests previously registered with the performSelector:withObject:afterDelay: instance method. Argument equality is determined using isEqual:, so the value need not be the same object that was passed originally. Pass nil to match a request for nil that was originally passed as the argument.

The requirement that the objects passed match based on -isEqal: doesn’t lend itself well to coalescing. You aren’t likely to call the same reload method with the same object again and again. (If you are doing this, then you likely have made some questionable design choices.)


Couple of things you could play around with:

PSA: Property Shenanigans

What type of storage does this property use?

@property id thing;

Trick question.

It depends.

Pre-ARC and in GC it’s an implicit assign, post-ARC, but before Xcode 4.3 it’s not a valid declaration because explicit storage is required.

In Xcode 4.3+ with ARC it’s an implicit strong.

Now you might say to yourself:

“Well that’s a little convoluted, but I guess ultimately it makes sense. Strong is what you want most of the time.” – You (Maybe)

And you’d be right.

And if you used the ARC conversion tool, no big deal because it will do the right thing.

But, if you don’t use the ARC conversion tool, well too bad for you because you’ll get no warnings about this change.

AND… it’s completely undocumented.

I’m all for improvements, but maybe, just maybe, write it down somewhere.

I’ve filed a bug and as always appreciate dupes: Radar OpenRadar