objective c xcode 4.0.2: subclass can't access superclass variables "was not declared in this scope" - iphone

I have several classes that are subclasses of one Layer class.. for some reason one of the subclasses acts differently. this is a stripped down version:
#interface Layer: CCLayer
{
GameScene* _scene;
CCNode* _layerNode;
}
#end
#import "Layer.h"
#interface UILayer: Layer
{
}
#end
#implementation UILayer
-(void) doStuff
{
[_layerNode addChild:[CCNode node]]; <---gives compile error: "_layerNode was not declared in this scope"
[_scene playSound];<------gives compile error: "_scene was not declared in this scope"
}
#end
I think that gets the basic idea across. I can fix this by doing
[[_self _layerNode] addChild:[CCNode node]];
[[_self _scene] playSound];
but what I can't figure out is why other subclasses of Layer can access _layerNode and _scene directly? I am guessing that it is a problem with the build settings. Further this problem only happens when I build it for the device (iphone) and not the simulator. let me know if you need more information to answer.
Edit:
Oops I wrote it wrong. It should have been [[self _layerNode] addNode:[CCNode node]] but I guess the question is why would one subclass have direct access to the ivar _layerNode and another have to access it with [self _layerNode]. It is like the UILayer can't find the super class header

From the docs: Apple Objective-C Programming Language (doesn't mention this anymore, but you can find it in this retired document).
The instance variable is accessible within the class that declares it and within classes that inherit it. All instance variables without an explicit scope directive have #protected scope.
However, a public instance variable can be accessed anywhere as if it were a field in a C structure. For example:
Worker *ceo = [[Worker alloc] init];
ceo->boss = nil;
I have the compilation error using LLVM GCC 4.2 (for an iOS project, on device):
error: 'fooInstanceVar' undeclared (first use in this function)
and the same one using GCC 4.2 :
error: 'fooInstanceVar' undeclared (first use in this function)
I can compile using LLVM Compiler 2.0 whithout error.
For compiling with LLVM GCC 4.2 and GCC 4.2 with the use of self->:
[NSArray arrayWithObjects:self.barProperty, self->fooInstanceVar, nil];
in the doSomethingWithProperty method.

Maybe it's the name. The 'UI' prefix is designated for UIKit classes. Could well be that UILayer is a private UIKit class used by Apple.

If you #synthesize ivars, the compiler unhelpfully stops you from accessing them "directly". The fix is relatively easy: Write self->_layerNode and self->_scene.
I'm assuming your "stripped-down version" compiles fine.
Note that it's also bad form to directly access your superlcass's ivars unless you're aware of its implementation details.
(What's _self?)

Related

How to avoid compile warning when subclassing a class with category?

Say we have parent class
ParentViewController.h
#interface ParentViewController
....
#end
ParentViewController.m
#interface ParentViewController()
- (NSArray *)selectedItems;
#end
#implementation ParentViewController
.....
#end
And then we subclass it
ChildViewController.h
#interface ChildViewController : ParentViewController
....
#end
ChildClassViewController.m
#implementation ChildViewController
- (void)doSomething
{
// XCode Warning Flag on this line
NSUInteger count = [self selectedItems];
.....
}
XCode will set Warning flag at the commented line and says that "Instance method '-selectedItems' not found (return type defaults to 'id').
Yes I know that in ObjC there is no such thing as private methods, but using an empty category kind of gives the ability to do so. But somehow it does not get inherited by subclasses.
I usually fix it by moving the method from ParentViewController.m to ParentViewController.h. This feels weird, I loose the ability to make the method private just because I need to subclass it.
Now my question is:
Why does the parent subclass cannot find those methods that is declared in its category at the .m file?
Is there a way to remove the Warning Flag but without losing the ability to keep the method private.
Hopefully someone with more experience will be able to help explain this annoying issue.
First, note that your "empty category" isn't a Category at all, it's a Class Extension. Class Extensions very similar to categories but they're new in Objective C 2.0, and they differ slightly in their use. Primarily, the compiler will warn you if you don't implement a method in a Class Extension, but it won't with a Category. Anyways, on to the issue at hand...
Privacy in Objective-C is all about visibility. If the compiler can't see see the declaration of a method that's being used, you'll get a warning. Note that if you were to implement your subclass in the same file as your Class Extension, the compiler won't warn you because it can see the declaration.
Therefore, If you want to use "private" methods in subclasses, you just need some way of showing the compiler that those methods exist. My favorite pattern is to declare the private methods in a Category in a separate file (like MyClass_private.h). You then only import that interface into the implementation files of the base class and any derived classes that need to see it.
I have a solution, but generally I would advise against it. if you compile the file with -w (suppress all warnings), the warning goes away. I do not know if there is a specific warning flag for this message, if there was, you could use #pragma GCC diagnostic ignored "-Winstance-method-not-found", but I can't find it, sorry.

Why is this code giving me an error about a weak property?

This is my simple piece of code on iOS, using ARC:
#interface Person : NSObject {
NSObject *objStrong;
NSObject *objWeek;
}
#property(strong) NSObject *objStrong;
//getting error at this line
#property(weak) NSObject *objWeek; //Existing ivar 'objWeek' for _week property 'objWeek' must be _week
#end
#implementation Person
#synthesize objStrong;
#synthesize objWeek;
#end
When I try to compile, the compiler complains about an existing ivar 'objWeek' for _weak property 'objWeek'. Why isn't this code compiling correctly?
It's complaining because the backing variable, NSObject *objWeek is declared as __strong (all otherwise unannotated Objective C pointers to retainable objects are __strong). Change the backing variable to be __weak NSObject *objWeek, and the compiler will like you again.
Edit: As requested, the ARC documentation from LLVM's clang:
4.4.1. Objects
If an object is declared with retainable object owner type, but
without an explicit ownership qualifier, its type is implicitly
adjusted to have __strong qualification.
As a special case, if the object's base type is Class (possibly
protocol-qualified), the type is adjusted to have __unsafe_unretained
qualification instead.
Link: http://clang.llvm.org/docs/AutomaticReferenceCounting.html#ownership.inference
Same problem I have faced. I did spend lot of time on this finally I could solve this problem by cleaning the project.Here is the way....
product->clean
The reason is you may be deleted controller class(ViewController) but still it is not removed from your project.when you finish cleaning of your project the error will be gone automatically. This worked for me...

