Using class extensions in xcode 4.4 - iphone

Since xcode 4.4 you don't need to #synthesize properties anymore (see here), the compiler does it for you. So, why does the compiler complain
use of the undeclared identifier _aVar
in my viewDidLoad method of ViewControllerSubclass:
#interface ViewController : UIViewController
#property (assign, nonatomic) int aVar;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.aVar = 5;
NSLog(#"Super value: %d", _aVar);
}
#end
#interface ViewControllerSubclass : ViewController
#end
#interface ViewControllerSubclass ()
#property (assign, nonatomic) int aVar;
#end
#implementation ViewControllerSubclass
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"Subclass value: %d", _aVar);
}
#end
If I move everything to the one file instead of 4 separate files for the respective interfaces and implementations, the compiler instead complains that _aVar is private. But _aVar should have been automatically synthesized in my ViewControllerSubclass.
Still keeping everything in 1 file, if I move the initial property declaration to a class extension:
#interface ViewController ()
#property (assign, nonatomic) int aVar;
#end
The build still fails saying that _aVar is private.
If I go back to the 4 file setup for the respective interfaces and implementations xcode builds without even a warning.
If I then run the code:
[[[ViewControllerSubclass alloc] init] view];
the log statements in the above examples print out the following:
Super value: 0
Subclass value: 5
It makes sense that NSLog(#"Super value: %d", _aVar); produced a result of 0 because this variable is supposed to be private to the superclass. But then, why does NSLog(#"Subclass value: %d", _aVar); produce a result of 5??
This is all very odd.

You are confusing several different issues, and I'm somewhat confused when you talk about jumping between files and you don't specify where your errors are happening.
Anyway, there is the issue of instance variable visibility. If you declare your iVars within the interface scope, they are, by default, protected.
#interface Foo : NSObject {
int protectedInt;
#private
int privateInt;
#public
int publicInt;
}
#end
When you synthesize iVars, the instance variables themselves are private, unless you explicitly specify them.
Methods will always fire on the most derived implementation.
Now, when you call this...
[[[ViewControllerSubclass alloc] init] view];
You will allocate a subclass, initialize, and cause the view to be loaded. This code will execute...
#implementation ViewControllerSubclass
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"Subclass value: %d", _aVar);
}
#end
The first thing it does is call the base class implementation...
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.aVar = 5;
NSLog(#"Super value: %d", _aVar);
}
#end
Of course, it calls super, but that part's not important here. The next line assigns 5 to self.iVar. But, which iVar? It calls the property setter method on this object. What type is this instance? It's a ViewControllerSubclass. Since you have given both your base class and its subclass the same name (and declared the property as part of the class extension), they each have their own private-scope instance variable .
However, a method is called on the most derived implementation. Thus, self.iVar will set the instance variable of the subclass. The instance variable for the base class remains unchanged.
When you NSLog the value, you are accessing the private instance variable of the base class, which has not been changed.
Now, after the base class viewDidLoad finishes, we get the code running for the subclass. It logs the value of its private instance variable, which was changed as a result of the base class calling the property setter. So, it will now print it's value, which is 5.

When you make the superclass declaration public, the compiler won't attempt to re-synthesize the property; it assumes that's been taken care of in the superclass. Thus, _aVar is not in scope anywhere in the subclass. It's private anyway, so even when you put them all in the same file that's why you see those errors.
However when you make the superclass property declaration inside the class extension, the compiler will auto-synthesize the property for both the superclass and the subclass. This ends up with both classes having private instance variables _aVar (with two distinct addresses). However, when the superclass viewDidLoad method sets the property, the method invokes the subclass's accessors, which set the value of the subclass's private _aVar variable, and not the superclass's. So that explains why you see the superclass value not changing.
Hope this helps!

I just tested your setup and could replicate your error. I came to the following conclusion:
You need to declare your #property in a .h file. If you want a private variable, declare it in .m in the category #interface (the one with the parentheses).

Related

feasible to not declare ivar but use in synthesize?

