Objective-C generics not working for methods? (Xco

2019-02-21 21:03发布

问题:

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?

回答1:

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.



回答2:

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.



回答3:

I'd agree that this is a bug. I tested a simpler version of your case and it still fails.

NSString *first = @"1";
NSString *second = @"2";
NSString *third = @"3";
NSNumber *test = @(55);

NSArray <NSString*>* arr  = @[first, second, third, test, @(20)];

It doesn't seem to be an issue with the array literal syntax either. This line still produces no error.

NSArray <NSString*>* anotherArray = [NSArray arrayWithObjects:first, second, third, test, @(20), nil];


回答4:

I just came across this post and I'm not getting the same results. I've cut and pasted the code into XCode to confirm and all the examples above produce errors (I have warnings set to errors). So I'm not seeing the bug. Either that or there is a compiler setting somewhere that's different between us.