Can I use __kindof with protocols in Objective-C? - objective-c

I know I can make use of Objective-C's lightweight generics using the __kindof keyword, e.g.
NSArray<__kindof BaseClass*> *myArray;
This would remove any warnings for assigning any object from the array to a derived class.
However, instead of BaseClass, I have BaseProtocol, e.g. all my classes in question will conform the BaseProtocol, regardless of their base class. I want to use lightweight generics to dictate "my array consists of elements conforming to BaseProtocol, but they can be any class".
For example in C#, I can say: List<IMyInterface> which would mean that the list consists of elements that implement the IMyInterface interface (I know C# has strong generics and Objective-C has only lightweight generics that doesn't prevent compilation, but you get the idea).
Is there any way to achieve this functionality on Objective-C?
E.g. I want to write
NSArray<__kindof id<MyProtocol>> //compiles, but as the generic argument is "id", it accepts any object, including invalid ones
or
NSArray<id<__kindof MyProtocol>> //doesn't compile
Is this possible?
UPDATE:
Here is a complete self-contained code:
#protocol MyProtocol
#end
#interface MyClass : NSObject<MyProtocol>
#end
#implementation MyClass
#end
#interface AnotherClass : NSObject
#end
#implementation AnotherClass
#end
NSMutableArray<__kindof id<MyProtocol>> *myArray;
void test(){
MyClass *myClassInstance = [[MyClass alloc] init];
AnotherClass *anotherClassInstance = [[AnotherClass alloc] init];
myArray = #[].mutableCopy;
[myArray addObject:myClassInstance];
[myArray addObject:anotherClassInstance]; //i get warning. good.
MyClass *returnedInstance = myArray[0];
AnotherClass *anotherInstance = myArray[1]; //why don't I get a warning here?
}

This syntax is correct:
NSArray <__kindof id <MyProtocol>> *array = ...
You can also omit __kindof, too, and still enjoy lightweight generics. It will still warn you about adding objects of the wrong type even without that keyword. The __kindof is used if you want to pull an object out of that array and assign it to a subtype without a cast, but otherwise __kindof is not needed:
NSArray <id <MyProtocol>> *array = ...
Both of these patterns will warn you if you add an object of a specific type to the array, but that type doesn't conform to MyProtocol.
What this won't warn you about is if you try to add an object of type id by itself. So avoid using unqualified id types within your code and you'll enjoy lightweight generics.
If you're still not seeing the warnings, make sure you have -Wobjc-literal-conversion warning turned on. So, go back to your build settings for the first project and search for "literal" and you'll see the setting (called "Implicit Objective-C Literal Conversions").
Consider this example:
#protocol MyProtocol
#end
#interface Foo: NSObject <MyProtocol>
#end
#interface Bar: Foo
#end
#interface Baz: NSObject
#end
Then consider:
Foo *foo = [[Foo alloc] init];
Bar *bar = [[Bar alloc] init];
Baz *baz = [[Baz alloc] init];
id qux = [[Baz alloc] init];
NSArray <id <MyProtocol>> *array1;
array1 = #[foo, bar, baz, qux]; // warning: object of type 'Baz *' is not compatible with array element type 'Foo *'
Note, that warns us about baz, but not qux. So be wary of using id types.
id <MyProtocol> object1 = array1[0]; // no warning, great
So, that's using the protocol as a lightweight generic and it works as expected.
The only reason you'd add __kindof is if you wanted to avoid this warning:
Foo *foo1 = array1[0]; // warning: initializing 'Foo *__strong' with an expression of incompatible type 'id<MyProtocol> _Nullable'
In that case, you'd use __kindof:
NSArray <__kindof id <MyProtocol>> *array2;
array2 = #[foo, bar, baz]; // again, warning: object of type 'Baz *' is not compatible with array element type 'Foo *'
id <MyProtocol> object2 = array2[0]; // no warning, great
Foo *foo2 = array2[0]; // no warning, great

Related

Why is the compiler requiring instance variable access over property access in class extension?