I'm looking at some sample code and I'm puzzled over the lack of declaration of a specific ivar. Hoping someone can help me understand this better:
typedef NSUInteger (^NumberOfItemsInSection)(ViewClass *viewClass, NSUInteger section);
// class declaration
#interface SampleScrollView : UIScrollView
#property (nonatomic, copy) NumberOfItemsInSection itemsSectionBlock;
#end
// class implementation
#implementation SampleScrollView
#synthesize itemsSectionBlock = _itemsSectionBlock;
- (void)setItemsSectionBlock:(NumberOfItemsInSection)itemsSectionBlock
{
// _itemsSectionBlock is not declared any where in the class
// How does the compiler not complain?
_itemsSectionBlock = [itemsSectionBlock copy];
[self reloadData];
}
#end
The instance variable, "_itemsSectionBlock", is not declared any where and it can just be used in the property's setter override. How does that work?
It's part of the modern runtime, and cuts down on the duplication of code - declaring iVars and then declaring properties for those iVars.
It's handled for you by the #synthesize
The modern runtime lets you do other things that you thought you couldn't do before. For example, you can now declare iVars in the .m file as part of a class extension, which reduces the amount of information you expose in your public interface.
Update
The modern LLVM 4 compiler even lets you do away with the #sytnthesize line. If you declare a property it will auto-synthesize for you and it will even create a backing store with a leading underscore.

"Variable Undeclared" error when compiling to iOS Device, but not for Simulator

