Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it allowed to 'change'/abuse/override the signature of a block in Objective C as follows?

Is this allowed and why?

void (^bar)(NSNumber *) = ^(NSNumber *number) {
    NSLog(@"Value is %@, class is %@.", number, [number class]);
};
bar([NSNumber numberWithInt:10]);

void (^foo)(id) = bar;
foo([NSDate date]);

Output is:

Value is 10, class is __NSCFNumber.
Value is 2012-05-17 18:54:14 +0000, class is __NSDate.

I couldn't find anything related that explains this. Can you provide a link to the objective c blocks specification that covers this?

Currently I'm working on a block based UITableView subclass and it would make things a lot easier, if I'm safe to use this.

like image 971
Klaas Avatar asked May 17 '12 19:05

Klaas


1 Answers

The underlying reasons this works is a combination of:

[1] Objective-C (and C) is not strongly typed at compile time. While warnings may be produced by the compiler they can usually be silenced by (sometimes type unsafe) casts. Your assignment in this case is invalid as you are assigning a block reference which declares it requires an argument value compatible with NSNumber * to another block reference which only declares it requires an argument value compatible with id. This is type unsafe and will produce runtime errors sometimes, see below.

[2] Objective-C runtime message passing is dynamic, that is the target code for a message is determined as the code runs. This means as all your uses of number in the block are non-specific to NSNumber when you pass an NSDate at runtime suitable methods are still located dynamically. However change your bar to:

void (^bar)(NSNumber *) = ^(NSNumber *number)
{
   NSLog(@"Value is %@, class is %@, int value is %d.", number, [number class], [number intValue]);
};

and you will see runtime errors.

[3] Both [NSNumber numberWithInt:10] and [NSDate date] are declared to return values of type id, not NSNumber * & NSDate * as you might expect. This means you don't need foo, you can just type:

bar([NSDate date]);

and get the same result without any warnings... As a further example consider this:

NSNumber *num = [NSNumber numberWithInt:3];
NSDate *date = num;         // produces a warning
id erase = num;             // erase type info and do...
date = erase;               // effectively the same assignment, no warning

Take way: Objective-C is not a type safe language, the compiler will in many cases warn you about potential problems, but it will not do so in all cases.

like image 80
CRD Avatar answered Nov 15 '22 09:11

CRD