Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference Between __kindof and not using it in Objective-C

I have read the article about some new features of objective-C in iOS. However, I cannot get what is the main difference between these two ways:

@property (strong, nonatomic, nonnull) NSArray<UIView *> *someViews;

and

@property (strong, nonatomic, nonnull) NSArray<__kindof UIView *> *someViews;

For me, they look pretty similar. What is the difference, and when I should use one over another?

like image 494
Darius Miliauskas Avatar asked Nov 19 '15 08:11

Darius Miliauskas


3 Answers

It's all simple. In the first case you will need write:

UILabel* titleLabel = (typeof(titleLabel))someViews[0];

In the second you can just write:

UILabel* titleLabel = someViews[0];

You should choose which way you prefer: implicit or explicit type casting.

like image 24
Cy-4AH Avatar answered Nov 15 '22 17:11

Cy-4AH


To see the full effect of the __kindof I would recommend just putting it to use and taking a look at the different outcome:

NSMutableArray<UIView *> *views;
NSMutableArray<__kindof UIView *> *subviews;

views = [NSMutableArray new];
subviews = [NSMutableArray new];

UIView *someView = [UIView new];

[views addObject:someView];
[subviews addObject:someView];

UIButton *someSubview = [UIButton new];

[views addObject:someSubview];
[subviews addObject:someSubview];

So far for the insertion into the different generic arrays. Both compile and run just fine. No warnings, no crashes.

The interesting part however is reading from the arrays - keep in mind that in the first slot of both arrays is an actual UIView *, in the second slot is a UIButton *

UIView *extView00 = views[0];
UIView *extView01 = subviews[0];
UIView *extView10 = views[1];
UIView *extView11 = subviews[1];

UIButton *extButton00 = views[0]; <-- warning
UIButton *extButton01 = subviews[0];
UIButton *extButton10 = views[1]; <-- warning
UIButton *extButton11 = subviews[1];

This will run fine, but give two compiler warnings for the marked lines:

Incompatible pointer types initializing 'UIButton *' with an expression of type 'UIView *'

The other two lines work as expected. Still of course no crash. But we have some problematic situation present: extButton01 contains a UIView * but looks like a UIButton *.

Therefore adding the following

NSLog(@"%@", extButton00.titleLabel);
NSLog(@"%@", extButton01.titleLabel);
NSLog(@"%@", extButton10.titleLabel);
NSLog(@"%@", extButton11.titleLabel);

crashes as expected on the first and second line. If we remove the entire views array we will end up with warning-free but crashing code. And I do not like crashing but warning-free code. Of course warning-free does not guarantee no crashes but removing warnings for convenience sake is not a good idea IMHO.

Conclusion

Yes, that feature is neat for removing a cast. BUT it also removes the possibly helpful warning of mismatched types. If you are 100% sure that your object at index X is of type T then you could go with the subviews approach of using __kindof.

I, personally, would/will not use it yet - until I come across a really, really good use case which I fail to see yet.

like image 56
luk2302 Avatar answered Nov 15 '22 18:11

luk2302


You know how you can declare a variable like id foo to indicate that foo a pointer to any kind of object, and that you can then send any message to foo without the compiler complaining? kindof__ UIView* foo is like that -- it lets you specify that foo is some "kind of" view, but you can send any message to foo that would be valid for any kind of view without needing to cast. For example:

UIView *bar = [[UIButton alloc] initWithFrame:CGRectZero];
[bar setTarget:nil];

In this case, the compiler will complain about the second line because -setTarget: isn't a method in UIView. If you want to get that to compile, you have to cast like:

[(UIButton*)bar setTarget:nil];

__kindof lets you avoid that:

__kindof UIView *foo = [[UIButton alloc] initWithFrame:CGRectZero];
[foo setTarget:nil];

Here you're telling the compiler that foo could be any kind of view, which makes it relax a little and lets you call -setTarget: without having to cast it to UIButton*. You could just declare foo as id foo and get a similar effect, but specifying that foo is some kind of view gives the compiler more information to work with. Specifically it makes it possible to write Objective-C code that plays nicely with Swift, which is more strongly typed than Objective-C.

like image 3
Caleb Avatar answered Nov 15 '22 19:11

Caleb