I'm having some issues trying to override a property in a protocol to make it mutable.
I have this protocol:
#protocol TheProtocol
#property (nonatomic, readonly) NSString *someString;
#end
And this class:
#interface SuperObject : NSObject <TheProtocol>
#end
Which synthesizes the variable like so:
#implementation SuperObject
#synthesize someString;
-(id)init {
if(self=[super init]) {
someString = [aString copy];
}
return self;
}
#end
and can thus write to it internally
I then have a subclass:
#interface SubObject : SuperObject
#end
#implementation SubObject
#synthesize someString;
- (id)init {
if(self=[super init]) {
NSLog(#"Some string is %#",someString");
someString = [bString copy];
}
return self;
}
In my subclass, trying to assign to someString doesn't work. I tried also synthesizing someString in my subclass but before I try and modify it, when I print out "someString", it prints nil instead of "something"
Answered
I figured out the answer. Ultimately what worked is this:
#interface SuperObject : NSObject <TheProtocol> {
#protected
NSString *someString;
}
#end
What is happening is that when you synthesize in your subclass, that is allocating a different ivar than the one in your super class. Therefore the value when printed is nil. You can refer to the original ivar by doing self.someString.
Hope that helps.
Related
I want to expose an NSArray to my user (and I want them to only read it), but in my class, I want to use an NSMutableArray.
I tried the following code and it does not raise any warning:
// In the .h
#interface MyClass : NSObject <NSApplicationDelegate>
#property (nonatomic, readonly) NSArray * test ;
#end
and
// In the .m
#interface MyClass ()
#property (nonatomic, strong, readwrite) NSMutableArray * test ;
#end
#implementation MyClass
- (id)init
{
self = [super init];
if (self)
{
self.test = [[NSMutableArray alloc] init] ;
}
return self;
}
#end
But, if I try to access the #property test from within my class, I can use the method addObject:. So, I guess what precedes is not possible.
Why is there no warning as it is?
I don't think that mixing property type would be a good practice. Instead I would create an accessor that returns a copy of the private mutable array. This is more conventional. Please note, don't use self for property access in your -init: method:
// In the .h
#interface MyClass : NSObject <NSApplicationDelegate>
- (NSArray *)test;
#end
// In the .m
#interface MyClass ()
#property (nonatomic, strong) NSMutableArray *aTest;
#end
#implementation MyClass
- (id)init
{
self = [super init];
if (self)
{
_aTest = [[NSMutableArray alloc] init] ;
}
return self;
}
- (NSArray *)test
{
return [self.aTest copy];
}
#end
The #property is just syntax sugar which automatically creates getter/setter methods for you. With the readonly in the .h file only the getter method will be created for the public but by overriding it in the .m file you get both methods in your implementation.
readwrite is the default (see here) so even if leave out readwrite put still have the #property in you implementation file you will get a setter method. It is good practice to explicitly write readwrite then in your .m file so you and other will get a hint that this variable might only be declared read only in the .h file.
just wanted to ask where I define initial class properties?
From other languages I am used to define some standard properties in the head before the content of the class starts.
For example paths to files. Settings and so on.
Where I fill these initial properties with values in Objective-C?
Thanks
Generally it's something like:
MyClass.h:
extern NSString * const staticValue1;
extern NSString * const staticValue2;
#interface MyClass : NSObject
{
NSString *_strval;
int _intval;
float _fltval;
}
#property (retain, nonatomic, readwrite) NSString *strval;
#property (assign, nonatomic, readwrite) int intval;
#property (assign, nonatomic, readwrite) float fltval;
#end
MyClass.m:
NSString * const staticValue1 = #"Something";
NSString * const staticValue2 = #"Something else";
#interface MyClass
#synthesize strval = _strval;
#synthesize intval = _intval;
#synthesize fltval = _fltval;
- (id)init
{
self = [super init];
if (self != nil)
{
[self setStrval:[NSString stringWithFormat:#"This is a %#", #"string"]];
[self setIntval:10];
[self setFltval:123.45f];
}
return self;
}
- (void)dealloc
{
[self setStrval:nil];
[super dealloc];
}
#end
This demonstrates the use of synthesized properties which are being used here to manage the memory of the instance variable _strval, which requires retaining/releasing to avoid memory leaks. Note that [self setStrval] is initialised with an autoreleased object (from [NSString stringWithFormat) and will be retained by the setter method. Alternatively these methods can be called using the following syntax, if you prefer:
self.strval = [NSString stringWithFormat:#"This is a %#", #"string"];
self.intval = 10;
self.fltval = 123.45f;
Maybe some of what you are after can be implemented with class methods.
Class methods are coded with a + (instead of the instance methods' -), and can't refer to instance variables, as they are not associated with any specific instance of the class.
This is a class method to return a default string:
+ (NSString *)myDefaultString
{
return #"Some default value";
}
You call it by simply calling it with the class name at the receiver's place. Imagine you have defined the method in a class called MyClass, the you call it like this:
NSString *str = [MyClass myDefaultString];
You'll notice that there is no alloc/init calls in this.
Public property needs to be define in .h file.
#interface MyClass {
}
#property(nonatomic, reatin) NSString *a;//Define as per needs, then synthesise in .m file
#end
For private property you need define inline category in .m file-
#interface MyClass ()
#property(nonatomic, reatin) NSString *b;//Define as per needs, then synthesise in .m file
#end
#implementation MyClass
#synthesize a = _a;
#synthesize b = _b;
- (void)viewDidLoad {
//You can initialise property here or in init method
self.a = #"Demo1";
self.b = #"Demo2";
}
//Now you can have other code for this class.
#end
The code is probably the best way to see what I am trying to do:
AcInfo.h:
#interface AcInfo : NSManagedObject {
#private
}
#property (nonatomic, retain) NSString *registrationNumber;
#end
AcInfo.m:
#implementation AcInfo
#dynamic registrationNumber;
#end
AnotherClass.h:
#interface AnotherClass : NSObject {
}
#property (nonatomic, retain) AcInfo *detailItem;
#property (nonatomic, retain) IBOutlet UITextField *registrationNumberTextField;
- (void)setDetailItemValueFromUIElement:(id *)uiElement forAcInfoTarget:(id *)acInfoTarget;
#end
AnotherClass.m:
#import "AcInfo.h"
#implementation AnotherClass
#synthesize detailItem, registrationNumberTextField;
- (void)viewDidLoad
{
[super viewDidLoad];
registrationNumberTextField.text = #"Test";
// I expect this to set detailItem.registrationNumber to the value of
// registrationNumberTextField.text (Test) but it doesn't change anything!
setDetailItemValueFromUIElement:registrationNumberTextField forAcInfoTarget:detailItem.registrationNumber;
}
- (void)setDetailItemValueFromUIElement:(id *)uiElement forAcInfoTarget:(id *)acInfoTarget
{
if ([(id)uiElement isKindOfClass:[UITextField class]]) {
// This doesn't do anything when it returns!
(NSString *)acInfoTarget = (UITextField *)uiElement.text
return;
}
}
#end
In short, I want acInfoTarget to call the getter [detailObject registrationNumber] and the setter [detailObject setRegistrationNumber] in the setDetailItemValueFromUIElement: function...
You can set or read properties by name using
// setter
NSString *propertyName = #"myProperty";
[object setValue:newValue forKey:propertyName];
// getter
id value = [object valueForKey:propertyName];
This is slower than using the normal dot notation, though, and it's frequently (though not always) a sign of poorly-designed code.
Also note that id is a pointer type, so you probably don't actually mean "(id*)".
Your code wants to look something like this, I think:
- (void)setDetailItemValueFromUIElement:(id)uiElement forAcInfoTarget:(NSString*)acInfoTarget {
if ([(id)uiElement isKindOfClass:[UITextField class]]) {
NSString *newValue = ((UITextField*)uiElement).text;
[self.detailItem setValue:newValue forKey:acInfoTarget];
}
}
Properties are just syntax sugar for a couple of accessor methods. They are not, in essence, variables so you shouldn't treat them as such. If you want to affect a property, then what you wanting to do is call a method. So you should pass a id and selector parameter and not pointer to a variable type.
I am trying to write a cocoa touch static library.
To keep it simple I would prefer not to use private variables within my interface file.
The code right now looks like this:
interface file (myView.h):
#interface myView: UIView {
NSTimer * myTimer;
}
#end
implementation file (myView.h)
#implementation myView
#end
This NSTimer pointer is just a private variable so I tried this:
(not working)
interface file (myView.h):
#interface myView: UIView {
}
#end
implementation file (myView.h)
NSTimer * myTimer;
#implementation myView
#end
It seems to work however it turned out that the timer is now a static variable.
Am I doing sth wrong or is there no solution?
You can't define instance variables in your implementation file.
A possible solution is to have a private structure containing the private variables and have one publicly declared private variable pointing to this private structure:
#interface MyView {
void *privateData;
}
Implementation file:
typedef struct {
NSTimer *myTimer;
} PrivateData;
#implementation MyView()
#property (readonly) PrivateData *privateData;
#end
#implementation MyView
- (id) init {
if (self = [super init]) {
privateData = malloc(sizeof(PrivateData));
self.privateData->myTimer = nil; // or something else
}
return self;
}
-(PrivateData *) privateData {
return (PrivateData *) self->privateData;
}
- (void) myMethod {
NSTimer *timer = self.privateData->myTimer;
}
- (void) dealloc {
// release stuff inside PrivateData
free(privateData);
[super dealloc];
}
#end
It's not beautiful, but it works. Maybe there are better solutions.
Just a note; trying to hide iVar's for the sake of security is silly. Don't bother.
For simplicity's sake, though, it has value.
However, a couple of solutions:
(1) If targeting iPhone OS or 64 bit Cocoa, you can #synthesize the ivar:
Foo.h:
#interface Foo:NSObject
#property(readwrite, copy) NSString *publiclyReadwriteNoiVar;
#property(readonly, copy) NSString *publiclyReadonlyPrivatelyReadwriteNoiVar;
#end
Foo.m:
#interface Foo()
#property(readwrite, copy) NSString *privateProperty;
#end
#implementation Foo
#synthesize publiclyReadwriteNoiVar, publiclyReadonlyPrivatelyReadwriteNoiVar, privateProperty;
#end
(2) Use a private subclass kinda like class clusters:
Foo.h:
#interface Foo:NSObject
#end
Foo.m:
#interface RealFoo:Foo
{
.... ivars here ....
}
#end
#implementation RealFoo
#end
#implementation Foo
+ (Foo *) convenienceMethodThatCreatesFoo
{
.... realFoo = [[RealFoo alloc] init]; ....
return realFoo;
}
#end
Depending on the goal of your encapsulation, there's also the #private directive:
Access Modifiers
Given the following definition of a class with retain properties:
#interface FeedEntry : NSObject<NSCoding>
{
NSURL* url;
NSData* source;
}
#property (retain) NSURL* url;
#property (retain) NSData* source;
#end
#implementation FeedEntry
#synthesize url;
#synthesize source;
-(void)encodeWithCoder:(NSCoder*)coder
{
[coder encodeObject:url forKey:#"url"];
[coder encodeObject:source forKey:#"source"];
}
Why does the url property in initWithCoder method need the "retain":
-(id)initWithCoder:(NSCoder*)coder
{
url = [[coder decodeObjectForKey:#"url"] retain];
source = [coder decodeObjectForKey:#"source"];
NSLog(#"got url=%#\n", url);
return self;
}
Specifically, why doesn't the synthesized "get url" method retain the object? (I'm guessing the source property will need a retain as well).
Quick answer:
When you set:
url = [[coder decodeObjectForKey:#"url"] retain];
you are not using the #property. You are manually setting the value of the instance variable url. You must, therefore, also manually retain the value.
To set the variable using the synthesized properties, you would instead call:
[self setUrl:[coder decodeObjectForKey:#"url"]];
or
self.url = [coder decodeObjectForKey:#"url"];
Either of these forms would make use of the synthesized methods, and handle the retain automatically.
Details:
In Objective-C, the #property and #synthesize keywords automatically create the getter and setter methods for you:
#interface MyClass
{
id someValue;
}
#property (retain) id someValue;
#end
#implementation MyClass
#synthesize someValue;
#end
Is equivalent to:
#interface MyClass
{
id someValue;
}
- (id)someValue;
- (void)setSomeValue:(id)newValue;
#end
#implementation MyClass
- (id)someValue { return someValue; }
- (void)setSomeValue:(id)newValue
{
[newValue retain];
[someValue release];
someValue = newValue;
}
#end
This creates an important distinction between the "internal" member variable and the property having the same name. If you reference the member variable by name, you are bypassing the synthesized property methods.