I have an custom UIVIewController that is the base class for other controllers and has an instance of a custom UIView variable that is accessed by inherited the classes.
BaseViewController.h
#interface BaseViewController : UIViewController {
UIView *_vwHeader;
}
#end
BaseViewController.m
#import "BaseViewController.h"
#implementation BaseViewController
-(void)loadView {
[super loadView];
_vwHeader = [[UIView alloc] init];
}
#end
CustomViewController.h
#import "BaseViewController.h"
#interface CustomViewController : BaseViewController
#end
CustomViewController.m
#import "CustomViewController.h"
#implementation CustomViewController
- (void)loadView
{
[super loadView];
[_vwHeader setHidden:NO];
}
#end
The problem is that when I am running it on the simulator everything works perfectly fine, but when I change to the device I have an error on the [_vwHeader setHidden:NO]; line which says: '_vwHeader' undeclared (first use in this function)
I already tried to do:
Comment this line of code, but then it gives me an error in another class using a variable from the base class the same way (It only returns one error at a time), so it seems that it is not an specific error in the view or the controller class as the error occurs in other clases with different types, such as UIView and NSObject types
Change target compiler configuration, such as: architectures (all of them), base sdk (all above 4.0) didn't change anything
What seem to solve the problem, but not completely
Creating a property for _vwHeader and accessing it by self._vwHeader or super._vwHeader seems to work, but having to create a property just to access a variable does not make me confortable, specially because I would have to do it for all variables in the same situation inside my project.
changed C/C++ compiler version: using Apple LLVM Compiler 2.1 makes the compilation error goes away, but gives a bunch of other problems with other libraries being used in the project. So, it is not a definitive solution, but might be a clue of what the problem is.
EDIT:
I tried to create another variable that is not a pointer, a BOOL instead of the UIView * and then used it in the inherited class: the problem also occurs
EDIT (2):
I have no properties whatsoever in any of my classes and I still get the error.
I just added the properties for test porpouses, to see if a property in a parent class caused the same behaviour, and apparently it doesn't.
Something that is also weird is that when I get the error in the variable, I checked with my intellisense and it finds it...
In order to refer to an instance variable within any object other than self, including super, you must use the structure pointer operator (->). The default scope of an instance variable is protected, which means it can only be accessed within the class it is defined in or a subclass of that class. Since CustomViewController is a subclass of BaseViewController, this scope is sufficient to access the variable using self->_vwHeader, but if the second class you were trying to do this from is not a subclass you will also need to change the scope to either #public or #package.
In summary, change your method call to:
[self->_vwHeader setHidden:NO];
and it should work for any subclasses of the base view controller.
Do a clean and build, and also make sure you are not specifying a specific framework search path in the build settings. If you leave it empty you should get the correct libraries.
well I don't know, should work.
BaseViewController.h
#interface BaseViewController : UIViewController {
UIView *_vwHeader;
}
#property(nonatomic,retain)UIView *_vwHeader;
#end
BaseViewController.m
#synthesize _vwHeader;
CustomViewController.m
#import "CustomViewController.h"
#implementation CustomViewController
- (void)loadView
{
[super loadView];
[self._vwHeader setHidden:NO];
}
#end
I faced similar problem as you. In my case the reason was (strangely!) wrong synthesization of properties in subclass.
Example:
In .h file of subclass you have following declaration
BOOL _flag;
...
#property (nonatomic, assign) BOOL flag;
while in you synthesize the property in the wrong way:
#synthesize flag;
instead of
#synthesize flag = _flag;
Strangely, the compiler does not complain about the wrong synthesization (the properties even work fine!), but raises an error, when I try to access protected fields declared in base class.
Detailed explanation
Here is what my code look like
I have base class (excerpt):
#interface BaseEditionModalController : NSObject
{
DataContext *_dataContext;
}
And I have subclass of it (excerpt):
#interface LocationModalController : BaseEditionModalController
{
MCLocation *_readLocation;
LocationCommModel *_oldLocationCommModel;
}
//This is MCLocation for reading only - from the main application context
#property (nonatomic, retain) MCLocation *readLocation;
#property (nonatomic, retain) LocationCommModel *oldLocationCommModel;
#end
And in the LocationModalController.m I have following wrong declarations:
#implementation LocationModalController
#synthesize readLocation;
#synthesize oldLocationCommModel;
Trying to access _dataContext in LocationModalController produced the error that _dataContext is undeclared.
Changing the synthesization of properties to:
#implementation LocationModalController
#synthesize readLocation = _readLocation;
#synthesize oldLocationCommModel = _oldLocationCommModel;
MAGICALLY SOLVES THE PROBLEM!
Regards
I just stumble upon your method declaration
-(void)loadView { ... }
In a view the first point you can rely that everything is fully initialized is after -(void)viewDidLoad was called. Maybe your code works on the simulator because your Mac is fast enough to cope this speed issue - but your mobile device isn't.
Maybe try this coding:
Your BaseViewController.h file:
#interface BaseViewController : UIViewController {
UIView *_vwHeader;
}
#end
Your BaseViewController.m file:
#import "BaseViewController.h"
#implementation BaseViewController
-(void)viewDidLoad {
[super viewDidLoad];
_vwHeader = [[UIView alloc] init];
}
Your CustomViewController.h file:
#interface CustomViewController : BaseViewController {
}
#end
Your CustomViewController.m file:
#import "CustomViewController.h"
-(void)viewDidLoad {
[super viewDidLoad];
[_vwHeader setHidden:NO];
}
Now your CustomViewController can rely on every instance variable in BaseViewController is correctly instantiated.
The error says that _vwHeader undeclared.
So try by modifying the code in:
CustomViewController.m
#import "CustomViewController.h"
#implementation CustomViewController
- (void)loadView
{
[super loadView];
if(!_vwHeader)
{
_vwHeader = [[UIView alloc]init];
}
[_vwHeader setHidden:NO];
}
#end
It is possible that when you compile for target and for simulation the data members are either protected or private. Probably for target are private by default and this seems to cause the problem. Try out playing with the #private and #protected keywords.
However, I strongly suggest that you use properties even between your super/sub-classes. Having a complex structure is a bit hard to debug. Setting up a property will transfer the access to the data through the getter/setter methods (a breakpoint on #synthesize also works) and you will be able to see in the call stack who is accessing what.
Especially, the syntax #synthesize propertyName = prefixDataNameSufix; allows you to easily adjust your class interface style without having to modify your coding habits.
I had the exact same problem and it turns out that I did not remove an unused iVar/property in the SUBCLASS. Let's call it session. I removed _session from the iVar but I forgot to remove it from the properties, then in the .m file I had this synthesize session = _session. Once I removed them all, I can compile for iOS device without problems.
If you think your superclass is fine, look into your subclass, check your iVars and properties and synthesize section in the .m file
I had this exact problem.
In my case, I was relying on ivar synthesis of a property. That is, I did NOT declare UITextView *textView_, but I did #synthesize textView = textView_;
This builds fine on my iOS Simulator. However, my iOS device build fails, regardless of whether I use llvm or gcc.
When I add the declaration back to my interface:
#interface MyTableViewController : BaseTableViewController {
#private
UITextView *textView_; // this line is important!
}
Everything works fine!
See the answer from tc above. This is a bug in the compiler shipped with sdk 4.2.
Specifically I have seen the same error and if on the same machine I have sdk 4.2 and sdk 4.3 installed the error disappears (even if I compile for 4.2).
If anyone is having this issue after upgrading their tools and devices to iOS10, I had it and found that declaring them as weak, nonatomic in the .h file was the issue. I had never encountered this when doing so before but after removing (weak, nonatomic) from the property declaration, everything worked fine again.

