how do i allocate an NSMutableArray with a protocol? - ios

This is a newbie question. I have an object that looks like the following (e.g. MyObject.h).
#import <Foundation/Foundation.h>
#import "JSONModel.h"
#protocol MyObject
#end
#interface MyObject : JSONModel
#property (strong,nonatomic) NSString* name;
#end
And the implementation looks like the following (e.g. MyObject.m).
#import "MyObject.h"
#implementation MyObject
#end
In my code elsewhere, I then define a NSMutableArray as follows.
NSMutableArray<MyObject>* list;
list = [[NSMutableArray alloc] init];
And I get this warning.
Incompatible pointer types assigning 'NSMutableArray<MyObject> ' from 'NSMutableArray'
I know from generics aren't supported out-of-the-box by Objective-C for collections (and that is not what I am trying to do here either), but my NSMutableArray is defined with such a protocol MyObject because I am trying to follow the examples from "JSONModel's" GitHub page.
The code still compiles, but with the warning above. How do I
make the warning go away, or
allocate/initialize NSMutableArray correctly?
Update:
I continued to search how to get rid of that warning, and it turns out if I create a NSMutableArray like the following, the warning goes away (and project compiles without errors). I will write a unit test and see if it works as expected.
list = (id)[NSMutableArray new];

NSMutableArray<MyObject>*
This does not mean what you think this means. This is not "an NSMutableArray of things that conform to <MyObject>." This is "a subclass of NSMutableArray that itself conforms to <MyObject>."
I have no idea what the link you provided is trying to achieve. Maybe they subclassed NSArray, or perhaps they've decorated NSArray with a category that tries to conform to the protocol. In either case, that's between a little dangerous and insane. I would talk to them about what they had in mind. My suspicion is that they're doing this as a decoration that appears to mean something and in fact means nothing. (That's not unheard of. Apple does it themselves with CFPropertyListRef, which appears to mean something but is in fact const void*, which can be quite surprising when you expect a warning that never comes.)
To the question of how to get rid of the warning, just get rid of the incorrect protocol decoration. It should be NSMutableArray*, not NSMutableArray<something>*.
BTW, this code gives you between zero and negligible benefit:
NSMutableArray<MyObject>* list = (id)[NSMutableArray new];
It gives you a tiny benefit in that a later call to list = someOtherKindOfList would give a warning, but that's an incredibly unusual operation (and 90% of the time you'll have to cast it anyway, so the warning will almost never catch real errors). Most notably, however, it will not throw a warning if you do [list addObject:someRandomType], which is the most common way to make a mistake here. So there's no point to the decoration.

Related

Is there a compiler flag so that the linker warns me about this? .h file with no .m file

