Implementing booleans in Objective C as objects

Contenido del Bootcamp Dirigido por: | Última modificación: 10 de noviembre de 2022 | Tiempo de Lectura: 3 minutos

Algunos de nuestros reconocimientos:

Premios KeepCoding

¿Qué encontrarás en este post?

octocat_booleansBooleans

Just like GitHub’s Octocat, Objective C is the result of mixing very unlikely bedfellows: Smalltalk and C.

Smalltalk is a pure Object Oriented language, designed and loved by «software artists«. On the other hand, C is the «software artisan» language «par excellence»: performance and practicality is paramount.

The end result is a compromise that occupies a «local maximum» in productivity, but the oddity of its origins shows sometimes.

Objectively True… and False

An oddity and minor annoyance of Objective C is that even though it inherited most of the object oriented features of Smalltalk, it retained C’s primitive booleans.

Unlike many other languages, Smalltalk lacks control structures. Instead, they are implemented by the class library. A conditional expression, for example, is implemented by sending a message to an instance of the Boolean class. true and false values are actually singleton instances of the True and False classes respectively.

Both understand messages such as ifTrue:, ifFalse:,  ifTrue:ifFalse: and others. All these messages expect a block as a parameter. True’s implementation will be a NOP for the ifFalse: method and will execute the block passed in ifTrue:. False and other messages work similarly.

In Objective C we must deal with a primitive Boolean (YES and NO) which are typedefs to int. This becomes a nuisance when sending messages such as performSelector: with a selector that returns a BOOL. Unfortunately performSelector: won’t provide you the return value if it’s a non-object. You may workaround this with NSInvocation, but this is usually overkill.

Ideally booleans (a very common return type) should be easier to mix with the object oriented scaffolding of Objective C. An interesting possibility would be to mimic Smalltalk’s object oriented booleans:

[crayon lang="objective c" title="Interface of the FRRBoolean class"]
//
// FRRBoolean.h
// ObjectiveBooleans
//
// Created by Fernando Rodríguez Romero on 11/9/11.
// Copyright (c) 2011 AGBO Business Architecture, SL. All rights reserved.
//
// Implements Booleans as objects (similar to Smalltalk) removing the need
// for control structures in Objective C.
//
// FRRBoolean is an abstract class. FRRYes and FRRNo do the actual work.

#import

@class FRRNo;
@class FRRYes;

@interface FRRBoolean : NSObject

// Class methods that define Yes & No singletons
+(FRRBoolean*) yes;
+(FRRBoolean *) no;

// Boolean methods
-(void) ifYes: (void (^) (void)) block;
-(void) ifNo: (void (^) (void)) block;
-(void) ifYes:(void (^)(void))YesBlock ifNo: (void (^) (void)) NoBlock;
-(void) ifNo:(void (^)(void))NoBlock ifYes: (void (^) (void)) YesBlock;

// Boolean "operators" are now methods
-(FRRBoolean *) not;
-(FRRBoolean *) and: (FRRBoolean *) alternative;
-(FRRBoolean *) or: (FRRBoolean *) alternative;
-(FRRBoolean *) xor: (FRRBoolean *) alternative;

@end
[/crayon]

These methods would be implemented as follows:
[crayon lang="objective c" title="Implementation of the FRRYes class"]
//
// FRRYes.m
// ObjectiveBooleans
//
// Created by Fernando Rodríguez Romero on 11/9/11.
// Copyright (c) 2011 AGBO Business Architecture, SL. All rights reserved.
//

#import "FRRYes.h"

@implementation FRRYes

// Boolean methods
-(void) ifYes: (void (^) (void)) block{
// execute the block
block();
}

-(void) ifNo: (void (^) (void)) block{
// NOP
}

-(void) ifYes:(void (^)(void))yesBlock
ifNo: (void (^) (void)) noBlock{

// Execute yesBlock and ignore the alternative
yesBlock();

}
-(void) ifNo:(void (^)(void))noBlock
ifYes: (void (^) (void)) yesBlock{
[self ifYes:yesBlock ifNo:noBlock];

}

#pragma mark - Boolean Operators
-(FRRBoolean *) not{
//Negation--answer no since the receiver is yes
return [FRRBoolean no];
}
-(FRRBoolean *) and: (FRRBoolean *) alternative{
// return the alternative since the receiver is yes
return alternative;
}
-(FRRBoolean *) or: (FRRBoolean *) alternative{
//answer yes since the receiver is yes
return [FRRBoolean yes];
}
-(FRRBoolean *) xor: (FRRBoolean *) alternative{
// Since the condition is yes, return the negation of the alternative
return [alternative not];
}

@end
[/crayon]

Usage

The usage of these booleans would be more «natural language» like. For instance, instead of saying:

If the store is open, go and buy some bread. If not, come back home.

we would say

Go to the store. If open, buy some bread, otherwise come back home.

[crayon lang="Objective C" title="Usage"]
int x = 2, y = 4;
NSNumber *rx = [NSNumber numberWithInt:x];
NSNumber *ry = [NSNumber numberWithInt:y];

// standard way
if( x < y ){
NSLog(@"%d is less than %d", x, y);
}

// FRRBoolean way
[[rx lessThan:ry] ifYes:^{
NSLog(@"%@ is less than %@", rx, ry);
}];
[/crayon]
This would allow us easily add booleans in collections (without having to box them in NSNumbers) and get the return value when sending performSelector:.

Still, all boolean operators (==, !, ) in Objective C return ints. Therefore, to integrate this into Cocoa it would be necessary to add categories to NSValue (and others) with messages that substitute !,  as well as all other messages that return BOOL.

This is obviously overkill, but it still makes the Objective Booleans and interesting thought experiment. You may find the source code at GitHub.

Fernando Rodríguez

iOS Developer & Co-Fundador de KeepCoding

Posts más leídos