The compiler in Xcode requires for me to use the instance variable name for a property declared in a class extension
#interface Person ()
#property NSString *bar
#end
#implementation Person
-(void) foo {
NSString *foo = _bar; // underscore required here
}
#end
In the code example above why can I not access bar without the underscore?
You can access bar as self.bar and it's more preferred than _bar. _bar is available for legacy code.
Looks like you came to Objective-C from Swift where the compiler adds self. for you automatically.

Objective-C method return type with generics and Xcode autocomplete

I have custom collection class with generic parameterisation. I create collection of cats, add cat. When I try to get cat back Xcode is showing error: "Property 'name' not found on object of type 'id'". This is nonsense, because Cat has property name and MyCustomCollection doesn't return id but ObjectType. How do I declare method so the autocompletion could understand which type is returning by the method?
MyCustomCollection *collection = [[MyCustomCollection<Cat *> alloc] init];
[collection addCustomObject:[[Cat alloc] init]];
NSString *string = [collection customObjectAtIndex:0].name; // Property 'name' not found on object of type 'id'
MyCustomCollection.h file
#interface MyCustomCollection<ObjectType> : NSObject
-(ObjectType)customObjectAtIndex:(NSUInteger)index;
-(void)addCustomObject:(ObjectType)object;
#end
Cat.h file
#interface Cat : NSObject
#property (nonatomic, strong) NSString *name;
#end
The problem isn't with the method declaration. It is the declaration of the collection variable. You have to tell the compiler what type of objects are in the collection:
MyCustomCollection<Cat *> *collection = [[MyCustomCollection<Cat *> alloc] init];
Otherwise it won't know what sort of objects the collection variable references and assumes that they are of type id (hence the error).
You theoretically can also cast the result of customObjectAtIndex, but that seems to defeat the purpose of using generics.

Which one is initialized, property or its instance variable