receiver type *** for instance message is a forward declaration

In my iOS5 app, I have NSObject States class, and trying to init it:
states = [states init];
here is init method in States:
- (id) init
{
if ((self = [super init]))
{
pickedGlasses = 0;
}
return self;
}
But there is error in the line states = [states init];
receiver type "States" for instance message is a forward declaration
What does it mean? What am I doing wrong?
That basically means that you need to import the .h file containing the declaration of States.
However, there is a lot of other stuff wrong with your code.
You're -init'ing an object without +alloc'ing it. That won't work
You're declaring an object as a non-pointer type, that won't work either
You're not calling [super init] in -init.
You've declared the class using #class in the header, but never imported the class.
FWIW, I got this error when I was implementing core data in to an existing project. It turned out I forgot to link CoreData.h to my project. I had already added the CoreData framework to my project but solved the issue by linking to the framework in my pre-compiled header just like Apple's templates do:
#import <Availability.h>
#ifndef __IPHONE_5_0
#warning "This project uses features only available in iOS SDK 5.0 and later."
#endif
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#endif
I got this sort of message when I had two files that depended on each other. The tricky thing here is that you'll get a circular reference if you just try to import each other (class A imports class B, class B imports class A) from their header files. So what you would do is instead place a forward (#class A) declaration in one of the classes' (class B's) header file. However, when attempting to use an ivar of class A within the implementation of class B, this very error comes up, merely adding an #import "A.h" in the .m file of class B fixed the problem for me.
I was trying to use #class "Myclass.h".
When I changed it to #import "Myclass.h", it worked fine.
If you are getting this error while trying to use Swift class or method in Objective C: you forgot one of 2 steps Apple defined on this diagram:
Example:
Error shows up in your Test.m file:
Receiver 'MyClass' for class message is a forward declaration
In Obj-C files:
Step 1: check that Test.h has
#class MyClass;
Step 2: find *-Swift.h file name in Build Settings (look for Objective-C Generated Interface Header Name). Name will be something like MyModule-Swift.h
Step 3: check that Test.m imports the above header
#import <MyModule/MyModule-Swift.h>
In Swift file:
Ensure MyClass (or it's base class) inherits NSObject class.
Ensure #objc is before each method you want call from Obj-C.
Also, check Target Membership section (in File Inspector).
You are using
States states;
where as you should use
States *states;
Your init method should be like this
-(id)init {
if( (self = [super init]) ) {
pickedGlasses = 0;
}
return self;
}
Now finally when you are going to create an object for States class you should do it like this.
State *states = [[States alloc] init];
I am not saying this is the best way of doing this. But it may help you understand the very basic use of initializing objects.
Check if you imported the header files of classes that are throwing this error.
Make sure the prototype for your unit method is in the .h file.
Because you're calling the method higher in the file than you're defining it, you get this message. Alternatively, you could rearrange your methods, so that callers are lower in the file than the methods they call.
There are two related error messages that may tell you something is wrong with declarations and/or imports.
The first is the one you are referring to, which can be generated by NOT putting an #import in your .m (or .pch file) while declaring an #class in your .h.
The second you might see, if you had a method in your States class like:
- (void)logout:(NSTimer *)timer
after adding the #import is this:
No visible #interface for "States" declares the selector 'logout:'
If you see this, you need to check and see if you declared your "logout" method (in this instance) in the .h file of the class you're importing or forwarding.
So in your case, you would need a:
- (void)logout:(NSTimer *)timer;
in your States class's .h to make one or both of these related errors disappear.

Why am I getting this warning from Xcode? The iOS code seems fine

This one has me stumped. I'm writing an iPhone app that tracks bus schedules. Users can bookmark their favorite bus stops so they can jump directly to them from the home screen. I manage the list of favorites in my AppDelegate class (unrelated code has been redacted):
#interface AppDelegate : NSObject <UIApplicationDelegate>
+ (BOOL) isInFavorites: (FavoriteStopData*) inStop;
#end
I have a view controller that presents the list of stops for a given bus route and lets users select one to see predicted bus arrival times for that stop in a new view (and maybe add the stop to their list of favorites):
#implementation RouteStopsViewController
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
FavoriteStopData *stopData = [[FavoriteStopData alloc] init];
// ... set various properties in stopData from data in the selected cell
FavoriteStopViewController *fvc = [[FavoriteStopViewController alloc] initWithNibName:#"FavoriteStopViewController" bundle:nil];
fvc.stop = stopData;
fvc.isBookmarked = [AppDelegate isInFavorites:stopData];
[stopData release];
[self.navigationController pushViewController:fvc animated:YES];
[fvc release];
}
#end
The line
fvc.isBookmarked = [AppDelegate isInFavorites:stopData];
gets two warnings:
"'AppDelegate' may not respond to +isInFavorites:"
"Passing argument 1 of 'setIsBookmarked' makes integer from pointer without a cast"
I can't see any reason for Xcode to think '+isInFavorites:' is undefined, yet it does. I've verified that these possible causes for the warning are not in fact the case:
'+isInFavorites:' is declared in "AppDelegate.h" (as shown above)
"RouteStopsViewController.m" does #import "AppDelegate.h" (and "FavoriteStopData.h" and "FavoriteStopViewController.h")
'isBookmarked' is a public BOOL property on FavoriteStopViewController
The code is not being munged by some #define macro; when I preprocess "RouteStopsViewController.m", this code is unchanged.
The code behaves correctly, but I REALLY don't want to live with a warning that I must ignore every time the code compiles, and disabling this warning with some #pragma is a road I'd rather not take unless I have to.
I've tried renaming the method name, the variable names, using the method to set a local BOOL variable and then setting the property with that, using a conditional operator (x ? y : z) to make sure I'm passing a BOOL to the property ... nothing works. That first warning never goes away.
Can anyone suggest why Xcode is giving me this warning?
This is with Xcode 4.2 (Build 4C199) and iOS 5 SDK running in the 5.0 iPhone Simulator on a MacBook Pro running Mac OS X 10.6.8 (Snow Leopard).
If the +isInFavorites: method was completely unknown to the compiler then you'd see a warning like +isInFavorites: not found (return type defaults to 'id').
If you're not seeing that warning then we can assume that the compiler has seen a declaration of that method somewhere. However, the compiler expects this method to return a pointer rather than a BOOL, which is why you're seeing the makes integer from pointer without a cast warning.
Check for any other declarations of an isInFavorites: method in your project. Check for any global variables named AppDelegate that may conflict with your class name. Check for any circular imports between AppDelegate.h and RouteStopsViewController.h. Try renaming your AppDelegate class.
declare your isInFavorites method in your appdelegate.h file.
'AppDelegate' may not respond to +isInFavorites:
Passing argument 1 of 'setIsBookmarked' makes integer from pointer without a cast
The first error causes the second. The compiler's confusion on the existence of +isInFavorites: causes the compiler to assume the return type is id
This assumption causes the warning of making integer from pointer without a cast
You really have to focus on the first warning.
Are these the only warnings?
Try changing your AppDelegate.h to
#class FavoriteStopData
#interface AppDelegate : NSObject <UIApplicationDelegate>
+ (BOOL) isInFavorites: (FavoriteStopData*) inStop;
#end
If you still have issues, you might want to consider making this an instance method instead, considering the appDelegate is valid the whole runtime of your app
Found it. Even makes perfect sense ... now that I know exactly where to look.
My project has two targets: Dev for the version I use to test new code, and App for the user-facing version. Each has its own AppDelegate class (and some other duplicates). Code specific to one target or the other goes into either the ./Dev/ or the ./App/ folder. Common code goes into other folders.
Recently I promoted one Dev-specific class to be used in both targets ... but hadn't yet moved the files out of the Dev folder. This was my problematic RouteStopsViewController. My project was compiling the right "AppDelegate.m", but Xcode was finding the 'wrong' (to my thinking) "AppDelegate.h" because it was looking first in the same folder as "RouteStopsViewController.m".
The fix was easy: move RouteStopsViewController out of the Dev-specific folder into one for code shared by both targets. Now Xcode uses the "AppDelegate.m" file it's compiling to find the matching "AppDelegate.h".
I knew at the time I should move that RouteStopsViewController class when I decided to reuse it in the App target, I just didn't get around to it. When it comes to writing code, trust your nose. If it smells funny, it very probably is.

Objective-C changes between OS 2.2.1 and OS 3?

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.