Quick question. I've got a project with a class with no implementation file.
Then in the AppDelegate I've got:
#import "AppDelegate.h"
#import "SomeClass.h"
#interface AppDelegate ()
#property (nonatomic, strong) SomeClass *myProperty;
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self.myProperty hello];
// self.myProperty = [[SomeClass alloc] init]; // uncomment and fails as expected.
return YES;
}
Shouldn't somebody tell me that there is no implementation file ? Some sort of warning or anything?
If I do an alloc] init] it'll not compile as expected.
That code actually compiles.
Here is the project in github.
https://github.com/nmiyasato/noImplementation
Thanks
No. This isn't detectable at compile time or at link time in Objective-C.
First, the compiler knows exactly nothing about "header files" or "implementation files." (This is changing a little with the new module system, but that's not what we're discussing here.)
#import is not handled by the compiler. It's handled by the pre-processor. It takes the file SomeClass.h and splats it into AppDelegate.m as text before the compiler even sees line one. So all the compiler has to work with is this one giant file with all the text of all the headers plus this implementation (while there is "whole module optimization" now, that's a link step, not a compile step). It doesn't have any access to the rest of the project.
So the compiler has no way to know that you haven't provided an implementation. And in ObjC, even if the compiler looked at all the code, it couldn't actually know that there's no implementation anywhere because you can add implementations at runtime. In fact, it's pretty common to do this. It's how all of Core Data works. The implementations also may be linked in via a shared framework (which is very common), and may even be linked in at runtime on OS X. Or the implementations may be in a static library, so lacking a .m is still unhelpful.
It's even possible that the result of self.myProperty is random "other thing" that is only pretending to be SomeClass. Yeah, I know that sounds crazy. Welcome to class clusters with Core Foundation bridging. That's a thing. So there might not even be an implementation in the way you're thinking. Objective-C is a pretty insanely dynamic language.
As an example, the following is legal ObjC (it even works):
#interface NSString (Hello)
- (void)hello;
#end
#implementation NSString (Hello)
- (void)hello {
NSLog(#"I'm string's Hello!");
}
#end
...
self.myProperty = (SomeClass *)#"";
[self.myProperty hello];
You'd think maybe the linker could figure it out, but by the time we get to the linker, all object types are id and all methods are just selectors and method signatures. Most of the type information is gone.
So why does this fail to link if you call [[SomeClass alloc] init]? First note that it does compile, it just doesn't link. The reason is that [self.myProperty hello] is a message to an object. The linker doesn't know or care about the type of the object. It just needs a pointer to the instance. But [SomeClass alloc] is a message to a class. In order to link it, the linker has to have a pointer to the class. You'll find that any message to a class that isn't implemented will create a linker error (try [SomeClass initialize]).
In your code nothing happens at runtime because self.myProperty is nil, so there's no error. That would be the same even if you had an implementation. In the vast majority of cases the lack of an implementation file is going to be caught during link because somewhere in your system you probably call +alloc. So in practice, this shouldn't come up often at all, and this rare case is extremely difficult to detect without breaking a lot of legitimate ObjC.

How the getter/setter implement when add a #property in category with #dynamic

I add a property to my category and use #dynamic in my category implementation.
It seems work well.
#interface aClass (Properties)
#property (nonatomic, readonly) NSString *p;
#end
#implementation aClass (Properties)
#dynamic p;
#end
I know #dynamic means tell The compiler not to automatically synthesize the getter and setter methods.but why above code could work without my getter/setter implementation?
is it provides by runtime?
This would compile (and link, provided you had an actual #interface and #implementation and not just a category). But it would crash at runtime if you tried to use the getter.
When you passed #dynamic, you promised that the getter would be available at runtime (not the setter; this is readonly). If you try to use the getter at runtime, you will discover that your promise was not fulfilled and you'll crash with an "unrecognized selector" message. #dynamic says "trust me." You then have to be trustworthy.
(Note that classes should always start with an uppercase letter.)
Ah, you mentioned it was CALayer. That changes everything. CALayer is magic. Well, not magic, but definitely special. CALayer catches unhandled selectors and turns them into dynamic properties (storing the values in a dictionary I think), just like you're seeing. It was a very clever idea, and back in 10.5, some of us thought maybe this would be the "new way." (It's a natural extension of how Core Data works.) I think Apple figured out that it's too clever because they haven't spread it to other classes.
I actually talked with some of the Apple devs about it a few years ago at WWDC. I wanted to know if it was safe to rely on. The folks I talked to didn't realize it was in there and didn't recommend it.

Properties and ivars in MasterDetail template

After reading about properties and ivars in Objective C I'm still confused with MasterDetail template for iOS in XCode.
MasterViewController declares property for DetailViewController:
#class DetailViewController;
#interface MasterViewController : UITableViewController
#property (strong, nonatomic) DetailViewController *detailViewController;
#end
And ivar for array of objects:
#interface MasterViewController () {
NSMutableArray *_objects;
}
#end
Why is it that way? I just can't get why those two things are declared differently.
Thanks.
Declaring something as a "property" allows other objects to access and work with it. In The case above, adding "detailViewController" as a property to MasterViewController means other objects can access and work with the methods & properties DetailViewController exposes.
While the "_objects" variable is internal (or private) to the MasterViewController.
Apple's documentation is generally excellent. Apple's templates are... sometimes a little challenged. They are also sometimes slow to be updated as the language improves (or they are updated erratically). The objects array should really be a private property rather than an implementation-declared ivar. In any case, don't read too much into this.
Remember, the view controller shouldn't even be holding the data; it should be getting it from the model classes (which the template doesn't provide). Some of this is in order to keep the templates simpler to use (they're not actually example code; they're templates). Some of the weird code is due to limitations in the templating engine. (They didn't used to be able to prefix your classnames, even though they told you that you must prefix your classnames; it was very annoying.)
Unfortunately, seeing something in example code also doesn't necessarily mean it's a correct way to code. Much of Apple's example code would be completely inappropriate in production code (most of their examples lack proper model classes, or fail to handle errors correctly). But again, that's kind of the nature of example code. Focus on the coding guidelines. They're much more useful than learning from templates and examples.

"Proper" way to define a class - Properties vs Methods

This is an observation and a question:
I am loading some json data into a class (json already converted into an NSDictionary). The values will be read-only from the outside:
#interface Checklist
-(id)initWithJSON:(NSDictionary *)json;
-(NSInteger)checklist_id;
-(NSString *)checklist_name;
etc....
#end
With the corresponding method bodies in the .m file.
As a test, I created a class for another data element:
#interface ChecklistItem
-(id)initWithJSON:(NSDictionary *)json;
#property (readonly) NSInteger item_id;
#property (readonly) NSString *item_name;
#end
Functionally, the two classes have similar methods in the #implementation. In both cases they basically pull the appropriate value from the json and return the result. And as far as the rest of the program was concerned, the two approaches seem to be interchangeable.
So my question is:
Which approach is the best one to use?
I find either way equally readable and so far I can not find any code-reason to prefer one way over the other. I can kind of see the second option as nice since it kind-of documents the json.
You should use properties, they come in handy once you use KVO.
Also you can define public readonly properties and overwrite them in a class extension with a readwrite property that is only usable in the same class. If you try to achieve something similar you will have to deal with private helper methods — the code gets ugly.
-(NSInteger)checklist_id;
-(NSString *)checklist_name;
This isn't standard Objective-C naming. If you want to do things properly, follow the platform conventions. Apple document this in their coding guidelines documentation.
Which approach is the best one to use?
They are equivalent as far as Objective-C is concerned. The property syntax expresses your intent at a higher level than manually creating the methods, so I would prefer that approach. It's also less code.
This is less important now that ARC will clean up memory which would have been managed
inside the setter but this is still very much best practice. The performance overhead of
calling a setter method is also negligible compared to the safety gained from always
going through the setter.
this is a subjective question and you'll get nothing but opinions back, but here is mine:
the read only properties will just write the getters for you. if you don't write a private read write propertly in your .m file or wherever and just set the ivar's directly you don't even get the will/did change value for key calls and will have to call those yourself also.
#interface ChecklistItem ()
#property (readwrite) NSInteger item_id;
#property (readwrite) NSString *item_name;
#end
To access them KVO complient inside the object you'll have to do:
self.item_id = 13;
And not:
_item_id = 13;
Of course you could just have getter methods:
-(NSInteger)checklist_id;
-(NSString *)checklist_name;
And just wrap all changes in in your KVO methods:
[self willChangeValueForKey:#"checklist_id"];
_item_id = 13;
[self didChangeValueForKey:#"checklist_id"];
it's just a coding style choice, and sometimes leveraging what the compiler will write for you. but either option works the same.
If the values are read only, I'd think you'd want them as methods rather than as read-only properties to avoid any confusion that the values might be able to be set. Unless of course you want the subscribers to be able to use the dot notation for accessing the properties, but if you're just returning the values in the NSDictionary, the method form would be better as you're not keeping around another copy of the data.

List of known Xcode bugs / gotchas

Is there such a list?
I'm relatively new to iOS development and I'm think it would be great to study a list of most well-known compiler bugs or gotchas.
EDIT:
Today I spent too much time to understand what's going on with code like this:
in *.h
#interface I : NSObject {
..
NSSMutableArray* var; // typo, should be m_var;
}
#property (nonatomic, readonly) NSMutableArray* var;
in *.m
#implementation I
#synthesize var = m_var; // no warnings or anything
-(id) init
{
self = [super init];
if (self != nil)
{
// no warning or errors
m_var = [NSMutableArray new];
}
return self;
}
And I think it's time to learn some of the well-known Objective-C idiosyncrasies.
Apple’s list of bugs is internal to Apple.
I think that Open Radar is the closest thing you’ll get to a public list of bugs related to Apple products, including Xcode. It is maintained by the community — users are encouraged to post to Open Radar the bug reports that have been submitted to Apple.
The golden rule of debugging: it’s not compiler’s fault. Some behaviours are a bit strange, like the one you show here, but they are by design. As for the “bug” in question, the compiler can synthesize instance variables for you, without them having to be declared:
#interface Foo {}
#property(assign) float bar;
#end
#implementation Foo
#synthesize bar;
- (void) somewhere { bar = 1; }
#end
This is convenient and allows you to move private interfaces into the implementation file. Coming back to your example, you now have two instances variables, var and m_var, the second one acting as a storage for the var property. It’s not exactly something to be happy about, but it makes perfect sense. (Could there be a warning that you have an instance variable that doesn’t act as a store for a property with the same name?)
As this kind of behaviour is mostly by design, you won’t find it in the Radar and would have to look for a list of common Objective-C gotchas. (This is another interesting situation.)
Apple has its own bug tracker, but you can only see you own reports (!?)
Your best bet is then openradar... Which is limited.
EDIT: About your supposed Xcode Bug, even if that's not the question.
Remember that #synthesize is just syntactic sugar that will generate code at compilation.
My guess is that your var property is conflicting with your var member.
I would not say that's a bug, more a predictable issue that could be integrated in clang static analysis.
Anyway, that's obviously a code typo, a human error, tools are just there to help us, or we would write assembly bytecode directly :)

Resources