I have a doubt regarding property redeclaration
Overview:
Points to note: - Memory management = ARC (Automatic Reference Counting)
Question:
Code: (in separate files)
A.h
#import<Foundation/Foundation.h>
@interface A : NSObject
@property (readonly) int n1;
- (void) display;
@end
A.m
#import "A.h"
@implementation A
@synthesize n1 = _n1;
- (void) display
{
printf("_n1 = %i\n", _n1); //I expected _n1 and self.n1 to display the same value
printf("self.n1 = %i\n\n", self.n1); //but they seem to display different values
}
@end
B.h
#import"A.h"
@interface B : A
@property (readwrite) int n1;
@end
B.m
#import"B.h"
@implementation B
@synthesize n1 = _n1;
@end
test.m
#import"B.h"
int main()
{
system("clear");
B* b1 = [[B alloc] init];
b1.n1 = 20;
[b1 display]; //Doubt - my expected behavior is different from actual behavior
return(0);
}
Expected Behavior:
_n1 = 20
self.n1 = 20
Actual Behavior:
_n1 = 0
self.n1 = 20
There are two ways of going about this. In neither case do you call @synthesize
in the subclass. I'm surprised that compiles for you. I would expect an error like "Property 'n1' attempting to use ivar '_n1' declared in superclass 'A'". In any case it's definitely not something you can really do, which is why you're seeing strange behavior. (I remembered why you aren't seeing this error; it's because of the separate compile units. You're just winding up with different ivars.)
First, you need to understand @dyanmic
. This is a way of telling the compiler "yes, I know you don't see an implementation for the required method here; I promise it'll be there at runtime." In the subclass, you will use @dynamic
to let the compiler know that it's ok to inherit n1
.
@implementation B
@dynamic n1;
@end
Now, you need to provide the setN1:
method. IMO, subclasses shouldn't go messing with their superclass's ivars, so I approve of the fact that synthesized ivars are marked @private
. In a second, I'll tell you how to undo that, but for now let's deal with my preferred solution:
setN1:
as a private method in A
.B
.A.h
@interface A : NSObject
@property (readonly) int n1;
- (void) display;
@end
A.m
#import "A.h"
@interface A () // Private class extension, causes setN1: to be created but not exposed.
@property (readwrite) int n1;
@end
@implementation A
@synthesize n1 = _n1;
- (void) display {
...
}
@end
B.h
#import "A.h"
@interface B : A
@property (readwrite) int n1; // Tell the world about setN1:
@end
B.m
#import "B.h"
@implementation B
@dynamic n1; // Yes compiler, setN1: exists. I promise.
@end
Now, some people think it's fine for subclasses to mess with their superclass's ivars. Those people are wrong (ok, IMHO...) but it is possible in ObjC. You just need to declare the ivar @protected
. This is the default when you declare ivars directly in the @interface
(one of many reasons you shouldn't do this anymore). It would look like this:
A.h
@interface A : NSObject {
int _n1;
}
...
A.m -- remove the extra class extension that makes n1 writable in the superclass.
B.h -- no change
B.m
@implementation B
@dynamic n1;
- (void)setN1:(int)n1 {
_n1 = n1;
}
@end
I copied your code and verified the behavior that you are getting. I can explain the mechanics of it, but not the logic behind it.
Here is what's going on: each of the two @synthesize
directives produces a hidden variable _n
in its corresponding class. In addition, the directive synthesizes a getter for n1
in A
, and a getter/setter pair in B
. The getter of n1
in B
overrides the getter of n1
in A
; the setter does not, because there is nothing to override.
At this point, A
's _n1
in B
becomes orphaned: neither the getter of n1
nor its setter reference it. The setter references B
's _n1
, not A
's. That's why you are seeing different values printed in the display
method of A
. Putting the method in B
behaves the way that you would expect.
EDIT:
Naturally, the next question is how to make the behavior that you want. It turns out to be simple: do not synthesize the property in B
, and implement a setter of _n1
in A
's implementation file (without putting it in the interface, so that it remains read-only to the clients of your interface).
// This goes in A.m without a declaration in A.h
- (void) setN1:(int)n1 {
_n1 = n1;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With