When I tried compiling my app for OS 3 I encountered an the following error:
error: type of accessor does not match the type of property
The error was for a property I tried to access that is defined as follows:
NSMutableArray *myArray
#property (readonly,nonatomic) NSArray* myArray;
the property is #synthesized in the implementation file.
This worked just fine in OS 2.2.1 but doesn't is OS 3.0
Writing the getter method myself solved the problem.
Is anyone aware of changes to objective-c between OS 2.2.1 and 3.0?
Is there any documentation for these changes?
The API changes document doesn't appear to contain anything about this issue.
EDIT
the error occurs when you try to access the property e.g.
NSArray *anArray = myClass.myArray;
As I mentioned above I found a workaround for this: writing the getter method myself, however what I'm really after is some kind of documentation from apple explaining this change and any other changes that are not API related.
Thanks for your help
This is a compiler bug.
Though you didn't specify it completely, I expect your code looks like this:
#interface Foo : NSObject {
NSMutableArray *objects;
}
#property (readonly, copy) NSArray *objects;
#end
#implementation Foo
#synthesize objects;
#end
The compiler is, unfortunately, confused between the declaration of the objects property and the declaration of the objects instance variable. Remember that properties and instance variables are different things in Objective-C; a property can be backed by an instance variable, but it's really part of the public interface of a class.
You can work around this by changing your code to clearly separate the definition of the instance variable from the definition of the property, for example by prefixing the name of the instance variable:
#interface Foo : NSObject {
NSMutableArray *_objects;
}
#property (readonly, copy) NSArray *objects;
#end
#implementation Foo
#synthesize objects = _objects;
#end
This way the compiler doesn't get confused about the property versus the instance variable in expressions like self.objects (which it shouldn't anyway, but apparently does).
Just to head off the inevitable response: Apple does not reserve the underbar prefix for instance variables. It's reserved for methods. Regardless, if you dislike the underbar, feel free to use another prefix.
edit: Original answer removed after peer review found it lacking. Please read Chris Hanson's comments on the matter. I'm leaving the rest here because I think it is still valid.
Note that even if you declare the property type to be NSArray, the object returned is still an NSMutableArray, and the mutable methods are defined for it. Declaring the property in this way does not prevent someone from accidentally mutating the array.
If you want to be sure that the returned array is not mutable, you could declare the property as in your original example, and then roll your own accessor:
- (NSArray *)myArray { return [NSArray arrayWithArray:myArray]; }
Note that this would return an unretained NSArray. It would be up to the caller to take ownership of the object if it needed to persist.
You are seeing errors because XCode is now issuing warnings and errors for things it did not previously...
I would argue that it should be at most a warning to do what you are doing, I understand your attempt to present the array as immutable to the outside world but have it mutable inside the class. You may want to consider a different accessor with a different name, built to return the mutable array specifically.
It is still Objective-C 2.0; the compiler is just maybe a little updated with considering this kind of type changing an error. It pretty much should be an error. At least it should warn you that you likely don't mean what you wrote. Then you could cast stuff to make it not warn you, which you can't do with the #synthesize statement.
I just exactly pasted your code and a synthesize statement into my controller and I got no errors or warnings about it. It built fine. Now I set the base SDK to "Simulator 3.0", and the build to "Simulator 3.0 Debug". This project had started in the 2.2.1 SDK and I just installed the 3.0 SDK yesterday; Xcode is version 3.1.3.
Update: Oh I see that actually trying to set the property is where you get the error you mentioned.
self.myArray = [NSArray arrayWithObject:#"foo"];
Clearly you cannot #synthesize this behavior and must write your own accessors.
- (NSArray*)myArray {
return [NSArray arrayWithArray:myArray];
}
- (void)setMyArray:(NSArray*) pMyArray {
myArray = [NSMutableArray arrayWithArray:pMyArray];
}
Filling in these accessors, did not make the message go away, so I had to change the access to:
[self setMyArray:[NSArray arrayWithObject:#"foo"]];
Using the above syntax without custom accessors also did not work.
PS Wow, is anyone else annoyed that you can neither copy message bubbles, or the text in the build results window?
So this is really to do with the #synthesize call that is not happy about exposing a NSMutableArray as an NSArray - why not just implement the getMethod.
Actually thinking about it it must be the set method that is not happy - you wouldn't be able to set an NSArray into an NSMutableArray.
Your questions were:
Is anyone aware of changes to objective-c between OS 2.2.1 and 3.0?
Is there any documentation for these changes?
The definitive answers are:
1) There were no intentional changes to the language specification, but the compiler and other developer tools changed. Chris and his coworkers are the experts on those changes.
2) Probably not, because any changes were unintentional or made to better match behavior with the documentation.
You shouldn't be so quick to dismiss Chris' answer as "a guess." Chris works on Apple's developer tools. You might get another answer you like more, but you won't be getting a more expert answer.
Related
I have read many articles online, and I am a bit confused about ARC. I will list my questions below, please help me answer them.
1.) What does the keyword strong do (in #property (nonatomic, strong) …)?
2.) Don't we have to release or autorelease what we alloc anymore, because I get a warning saying that I need not have to. (I am not at my Mac, so I can't copy the warning I got.)
3.) I couldn't find the Leaks tool in Xcode 4.2. Where did it go? Or don't we have to check for leaks anymore?
4.) When creating the project using Xcode 4.2, I clicked on "enable ARC", so will my program work on iOS 3? (I think ARC only supports iOS 4 and above.)
strong is an ARC analog to retain (there's no retain under ARC). It means that property value will be hold by class where the #property was defined, and released upon reassignment or dealloc. (Note that under ARC there's no need to release or nullify properties in dealloc).
The point of ARC is taking care of retain/release routine by compiler, so there's no need for manual memory management methods.
You can check for leaks by running Profile (Product -> Profile menu) and selecting Leaks instrument. Leaks can be introduced with cyclic references or in CoreFoundation code (ARC doesn't work with CoreFoundation). Also if you are mixing ARC and non-ARC code, the latter could be a source of leaks.
ARC is supported by OS starting from iOS 4.0 (though iOS 5 SDK is needed). The key difference between 4.0 and 5.0 ARC support is availability of weak property specifier. Weak property (__weak variable) will be automatically nullified when the strong value it refers to is released. There are no weak properties in 4.0, so __unsafe_unretained specifier should be used (It's a close analog to assign specifier, it doesn't retain value and can point to already deallocated value, so it's dangerous).
Here's a great explanation of ARC:
Everything you need to know about automatic reference counting
Strong is equivalent to retain in ARC. It indicates ownership: What does the "strong" keyword do
No, that's done for you. ARC inserts those statements in (compiled) code automatically. That's its main job. But, per Abizern's comment, this only works for your objects and Cocoa objects. If you're using Core Foundation (CF*) classes then you must read about the __bridge family of casts. You might start reading here.
It's in Instruments. Per iHunter's answer, Cmd+I or Product->Profile, then choose Leaks. Particularly important given (2).
It will not work.
Well, there are already three answers that cover the main things so I'll focus on a single detail here, that isn't already mentioned:
3.) I couldn't find the Leaks tool in Xcode 4.2. Where did it go? Or don't we have to check for leaks anymore?
Yes we do still need to check for leaks:
ARC is not garbage collection, it is automated retain/release.
Thus, it is still possible — in fact, it's pretty easy — to create leaks!
Consider the following example:
#class Puppet;
#protocol PuppetDelegate : <NSObject>
- (void)puppetDidTwistStrings:(Puppet *)puppet;
#end
#interface Puppet : NSObject {
id <PuppetDelegate> delegate_;
}
- (id)initWithPuppetDelegate:(id <PuppetDelegate>)delegate;
- (void)unravelStrings;
#end
#implementation Puppet
- (id)initWithPuppetDelegate:delegate
{
if (!(self = [super init])) return nil;
delegate_ = delegate;
return self;
}
// assume some implementation for unravel strings
#end
#interface MasterOfPuppets : NSObject <PuppetDelegate> {
NSMutableArray *puppets_;
}
- (void)puppetDidTwistStrings:(Puppet *)puppet;
- (void)bringOutAPuppet;
#end
#implementation
- (id)init
{
if (!(self = [super init])) return nil;
puppets_ = [[NSMutableArray alloc] init];
return self;
}
- (void)bringOutAPuppet
{
Puppet *newPuppet = [[Puppet alloc] initWithPuppetDelegate:self];
[puppets_ addObject:newPuppet];
}
- (void)puppetDidTwistStrings:(Puppet *)puppet
{
BOOL isOurPuppet = [puppets_ containsObject:puppet];
if (!isOurPuppet) return;
[puppet unravelStrings];
}
#end
This example is (admittedly) a little stupid, but this code will leak, and ARC is not going to help you about it, whereas garbage collection would:
A MasterOfPuppets can have many puppets, which he stores in an instance variable — so far so good.
A Puppet has a delegate which it is going to inform if its strings are entangled — and here it starts.
Whereas in non-ARC code an instance variable was simply a pointer that was being assigned to, ARC assumes that if you stash something in a variable, you want to cling on to it and it will retain the value unless you tell it not to.
So you have a classical retain-cycle here and ARC won't save you from those.
This may seem a contrived and far-fetched example, but it's not: I've seen this quite a bit in delegate relationships.
(The solution, by the way, is pretty simple: declare Puppet's delegate_ instance variable as weak and everything works as it should.)
I am fairly far along developing a reasonably ambitious first iPhone project and I am confused as to which way to implement and access properties and why.
Example 1:
(in the .h)
Nsstring *_sale;
#property (nonatomic, retain) NSString *sale;
(in the .m)
#synthesize sale = _sale;
Example 2:
(in the .h)
#property (nonatomic, retain) NSString *sale;
(in the .m)
#synthesize sale;
Both of these seem to work to me without trouble but I am trying to figure out why there are two ways to do this and what benefits there may be to either.
Can someone tell me the difference?
Example 1 demonstrates the old way of defining ivar/property variable pairs. The new compiler now generates ivars (the NSstring *_sale; part) for you. Example 1 also demonstrates manually pairing up the property sale to the ivar _sale using the #synthesize sale = _sale; statement.
Example 2 is a more concise way to implement properties in Obj-C and is the way you will see most example code on the internet. The vast majority of the time you can write your properties without needing to overwrite the accessor/mutator methods generated for you by the compiler.
There are some die-hard proponents of the underscore prefix to denote instance variables for clarity's sake. You may find that this helps you when it comes to memory management, as in Example 1, setting self.sale equal to an autoreleased NSString would be fine since it would get retained, but setting _sale equal to an autoreleased object would result in erratic behavior later on because the NSString passed in would not be retained by the instance variable.
In general, I prefer writing my properties as you have shown in Example 2.
Short Answer: There are two ways of doing this because the new compiler now can infer some stuff for you, but the previous way of doing things has been left in for backwards compatibility.
They both work the same way. Some people prefix their instance variable with an underscore as a visual cue to differentiate member variables from instance variables.
More discussion at this SO question: How does an underscore in front of a variable in a cocoa objective-c class work?
For more Cocoa style guidelines checkout CocoaDevCentral.
I've gotten to a point where I'm starting to think this is may actually be a bug in Xcode, but to be sure, I'm asking it here. I was working on my app that uses MapKit and CoreLocation, but at some point I started getting the warning "Property 'coordinate' requires method '-coordinate'". At first I thought I was doing something wrong, as I did use the property coordinate for an MKPointAnnotation, but after I commented that out, the warning remained.
In fact, after I've commented out pretty much everything, I still get the warning. It tells me the file name and line number (the line with #end), but if I search for coordinate in that file, there aren't any results. The .h doesn't declare the property either, so I'm really lost as to where this error is coming from... I can provide you with code, of course, but I've commented so much stuff out that it doesn't really make any sense to post it here. Just a few memory management methods without any actual content other than sending a message to super...
Xcode is correct in telling you that you're required to implement -coordinate. This is a non-optional method of the MKAnnotation protocol.
http://developer.apple.com/library/ios/#documentation/MapKit/Reference/MKAnnotation_Protocol/Reference/Reference.html
coordinate The center point (specified
as a map coordinate) of the
annotation. (required) (read-only)
#property (nonatomic, readonly)
CLLocationCoordinate2D coordinate
I believe the reason you can't synthesize coordinate is not because you didn't declare the property, but because you haven't told the compiler what storage to use.
Adding
CLLocationCoordinate2D coordinate;
to the fields (variables) section of your class interface will give the compiler the storage it is looking for.
Or you can point the compiler to other storage using this syntax:
#synthesize coordinate=myCoordinateVariable;
But none of that really matters, because you don't have to use synthesize.
Just implement the method yourself! The required part is readOnly so you only need to implement the getter.
-(CLLocationCoordinate2D)coordinate {
return myCoordinate;
}
#property and #synthesize are primarily shortcuts. #synthesize just says to the compiler - "Implement these accessors if I haven't". But normally you declare a property like this, right?
#implementation MyClass : NSObject {
NSString *someString;
}
#property (nonatomic, retain) NSString *someString;
and then you synthesize it. #synthesize creates the appropriate implementations for the declarations implied by #property:
-(NSString *)someString;
-(void)setSomeString:(NSString *);
and uses the storage you provided when you declared the instance variable, NSString *someString.
Incidentally, in Xcode 4 #synthesize automatically creates storage for you if it doesn't already exist.
I've had the same problem for over a year on one of my apps. Nobody has ever been able to offer an explanation. I just got to the point of adding a mapview to my latest app and had only gotten as far as adding the MapKit Framework to the project, declaring support for the MKMapViewDelegate and MKAnnotation protocols then I did the #imports for MapKit/MapKit.h and MapKit/MKAnnotation.h. Build the app and Bang! there's the warning.
So, I commented out the MKAnnotation protocol declaration and like magic the warning went away. The only conclusion I can come to is that this is an Xcode issue.
Very strange this problem. I just added the following line as Ball suggested and my warning disappeared.
#synthesize coordinate=myCoordinateVariable;
Thanks Ball for the info.
I noticed a difference between the way I code (badly ;) ) and the code I see from others.
Can anybody explain why I see some people using
self.varname.anotherpropertie
When
varname.anotherpropertie
Seems to work just as well. I dont use self. a lot in my code. I'm wondering is this very bad or is there something that I need to learn to understand why its used so much by most people?
Thanks again,
-Code
They are different things. In a class where you have an instance variable named foo with a declared property also named foo, writing simply foo accesses the instance variable while self.foo goes through the getter method for the property (which might just return the instance variable or it might do more).
I'd suggest taking a look at the Declared Properties chapter of Apple's The Objective-C Programming Language for a full explanation of how it works and when to choose which option.
One gotcha that I've run into is the retain vs non-retain with properties.
So if you have a retained property like this:
#property (nonatomic, retain) NSString* myStr;
#synchronized myStr;
and you do something like this:
- (void) myMethod:(NSString*)inStr
{
myStr = inStr;
}
In this example you will not actually retain the string as your property is not invoked. If you change the assignment line to use the property (by using "self.") then the string would be retained (and the previous string would be released if non-nil).
self.myStr = inStr;
It takes some getting used to that properties are method calls, but once you start seeing them as such then the "self." syntax becomes much more clear. Hope that helps some.
I just tried compileing my iPhone app against OS 3.0 and I get a compile error when using fast enumeration.
I'm trying to go through an NSArray containing cComment classes:
for (cComment* newComment in comments.comments)
And I get this error
error: type of accessor does not match the type of property 'comments'.
This works flawlessly when compiled with OS 2.2.1.
I understand the error, the enumaretion isn't strongly typed but since as far as I know generics/templates are not supported in objective-c. So currently I can only see one way around this:
for (id commentObject in comments.comments)
{
cComment *newComment = (cComment *)commentObject;
}
Can anyone think of another way? Why has this changed? Any points to apple documentation about this change would be appreciated.
EDIT
Following Grouchal suggestion i tried this:
NSArray* allComments = comments.comments
and I got the same error so it seems its not about the enumeration after all
here's the code form the header file:
NSMutableArray *comments;
#property (readonly,nonatomic) NSArray* comments;
and the property is synthesized in the implementation file.
Changing the property to NSMutableArray seems to solve the problem but I don't see any reason why this should be the case. Any ideas.
Thanks.
The problem is in the property definition. You should change have it as:
#property (readonly,nonatomic) NSMutableArray* comments
On the downside, your array will be exposed as mutable (though I suppose that's what you wanted to prevent).
I don't see any reason why this code shouldn't work in OS 3.0 I have similar code working already.
What happens if you change the code to
NSArray *commentArray = comments.comments;
for (cComment *newComment in commentArray){
.....
}
I've just noticed as I've written this where the * is in your code - is this the problem? should it really be cComment* in the for declaration?
I'm going to close this question and ask a new one under objective-c changes for iPhone OS 3.0.
I implemented the getter method myself to return an NSArray and it works just fine. Also changing the property to NSMutableArray works but it really isn't what I want to do.
I have no idea why this changed. This is an objective-c change and not an OS change. Also there is no mention of it in the documentation of changes between 2.2 API 3.0 API.
Thanks everyone for responding.