Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Objective-C generics not working for methods? (Xcode 7 Beta (build: 7A120f))

So, obviously, after WWDC I'm playing with new stuff presented during last week. As you know Apple introduced generics to the world of Objective-C

Note: This answer is somehow follow-up to this question: Are there strongly-typed collections in Objective-C?

I tried this code in method, works great

NSMutableArray<NSString*> *array = [[NSMutableArray alloc] init];

[array addObject:@""];
[array addObject:@(54)];Incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString * __nonnull'
// Great, generics works as expected.

However I also have method I want to transform to generics

In header file:

- (NSArray <NSString*> *)objectsToSearch;

Implementation:

- (NSArray <NSString*> *)objectsToSearch
{
    NSString *first = @"1";

    NSString *second = @"2";

    NSString *third = @"3";

    NSNumber *test = @(55);

    return @[first, second, third, test]; // No-error!!!
}

Am I doing something wrong or Clang does not support generics + literals or there is something else I'm missing?

like image 706
lvp Avatar asked Jun 14 '15 10:06

lvp


2 Answers

I've just been diagnosing this some more and I don't think this is a bug. The following code shows a variety of options and why each will or will not compile. Note: This is based on my guesses as to how things work. It may be different to how Apple would explain it.

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-variable"
-(void) testGenericArrays {

    NSString *aString = @"abc";
    NSNumber *aNumber = @(55);

    NSArray<NSString *> *arr1  = @[aString, aNumber];
    // Compiles because the RHS is an un-typed array at compilation time.

    NSArray<NSString *> *arr2  = @[aString, @(20)];
    // Compiles because the RHS is an un-typed array at compilation time.

    NSArray<NSString *> *arr3 = [NSArray<NSString *> arrayWithObjects:aString, aNumber, @(20), nil];
    // Compiles because the type erasure for arrayWithObjects only types the first argument which is a NSString.
    // The rest of the arguments are a vaList which is not checked during header processing.

    NSArray<NSString *> *arr4 = [NSArray<NSString *> arrayWithObjects:@(20), nil]; // <- Error!
    NSArray<NSString *> *arr5 = [NSArray<NSString *> arrayWithObjects:aNumber, nil]; // <- Error!
    // Neither of these two compile because the first argument is now a NSNumber and is checked.

    NSArray<NSString *> *arr6 = [NSArray<NSString *> arrayWithObject:aNumber]; // <- Error!
    // Doesn't compile because the argument is checked during header processing.

    NSArray<NSString *> *arr7 = [NSArray arrayWithObject:aNumber];
    // Compiles because the RHS is an un-typed array at compilation time.

    NSMutableArray<NSString *> *arr8 = [[NSMutableArray alloc] init];
    [arr8 addObject:aNumber]; // <- Error!
    // Doesn't compile because the LHS is a typed array and we are adding to it.
}
#pragma clang diagnostic pop

Hope this clarifies things for people. Feel free to cut and paste into a unit test and try it out for yourself.

like image 68
drekka Avatar answered Nov 16 '22 02:11

drekka


Don't assume Apple is adding generics to Obj-C because they want to improve Obj-C. The real reason is that all iOS/OS X frameworks written in Obj-C are very difficult to use in Swift - you have to cast everything from AnyObject.

Adding generics to Obj-C enables Apple to tag the methods correctly, e.g.

@property(nonatomic, readonly, copy) NSArray <__kindof UIView *> *subviews

The important thing here is that now Swift can work with the frameworks much better. Implementing warnings/errors for misuse of generics in Obj-C is not that important so we can expect a lot of bugs there.

I advice you to report a bug but don't expect it to be fixed soon.

like image 38
Sulthan Avatar answered Nov 16 '22 03:11

Sulthan