Instance variables with underscore in Objective-C 2.0 and renaming with #synthetize leads to optimization warnings by the 'Analyze' tool of Xcode 4 [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How does an underscore in front of a variable in a cocoa objective-c class work?
I'm using the same convention for instance variable and properties naming as shown by sebnow in his following answer:
instance variable/ method argument naming in Objective C
I copy paste his example code here:
#interface Foo : NSObject {
id _bar;
}
#property (nonatomic, retain) id bar;
- (id) initWithBar:(id)aBar;
#end
#implementation Foo
#synthesize bar = _bar;
- (id) initWithBar:(id)aBar {
self = [super init];
if(self != nil) {
_bar = aBar;
}
return self;
}
#end
In the implementation of some methods of the Foo class, I use for example:
_bar = aBar
instead of using:
bar = aBar
The 'Analyse' tool introduced by Xcode 4 gives me this warning (I'm using version 4.0.2):
Instance variable 'bar' in class 'Foo' is never used by the methods in its #implementation (although it may be used by category methods)
Perhaps I should use:
self.bar = aBar
But for the readonly properties, that can't work, and beside that, I'm not sure if using the setter in the class itself is a good practice or not.
I'm not fresh in Objective-C, but I'm still in the beginning of learning. Perhaps I'm doing something wrong, and have a bad coding practice somewhere.
Thanks you in advance if you can help me ;)
"Is never used" should be taken literally: you only define its value in assignments, never use it.
This is the same kind of warning you get for local variables: if you only define their values and never use it, what are they for?
The static analyzer is thus warning you, because typically variables that are never accessed are just remains of older code that has changed and you can remove them. But in your case it could be perfectly fine.
The #synthesize line affects how the setter and getter for the property 'bar' operate. The line:
#synthesize bar = _bar;
Effectively says "put in the standard getter (and setter, if relevant) for bar, as per the way I've declared it as a #property, but use the instance variable _bar for the storage".
When you use self.bar as an lvalue you're actually making a method call to [self setBar:] and when you use it as an rvalue you're actually making a call to [self bar]. It looks like a normal C-style struct member access but internally it's a method call.
So, the #synthesize creates a suitable getter and setter to use for self.bar, but doesn't change the name of the instance variable. You should therefore be right to use _bar when accessing the thing directly from within the class itself (though some people now frown upon that from a style point of view) and self.bar otherwise, without receiving any analyser warnings.
For you to end up with an instance variable called bar, assuming you didn't declare one inside your interface, the most likely mistake is an error in the way you've performed your #synthesize. In the modern runtime you can supply a #property/#synthesize pair for a variable you haven't actually declared in your interface and the variable will be magically added to your interface. So you can do that by accident if you make an unfortunate typo.
If possible, could you post your actual code?
Please see my comment.
Try adding a -dealloc method to release the object. This will 'access' the Ivar and should make the static analyser happy.
-(void)dealloc
{
[bar release]; bar = nil;
[super dealloc]
}
Now that I can respond to my question 8 hours later, I'm doing it for anyone who made the same mistake as me during some test or something. However, the answers of sergio and Tommy are very informative.
After reading answers, I saw that I made a silly mistake. During a test of coding of my class, I removed the underscore before my instance variables declaration. So my actual code was truely looking like this:
#interface Foo : NSObject {
id bar;
}
#property (nonatomic, retain) id bar;
- (id) initWithBar:(id)aBar;
#end
#implementation Foo
#synthesize bar = _bar;
- (id) initWithBar:(id)aBar {
self = [super init];
if(self != nil) {
_bar = aBar;
}
return self;
}
#end
So the Analyse warnings were correct. Sorry for the false alarm! But thanks for very fast answers.
use this ->
#interface Foo : NSObject {
id _bar;
}
#property (nonatomic, retain) id _bar;
- (id) initWithBar:(id)aBar;
#end
#implementation Foo
#synthesize bar = _bar;
- (id) initWithBar:(id)aBar {
self = [super init];
if(self != nil) {
bar = aBar;
}
return self;
}
#end

