Apple recommends that you access the instance variables that back your properties directly, rather than using a getter/setter, when initializing a class:
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/EncapsulatingData/EncapsulatingData.html
However, it seems that instance variables backing a property in a parent class are not accessible in the child class; why is this the case? I'm extending a class in a library (Cocos2d) where not all the instance variables are initialized in the parent class init function. For example:
---
#interface parentClass
#property (assign) int myProperty;
----
#interface childClass : parentClass
----
#implementation childClass
- (id) init {
// this doesn't work.
_myProperty = 0;
}
You can't access instance variables from your superclass in a subclass, so _variableName will also not work.
You init method will look something like this
- (instancetype)init {
if (self=[super init]) {
// subclass initialisation goes here
}
}
Once [super init] returned an object, the superclass part of your object is initialised, so it should be safe to access properties using their getters and setters:
- (instancetype)init {
if (self=[super init]) {
self.superClassProperty = aValue;
}
}
Have a look at "Don't message self in Objective-C init" on QualityCoding on when to use instance variables and when to call methods (e.g. property accessors). In short: Only call methods when your object is in a consistent state.
Why can't you access backing ivars?
A property declaration in a header declares a getter and setter for the property, a backing ivar is created when the property is synthesised, which happens in the implementation. (Automatic and manual synthesis doesn't make a difference). The ivar declaration is therefor only visible in the implementation. If you absolutely have to access ivars in subclasses, you have to make them public (or semi-public by putting them in a header for subclassing only).
You can do the following in your parentClass.h:
#interface parentClass {
#protected
int _myProperty;
}
#property (nonatomic) int myProperty;
Then, in your childClass.m
- (instancetype)init {
if (self=[super init]) {
_myProperty = aValue;
}
}
iVars are declared as protected by default, so your children can see them. There is no need to write #protected. And for your information you can also declare them as #private or #public.
But if you write the protected iVar in a private interface in your parentClass.m, this will not work and the children will not see it.
Related
This question already has answers here:
Error accessing generated ivars when I override setters and getters in Modern Objective-C
(3 answers)
Closed 5 years ago.
I'm learning the Swift programing language and during this I sometimes get in touch with the old Objective-C programming language and its code.
I'm an absolutely beginner and therefore I have some question for better understanding the Setter and Getter.
So, I know that I can create an instance variable through curly braces in the .h file but normally I use properties. These properties are backed by an instance variable and offer automatically a Getter and Setter Method.
Example:
Vehicle .h file:
#interface Vehicle : NSObject
#property int myProperty;
#end
Because I created this property I don't have to declare a Getter and Setter method in the vehicle.m file because they are automatically created by the compiler. So I can create a vehicle-object, set and get the value.
Example
main.m
Vehicle *myvehicle = [[vehicle alloc] init];
[myvehicle myProperty] // myvehicle.myProperty
[myvehicle setMyProperty : 10] // myvehicle.myProperty = 10;
Now I read that it is possible to override the automatically created Getter and Setter method of my created property "myProperty". When declaring my own version of the Getter and Setter I have to declare two methods in the vehicle.h and vehicle.m file. In the vehicle.m file I don't call the object by using the self keyword but by using it's automatically created instance variable (_myProperty). Is it right?
I tried it but alway get an error and I don't know why and what is the point.
Example
Vehicle .h file:
#interface Vehicle : NSObject
#property int myProperty;
-(int) myProperty; //my new Getter method
-(void) setMyProperty: (int)updatedMyProperty; //My new Setter method
#end
vehicle .m file:
#implementation Vehicle
-(int) myProperty {
if (! _myProperty) {
_myProperty = NO;
}
return _myProperty;
}
-(void) setMyProperty: (int)updatedMyProperty {
if (_myProperty == updatedMyProperty) return;
_myProperty = updatedMyProperty;
}
#end
I always get the error "Use of undeclared identifier" and I don't know why. If I understand right I don't have to declare the ivar or its name using #synthesize because the compiler automatically creates the ivar called _myProperty for me. I just have to use #synthesize when I want to change the ivar's name.
I'm not sure why I get stuck and what the point is. Could you explain it?
Thanks in advance!
If you implement all of the accessor methods, the compiler will no longer automatically synthesize the ivar for you. In this case, you have to explicitly do so yourself. E.g.
#synthesize myProperty = _myProperty;
This is only necessary when you have manually implemented all of the accessor methods. The reason is that the compiler is smart enough to know that if you're taking over the accessor methods, you may well not need the ivar, namely that you might be doing something radically different, e.g. computing values from some other property, setting/getting values from some different store, etc. You may want the compiler to synthesize the ivar (in which case you add the above #synthesize statement), but it's equally likely that you've implemented the accessor methods because no backing ivar is needed (in which case you'd omit the above #synthesize statement).
Anyway, staying with your simple example, you get something like:
#interface Vehicle : NSObject
#property (nonatomic) int myProperty; // if you don't write atomic accessor methods, you really should be explicit that this is nonatomic
// as an aside, even if you implement accessor methods, you don't have to declare them here
//
// -(int) myProperty; //my new Getter method
// -(void) setMyProperty: (int)updatedMyProperty; //My new Setter method
#end
And
#implementation Vehicle
// since you implemented all of the accessor properties, you have to manually synthesize the ivar
#synthesize myProperty = _myProperty;
- (int) myProperty {
// do whatever you want here; note, the following doesn't make sense
//
// if (! _myProperty) {
// _myProperty = NO;
// }
return _myProperty;
}
- (void)setMyProperty:(int)updatedMyProperty {
if (_myProperty == updatedMyProperty) return;
_myProperty = updatedMyProperty;
}
#end
Clearly, there's no point in writing these particular accessor methods in the above example, because you're not offering any new functionality, so you wouldn't. You'd just avail yourself of the auto-synthesized accessor methods.
But in those cases that you really need to write your own accessor methods, then you have to explicitly tell the compiler whether you need it to synthesize the ivar for you, too, or not.
I want to do something like this:
#property (nonatomic, retain) NSObject *obj1;
#property (nonatomic, retain) NSObject *obj2;
- (id)init {
if ((self = [super init])) {
[SomeClass someFuncWithParam1:*(self.obj1) param2:*(self.obj2)];
}
}
#implementation SomeClass
+ (void)someFuncWithParam1:(NSObject **)param1 param2:(NSObject **)param2 {
//init obj1;
...
//init obj2;
...
}
#end
I haven't found any example how to pass objective-C properties into a function for initialization. I know that it is possible with usual variables but there are no examples about what to do with properties.
You cannot pass an argument by reference in (Objective-)C. What you probably mean is to
pass the address of a variable as an argument to the method, so that the method can
set the value of the variable via the pointer.
However, this does not work with properties.
self.obj1 is just a convenient notation for the method call [self obj1], which returns
the value of the property. And taking the address of a return value is not possible.
What you can do is
Pass the address of the corresponding instance variables:
[SomeClass someFuncWithParam1:&_obj1 param2:&_obj2];
The disadvantage is that the property accessor methods are bypassed. That may or may not be
relevant in your case.
Pass the address of temporary local variables:
NSObject *tmpObj1;
NSObject *tmpObj2;
[SomeClass someFuncWithParam1:&tmpObj1 param2:&tmpObj2];
self.obj1 = tmpObj1;
self.obj2 = tmpObj2;
Both solutions are not very nice. Alternatively, you could pass the entire object (self) to the helper method, which then initializes both properties.
Or you define a custom class with just the properties obj1 and obj2, and make the helper method return an instance of this custom class.
For example, I have some class A. And then I inherit another class from A.
#interface A : NSObject
{
int _nonHiddenProp;
#private
int _hiddenProp;
}
#property (nonatomic, assign) int property;
#property (nonatomic, assign) int nonHiddenProp;
#property (nonatomic, assign) int hiddenProp;
#end
#implementation A
- (id)init
{
if (self = [super init])
{
_property = 1000;
}
return self;
}
#end
#interface B : A
#end
#implementation TestCapabilitiesChild
- (id)init
{
if (self = [super init])
{
_nonHiddenProp = 1000;
//I cannot call _property and _hiddenProperty
}
return self;
}
#end
But:
A *a = [[[A alloc] init] autorelease];
B *b = [[[B alloc] init] autorelease];
NSLog(#"BClassProperties %d %d %d", b.nonHiddenProp, b.property, b.hiddenProp);
Shows: BClassProperties 1000 1000 0
Why? If I cannot call variable _property in init of B it still is 1000?
You have explicitly declared two instance variables _nonHiddenProp and _hiddenProp of which _nonHiddenProp has visibility to subclasses and _hiddenProp has visibility only to the class it is in. You have also declared three properties: property, nonHiddenProp and hiddenProp.
The first thing to note is that properties are not variables. Properties and instance variables are different things. A property is actually a pair of accessor methods, one to get its value and the other to set its value (the setter may be omitted for read only properties). Note that by "its value" I mean the property's value not the value of any particular instance variable. I might have mentioned this before but properties and instance variables are not the same thing.
As properties are really a pair of methods, the visibility rules for a property are the same as for methods, namely "all methods are public", therefore all properties are also public.
If you do not provide implementations for the property's two accessors and you do not explicitly synthesise the property, the compiler will automatically provide implementations. This it does by:
first inventing an instance variable name which is the same as the property name but with an underscore in front.
if the instance variable doesn't already exist, it declares one with private visibility.
create getter and setter to get/set the instance variable when the property is got/set.
You can't set the instance variable _property from within the subclass because the subclass does not have visibility of the instance variable. The NSLog works because it is using the property, not the instance variable.
You declared a property for the instance variable... this means, that a setter and getter was syntesized, which are methods... in Objective-C all methods are public (althrough you can "hide" them, or the compiler / IDE may prevent you from compiling)
But in reality and during runtime nothing prevents you from sending a message (calling a method) on that class
I need to enforce the initialization of an ivar in a superclass but that ivar usually can not be initialized without other data in the subclass to be initialized. The two solutions I have thought of is:
pass the required generated key for the ivar to the superclass's init method
calling a second superclass method from the subclass's init method
Here is example (contrived, non-working) code. The stringBasedOnSubclassKey ivar should be initialized to the NSString from the subclass's key method.
#interface MySuperclass : NSObject
#property (nonatomic, readonly) NSString *stringBasedOnSubclassKey;
#end
#interface MySubclass : MySuperclass
#property (nonatomic, assign, readonly) int value;
#end
#implementation MySubclass
- (instancetype)init
{
if (self = [super init]) {
_value = 30;
}
return self;
}
- (NSString *)key
{
return [NSString stringWithFormat:#"UniqueKey-%d", self.value];
}
So the question is is there a way to enforce the initialization of the stringBasedOnSubclassKey ivar using the return value of the "key" method? I don't believe I can enforce solution 1 and 2 above. These subclasses may also be created by other outside developers so the key method may be more complicated than this.
Update: I am dealing with existing subclasses of this base class so solutions limiting the changes to existing subclasses is a factor.
Write the getter for stringBasedOnSubclassKey in such a way as to force initialization of it:
- (NSString *) stringBasedOnSubclassKey {
if !(_stringBasedOnSubclassKey) {
_stringBasedOnSubclassKey = // whatever;
}
return _stringBasedOnSubclassKey;
}
And write the superclass key method to throw an exception, thus forcing the client to override it in the subclass.
I have a category on an existing class that adds a property and a few methods to the class.
#interface AClass (ACategory) {
NSString *aProperty;
}
#property (nonatomic, retain) NSString *aProperty;
#end
In the implementation file, I want to release this property when the object is deallocated. However, if I declare dealloc in this class, it will override the dealloc from the original class from what I understand. What then is the proper way to release this aProperty when the object is deallocated?
#implementation AClass (ACategory)
#synthesize aProperty;
- (void)dealloc {
[aProperty release];
// this will skip the original dealloc method from what I understand
[super dealloc];
}
#end
Well, this is a little problematic, since your code is wrong.
You can't declare instance variables in a category; using the latest Objective-C ABI, you can declare new instance variables within a class extension (#interface AClass () {//...), but that is different from a category (#interface AClass (ACategory)).
Even if you could, the syntax for instance variable declaration is that they be enclosed in curly braces after the #interface line.
You can declare a property in a category, but you'll have to define its storage without using a new instance variable (hence, #dynamic instead of #synthesize).
As to your actual question, you can't call the original implementation of an overridden method unless you use method-swizzling (facilitated by runtime functions like method_exchangeImplementations). I recommend against doing this anyway; it's really frightening and dangerous.
Update: Explanation of Instance Variables in Class Extensions
A class extension is like a category, but it is anonymous and must be placed within the .m file associated with the original class. It looks like:
#interface SomeClass () {
// any extra instance variables you wish to add
}
#property (nonatomic, copy) NSString *aProperty;
#end
Its implementation must be in the main #implementation block for your class. Thus:
#implementation SomeClass
// synthesize any properties from the original interface
#synthesize aProperty;
// this will synthesize an instance variable and accessors for aProperty,
// which was declared in the class extension.
- (void)dealloc {
[aProperty release];
// perform other memory management
[super dealloc];
}
#end
So, a class extension is useful for keeping private instance variables and methods out of the public interface, but will not help you add instance variables to a class over which you haven't control. There is no issue with overriding -dealloc, because you just implement it like you normally would, whilst including any necessary memory management for the instance variables you introduced within the class extension.
Please note that this stuff works only with the latest 64-bit Objective-C ABI.
As an aside, you can use associated references to "simulate the addition of object instance variables to an existing class".
Essentially, you can add an associated object as below:
static void* ASI_HTTP_REQUEST; // declare inside the category #implementation but outside any method
// And within a method, init perhaps
objc_setAssociatedObject(self,
&ASI_HTTP_REQUEST,
request,
OBJC_ASSOCIATION_RETAIN);
And release the associated object by sending 'nil':
// And release the associated object
objc_setAssociatedObject(self,
&ASI_HTTP_REQUEST,
nil,
OBJC_ASSOCIATION_RETAIN);
The Apple documentation is here.
It took me a while to find, so I hope that it helps someone.