Suppose I have a property called myPropertyName defined in my class MyClassName. Manual memory management is used throughout this post.
MyClassName.h
#import <UIKit/UIKit.h>
#interface MyClassName : NSObject {
#private
NSObject* myPropertyName;
#public
}
#property (nonatomic, retain) NSObject* myPropertyName;
// Some methods prototypes are here
#end
MyClassName.m
#import "MyClassName.h"
#implementation MyClassName
#synthesize myPropertyName;
// Some methods are here
#end
I'm confused with usages such as the place of myPropertyName declaration, its difference between instance variable. For example, what is the difference among these three statement of initialization code, for example, in the customized -(void)init method for my class myClassName.
self.myPropertyName = [[[NSObject alloc] init] autorelease];
This one is calling myPropertyName setter, but I'm not sure what is the name of the instance variable being used in the setter, myPropertyName (since I've declared a #private field named myPropertyName) or _myPropertyName (people say that this one with underbar is the default)?
myPropertyName = [[NSObject alloc] init];
Does this initialize the instance variable of the myPropertyName property? If I don't have #synthesize myPropertyName = _myPropertyName;, would it be wrong since the default instance variable for the property is said to be _myPropertyName.
_myPropertyName = [[NSObject alloc] init];
Is _myPropertyName still declared as the instance variable for my property myPropertyName even if I use #synthesize myPropertyName; and #private NSObject* myPropertyName;?
In my understanding, a property is just a name (such as myPropertyName), there should be some instance variable encapsulated to be used in actual operations in the code, such as assigning values.
First off, I highly recommend reading Apple's documentation on properties, also linked by nhgrif. However, I understand docs can be a bit dense reading material (though Apple's, I find, are not so bad), so I'll give a brief overview of properties here.
I like examples, so I'm going to rewrite your two classes in a bit more current form.
MyClassName.h
#import <UIKit/UIKit.h>
#interface MyClassName : NSObject
#property (nonatomic, strong) NSObject *myPropertyName;
// method prototypes here
#end
MyClassName.m
#import "MyClassName.h"
#implementation MyClassName
// some methods here
#end
The class MyClassName now has a property called myPropertyName of type NSObject *. The compiler will do a lot of work for you for "free" in this instance. Specifically, it will generate a backing variable, and also generate a setter and getter for myPropertyName. If I were to rewrite the two files, and pretend I'm the compiler, including that stuff, they would look like this:
MyClassName.h
#import <UIKit/UIKit.h>
#interface MyClassName : NSObject {
NSObject *_myPropertyName;
}
#property (nonatomic, strong) NSObject *myPropertyName;
- (void)setMyPropertyName:(NSObject *)obj;
- (NSObject *)myPropertyName;
#end
MyClassName.m
#import "MyClassName.h"
#implementation MyClassName
- (void)setMyPropertyName:(NSObject *)obj
{
_myPropertyName = obj;
}
- (NSObject *)myPropertyName
{
return _myPropertyName;
}
#end
Again, all of this is happening for "free": I'm just showing you what's happening under the hood. Now for your numbered questions.
self.myPropertyName = [[[NSObject alloc] init] autorelease];
First of all, you should probably be using Automatic Reference Counting, or ARC. If you are, you won't be allowed to call autorelease. Ignoring that part, this works fine. Excluding the autorelease, this is exactly equivalent to:
[self setMyPropertyName:[[NSObject alloc] init]];
Which, if you look at the second .m file I wrote out, above, will basically translate to:
`_myPropertyName = [[NSObject alloc] init];
myPropertyName = [[NSObject alloc] init];
As written, this code will give a compiler error, since there is no variable called myPropertyName in this class. If you really want to access the instance variable underlying (or, "backing") the myPropertyName property, you can, by using its real name:
_myPropertyName = [[NSObject alloc] init]; // note the underscore
But most of the time, it's better to use the setter, as in point 1., since that allows for side effects, and for Key-Value Coding, and other good stuff.
_myPropertyName = [[NSObject alloc] init];
Oh. Well you got it. See point 2.
You mentioned that:
I'm confused with usages such as the place of myPropertyName declaration, its difference between instance variable. For example, what is the difference among these three statement of initialization code, for example, in the customized -(void)init method for my class myClassName.
In case it hasn't been made clear, a property is something of an abstract concept; its data is stored in a normal instance variable, typically assigned by the compiler. Its access should usually be restricted to the setter and getter, with important exceptions. To keep this answer short, I won't go into more detail than that.
One more thing: as nhgrif mentioned, you don't need to use the #synthesize keyword anymore. That is implicitly understood by the compiler now.
If you're not sure about any of this, post a comment or, better yet, read the docs.
Let's take this example:
#property NSString *fullName;
If in the implementation, we override the setters and getters, and in these setters and getters, we don't use an instance variable fullName, it is never created. For example:
- (NSString *)fullName
{
return [NSString stringWithFormat:#"%# %#", self.firstName, self.lastName];
}
- (void)setFullName:(NSString *)fullName
{
//logic to split fullName into two strings
//self.firstName = etc
//self.lastName = etc.
}
In this example, there is no instance variable for fullName created.
This is according to Apple's Official Documentation
If, however, you don't override both the setter and getter, an instance variable is created.
As a sidenote, you can declare a property readonly, and then simply overriding the getter (without using the variable) will prevent an ivar being created. Likewise, you can declare a property writeonly and just override the setter.

Navigating a to-many relationship with Core Data

How do I navigate a Core Data to-many relationship in Objective-C?
I have an Event model and each instances has many EventOccurace objects that are exposed via an occurances [sic] relationship. Apple's docs say that the standard property accessor should be available but I keep getting compile-time errors:
DetailViewController.h
#interface DetailViewController : UIViewController <UISplitViewControllerDelegate>
#property (strong, nonatomic) id detailItem;
#end
DetailViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
NSSet * foo;
foo = [[self detailItem] occurances];
///No known instance method for selector 'occurances'
foo = self.detailItem.occurances;
///Property 'occurances' not found on object of type 'id'
//try casting to NSManagedObject to access
NSManagedObject * casted = (NSManagedObject *)self.detailItem;
foo = casted.occurances;
///Property 'occurances' not found on object of type 'NSManagedObject *'
foo = [casted occurances];
///No visible #interface for 'NSManagedObject' declares the selector 'occurances'
}
To use the (dynamically created) accessor methods, you have to create subclasses of NSManagedObject for your entities.
This is easily done in Xcode: Select the entities in the model editor and choose "Editor -> Create NSManagedObject subclass ..." from the menu. Include the header files (Event.h, EventOccurance.h) in your source code. Then
Event *event = self.detailItem;
NSSet *foo = event.occurances;
should work.
Alternatively, you can use Key-value coding:
NSSet *foo = [[self detailItem] valueForKey:#"occurances"];
which works even without the managed object subclasses.
Some things that could be not working:
First of all check that your ManagedObject classes have been generated.
Second, you should specify the type of the detailItem somewhere. Or in the .h, or by casting it to a type in the .m. Now its just an "id", the compiler cant know which is its type.
Third, this is one possible way of accesing it:
NSArray * occurances = [self.detailItem.occurances allObjects];
for(EventOccurance * ocu in occurances){
//blablabla
}

Can't figure out 'warning: incompatible Objective-C types'

I have a subclass of NSObject that implements an -(id)initWithRootElement:(MyElement *)e method. NSXMLDocument has an identical method that takes an NSXMLElement. When I compile, I get the following warning:
warning: incompatible Objective-C types 'struct MyElement *', expected 'struct NSXMLElement *' when passing argument 1 of 'initWithRootElement:' from distinct Objective-C type
In this case, I'm compiling with Clang + LLVM on SnowLeopard with Xcode 3.2.1, but this also happens with GCC 4.2 on both Leopard and SnowLeopard.
What I don't understand is why it's throwing a warning for my direct NSObject subclass when NSXMLDocument has to inherit from NSXMLNode first? Shouldn't it know that -(id)initWithRootElement:(NSXMLElement *)e only applies to NSXMLDocument which has nothing to do with my class? I could understand if I was trying to overload the method, but I'm not. Please tell me I'm not going crazy...
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSXMLElement.h>
// Importing this here causes the warning...
// #import <Foundation/NSXMLDocument.h>
typedef NSObject MyElement;
#interface TestClass : NSObject
{
}
- (id)initWithRootElement:(MyElement *)element;
#end
#implementation TestClass
- (id)initWithRootElement:(MyElement *)element { return nil; }
#end
// ...but here it doesn't
// #import <Foundation/NSXMLDocument.h>
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// No warning! Inheritance: NSXMLDocument -> NSXMLNode -> NSObject
NSXMLElement *xmlElement = [[NSXMLElement alloc] initWithName:#"foo"];
[[TestClass alloc] initWithRootElement:xmlElement];
// warning: incompatible Objective-C types 'struct MyElement *', expected 'struct NSXMLElement *' when passing argument 1 of 'initWithRootElement:' from distinct Objective-C type
MyElement *element = [[MyElement alloc] init];
[[TestClass alloc] initWithRootElement:element];
[pool drain];
return 0;
}
Objective-C doesn't support covariant declarations.
The declaration of initWithRootElement: in NSXMLDocument is as follows:
- (id)initWithRootElement:(NSXMLElement *)element;
This is different than your declaration:
- (id)initWithRootElement:(MyElement *)element;
In that the argument types are different. This causes confusion in this line of code (where element is of type MyElement *...
[[TestClass alloc] initWithRootElement:element];
... because the return type of +alloc is id and, thus, the compiler doesn't know which method to use; which type of argument is to be expected.
When developing code using Objective-C targeting Apple's frameworks, the rule of thumb is to never have different arguments declared for any given selector.
I'm slightly surprised that the first case doesn't also warn. It likely should. If you have a fairly minimal test case, file a bug via http://bugreport.apple.com/ and append the bug # to this question (or my answer).