Category-like extension for instance variables

Is there a way to somehow emulate category behavior for a class regarding to it's instance variables, not methods ?
I have a ClassA, and I want to keep its name after extending it with new methods AND ivars from other cllass (ClassB).
Of course, I can inherit ClassA, but resulting class will have different name.
For methods addition, it's not a problem - category would be a good solution.
UPDATE: ClassA used as file owner for a XIB, and these fields to be extended are IBOutlets. So I need them at build phase.
Since the iPhone uses the modern Objective-C runtime, you can use associative references to add data to instances without having to declare instance variables. See the documentation for objc_setAssociatedObject etc.
If you wrap the calls to the runtime in standard accessor methods, it will be very easy to use.
I've investigated this question playing around associative references (thanks to Ole), with methods static variables, methods swizzling, and finally come to this simple solution (no runtime stuff). I simply use "categorized" class only to return a pointer to a derived class, which of course can contain additional ivars. Doing so I achieve one unexpected benefit: I can call super's class methods, which is impossible when extending through categories.
Example of a class extension (tested):
ClassA+ClassB.h
#protocol _ClassB_Protocol
#optional // to avoid warnings
- (IBAction) onClick:(id)sender;
#property (nonatomic, retain) IBOutlet UIButton *aButton;
#end
#interface ClassA (_ClassA_Category) <_ClassB_Protocol>
#end
#interface ClassB: ClassA <_ClassB_Protocol> {
UIButton *aButton; // _ivar_ to add
}
#end
ClassA+ClassB.m
#implementation ClassA (_ClassA_Category)
// this will be called first on [ClassA alloc] or [ClassA allocWithZone:(NSZone *)zone]
+(id) alloc {
if ([self isEqual: [ClassA class]]) {
return [ClassB alloc];
} else {
return [super alloc];
}
}
#end
#implementation ClassB: ClassA
#synthesize aButton;
-(void) dealloc {
[aButton release];
[super dealloc]; // this is impossible for an ordinary category
}
- (void) onClick:(id)sender {
// some code here
}
#end
Now we have in the same time:
ClassB "extends" ClassA (category way);
ClassB inherits ClassA (ClassB can call ClassA methods);
ClassB can be accessed through ClassA name (category way)
I put Martin's example into a trivial app replacing ClassA with NSData, ClassB with XXData, and onClick with getIvar, and invoked it (Mac OS X 10.6.6, Xcode 4 Final) with:
NSData * data = [NSData data];
NSLog(#"%#", [data getIvar]);
It fails with "-[NSConcreteData getIvar]: unrecognized selector sent to instance" ..
It fails because "alloc" in the NSData category (which returns the pointer to the derived class) is not called by the above code. If, instead, "alloc" is called explicitly, as in:
NSData * data = [[NSData alloc] init];
NSLog(#"%#", [data getIvar]);
then all is well.

Object as a data member in Objective C

From what I have experienced it seems as if objects cannot be shared data members in objective c. I know you can init a pointer and alloc the object in each method but I cannot seem to figure out how one can say define a NSMutableString as a data member and allow all of the methods to use and modify its data as in c++. Is this true or am I missing something?
To define an instance variable (member), edit your .h file:
#interface MyClass : NSObject {
// ivars go here
NSObject *member;
}
// methods go here
#end
Then, in your .m file, from any instance method (one which begins with -), you can access this variable.
- (void)doThingWithIvar {
[member doThing];
}
If you want to access the variable from outside the object itself, you'll need accessors. You can do this easily with Obj-C properties:
#interface MyClass : NSObject {
// ivars go here
NSObject *member;
}
// methods go here
#property (nonatomic, retain) NSObject *member;
#end
And in the .m:
#implementation MyClass
#synthesize member;
// ...
#end
The #synthesize line creates getter/setter methods for the ivar. Then you can use property syntax:
MyClass *thing = ...;
NSLog(#"%#", thing.member); // getting
thing.member = obj; // setting
(Note that I specified (retain) for the #property; if your member isn't an Objective-C object you won't want that. And if your property's class has a mutable counterpart, you'll want (copy) instead.)
It sounds like you want to synthesize (create getter/setter methods) a property for a member variable. I just found this cheat sheet, go down to the section called, "Properties", should give a quick overview.
Other than that Apple's documentation should give you more info.