Why isn't a setter synthesized for myString in the example below? The basic assignment below results in myString being nil. Trying to use the setter [self setMyString:s]; results in an unrecognized selector exception.
// in .h file
#interface MyClass
#property (nonatomic, readonly) NSString *myString;
#end
// in .m file
#interface MyClass (MyCategory)
#property (nonatomic, copy) NSString *myString;
#end
#implementation MyClass
#synthensize myString;
- (void) someMethod:(NSString *) s {
myString = [s copy];
// why is myString nil here?
}
#end
Edit: the problem was with gdb. po myString printed Can't print description of a NIL object.. However NSLog(#"myString: %#", myString); printed the expected value.
The other two answers are correct, but I think they miss your intention. It's common to declare a property as read-only in the .h file, so that code outside the class implementation can't write it. Inside the .m file, you want it to be readwrite. This kind of redefinition is explicitly supported. However, you need to put the redeclaration as readwrite in a class-extension:
// In your .h file:
#interface MyClass : NSObject
#property (nonatomic, copy, readonly) NSString *myString;
#end
// In your .m file:
#interface MyClass () // Note the empty parentheses
#property (nonatomic, copy, readwrite) NSString *myString;
#end
You do still need to use self.myString = aString or [self setMyString:aString], instead of writing to the ivar directly as you're doing right now.
It looks like you're trying to declare a publicly readonly, privately writable property. You should do that in a class extension rather than a category. Syntactically, a class extension looks like a category with no name:
#interface MyClass ()
#property (nonatomic, copy) NSString *myString;
#end
Declaring a property with the same name in both your MyClass interface and your MyCategory category seems like a bad idea. Remove the declaration in the category and I expect all will be well.
Related
Right before my model class sends the variable stringToDisplay, NSLog shows me that it has a value. But when I try to use it in my ViewController, I just get (null). Any thoughts about what I'm doing wrong?
(The good news is that, while working on this, I had sort of a breakthrough in understanding how models and controllers relate to each other. I'm still a complete newbie, but I don't feel quite as lost as I did.)
Here's what I think is the relevant code:
CalculatorBrain.h
#import <Foundation/Foundation.h>
#interface CalculatorBrain : NSObject
#property (nonatomic) NSMutableString *stringToAdd;
#property (nonatomic,strong) NSString *stringForDisplay;
- (double)performOperation:(NSString *)operation withArray:(NSMutableArray *)particularStackYouNeedToPopOff;
CalculatorBrain.m
#implementation CalculatorBrain
#synthesize stringToAdd = _stringToAdd;
#synthesize stringForDisplay = _stringForDisplay;
#synthesize whatHappenedSinceLastClear = _whatHappenedSinceLastClear;
- (double)performOperation:(NSString *)operation withArray:(NSMutableArray *)particularStackYouNeedToPopOff
{
<long code that I think doesn't matter because this NSLog produces exactly what I want it to:>
NSLog(#"%#",stringForDisplay);
return result;
}
CalculatorViewController.h
#import <UIKit/UIKit.h>
#interface CalculatorViewController : UIViewController
#property (nonatomic) NSArray *arrayOfDictionaries;
#property (nonatomic) NSDictionary *dictionary;
#property (weak, nonatomic) IBOutlet UILabel *variablesUsed;
#property (nonatomic, strong) NSString *operation;
#end
CalculatorViewController.m
#import "CalculatorViewController.h"
#import "CalculatorBrain.h"
#interface CalculatorViewController ()
#property (nonatomic,strong) CalculatorBrain *brain;
#end
#implementation CalculatorViewController
#synthesize display = _display;
#synthesize history = _history;
#synthesize brain = _brain;
#synthesize operation = _operation;
- (IBAction)operationPressed:(UIButton *)sender
{
NSString *otherString=[self.brain stringForDisplay];
if (self.userIsEnteringNumber) [self enterPressed];
NSString *operation = sender.currentTitle;
double result = [self.brain performOperation:operation withArray:[self.brain whatHappenedSinceLastClear]];
self.display.text = [NSString stringWithFormat:#"%g",result];
self.history.text = otherString;
NSLog(#"%#",otherString);
}
And the NSLog in that last line of code give me (null).
Any thoughts?
Maybe I'm missing something but your property is declared in the class extension of CalculatorBrain so nobody outside CalculatorBrain.m knows about this property.
So if you want to expose this property to other objects, you will have to declare it in CalculatorBrain.h instead.
Oh - your declaration of the property whatHappenedSinceLastClear isn't exposed to other classes that import CalculatorBrain.h because you put the property declaration in an interface extension in the .m file, which other classes will not see.
To make it publicly accessible move the #property line for whatHappenedSinceLastClear to CalculatorBrain.h, not the .m file.
I can guess that problem lies in the way you assign your stringForDisplay, eg.:
if you use something like
stringForDisplay_ = anotherString;
setter for property doesn't fire, so you have to retain your variable yourself otherwise it'll live just until your method finishes;
If so - use property setters, eg.:
self.stringForDisplay = anotherString;
that way ARC will do all the memory management.
It really depends how you set stringForDisplay inside the performOperation:withArray: method.
for a blind guess, try using
NSString *otherString = self.brain.stringForDisplay;
after this line
double result = [self.brain performOperation:operation withArray:[self.brain whatHappenedSinceLastClear]];
I've just starting out with obj-c and I created 2 files, a .h and a .m file. The .h file is..
#import <Foundation/Foundation.h>
#interface CardUnit : NSObject
{
#private
NSString *_name;
NSString *_gold;
}
#property (nonatomic, assign) NSString *name;
#property (nonatomic, assign) NSString *gold;
#end
and the .m file is
#import "CardUnit.h"
#implementation CardUnit
#synthesize gold = _gold;
#synthesize name = _name;
#end
But it's giving me 2 errors on the #synthesize lines, which are...
"Existing ivar "_gold" for property gold with assign attribute must be __unsafe retained" and the same for name.
From the error i see you are using ARC, Automatic Reference Counting .
Basically you can get rid of all the #synthesize statements and even the private declarations of the ivar's name and gold is not necessary.
All you need is the CardUnit.h to be like this :
#interface CardUnit : NSObject
#property (assign) NSString *name;
#property (assign) NSString *gold;
#end
The Xcode compiler will take care of the rest.
See also this reply on SO
In the superclass MyClass:
#interface MyClass : NSObject
#property (nonatomic, strong, readonly) NSString *pString;
#end
#implementation MyClass
#synthesize pString = _pString;
#end
In the subclass MySubclass
#interface MySubclass : MyClass
#end
#implementation MySubclass
- (id)init {
if (self = [super init]) {
_pString = #"Some string";
}
return self;
}
The problem is that the compiler doesn't think that _pString is a member of MySubclass, but I have no problem accessing it in MyClass.
What am I missing?
The instance variable _pString produced by #synthesize is private to MyClass. You need to make it protected in order for MySubclass to be able to access it.
Add an ivar declaration for _pString in the #protected section of MyClass, like this:
#interface MyClass : NSObject {
#protected
NSString *_pString;
}
#property (nonatomic, strong, readonly) NSString *pString;
#end
Now synthesize the accessors as usual, and your variable will become accessible to your subclass.
I am familiar with this problem. You synthesize the variable in your .m class, so it is not imported along with the header since the _pString variable will be created as part of the implementation, and not the interface. The solution is to declare _pString in your header interface and then synthesize it anyway (it will use the existing variable instead of creating a private one).
#interface MyClass : NSObject
{
NSString *_pString; //Don't worry, it will not be public
}
#property (nonatomic, strong, readonly) NSString *pString;
#end
The given answer works perfectly fine. This is an alternative answer, that apparently Apple likes a bit more.
You can define a private extension of your class, a MyClass+Protected.h file, which needs to be included in MyClass.m and MySubclass.m.
Then, in this new file, you redefine the property as readwrite.
#interface MyClass ()
#property (strong, readwrite) NSString * pString;
#end
This alternative allows you to use the accessor self.pString rather than the ivar _pString.
Note: you still need to keep the definition of pString in your MyClass.h as is.
I've defined a class where I'd like a public property to appear as though it is backed by an NSArray. That is simple enough, but in my case the actual backing ivar is an NSMutableArray:
#interface Foo
{
NSMutableArray* array;
}
#property (nonatomic, retain) NSArray* array;
#end
In my implementation file (*.m) I #synthesize the property but I immediately run into warnings because using self.words is the same as trying to modifying an NSArray.
What is the correct way to do this?
Thanks!
I would declare a readonly NSArray in your header and override the getter for that array to return a copy of a private NSMutableArray declared in your implementation. Consider the following.
Foo.h
#interface Foo
#property (nonatomic, retain, readonly) NSArray *array;
#end
Foo.m
#interface Foo ()
#property (nonatomic, retain) NSMutableArray *mutableArray
#end
#pragma mark -
#implementation Foo
#synthesize mutableArray;
- (NSArray *)array
{
return [[self.mutableArray copy] autorelease];
}
#end
Basically, put the NSArray property in a category in your header file and the NSMutableArray property in the class extension in your implementation file. Like so...
Foo.h:
#interface Foo
#end
#interface Foo (Collections)
#property (nonatomic, readonly, strong) NSArray *someArray;
#end
Foo.m
#interface Foo ()
#property (nonatomic, readwrite, strong) NSMutableArray *someArray;
#end
Simple:
1) Don't use a property when it ain't one.
2) Code simplifies to:
- (NSArray *)currentArray {
return [NSArray arraywithArray:mutableArray]; // need the arrayWithArray - otherwise the caller could be in for surprise when the supposedly unchanging array changes while he is using it.
}
- (void)setArray:(NSArray *)array {
[mutableArray setArray:array];
}
When the object is alloced create the array, when it dies, dealloc the array.
When large effects happen at the mere use of a '.' operator, its easy to overlook hugely inefficient code. Accessors are just that. Also - if someone calls aFoo.array - the contract is to get access to foo's array members - but really its just a copy at the time of the call. The difference is real enough that it caused bugs in the other implentations posted here.
Update: this answer is not valid anymore. Use one of suggested solutions below.
These days you can do the following:
Foo.m:
#implementation Foo {
NSMutableArray* _array;
}
#end
Foo.h:
#interface Foo
#property (readonly, strong) NSArray* array;
#end
You can still address mutable _array by ivar from the inside of implementation and outside it will be accessible via immutable property. Unfortunately this doesn't guarantee that others can't cast it to NSMutableArray and modify. For better protection from idiots you must define accessor method and return immutable copy, however that might be very expensive in some cases.
I would actually agree with one of the comments above that it's better to use simple accessor methods if you need to return some read-only data, it's definitely less ambiguous.
That's because your property must match the actual ivar's class type.
A possible solution/workaround:
//Foo.h:
#interface Foo
{
NSMutableArray* mutableArray;
}
#property (readwrite, nonatomic, retain) NSArray* array;
//or manual accessor declarations, in case you're picky about wrapper-properties.
#end
//Foo.m:
#interface Foo ()
#property (readwrite, nonatomic, retain) NSMutableArray* mutableArray;
#end
#implementation
#synthesize mutableArray;
#dynamic array;
- (NSArray *)array {
return [NSArray arrayWithArray:self.mutableArray];
}
- (void)setArray:(NSArray *)array {
self.mutableArray = [NSMutableArray arrayWithArray:array];
}
#end
You're adding a private mutableArray property in a class extension and making the public array simply forward to your private mutable one.
With the most recent language extensions of ObjC I tend to remove the
{
NSMutableArray* mutableArray;
}
ivar block entirely, if possible.
And define the ivar thru the synthesization, as such:
#synthesize mutableArray = _mutableArray;
which will generate a NSMutableArray *_mutableArray; instance for you.
Simplest answer: your property type (NSArray) doesn't match your instance variable type (NSMutableArray).
This is yet another good reason that you shouldn't define your own backing variables. Let #synthesize set up your instance variables; don't do it by hand.
I have two classes, Class A and Class B, both of them are subclasses of UIViewController.
I class A I have an NSString and I want to use this NSString in class B.
ClassA.h:
#class ClassB;
#interface ClassA : UIViewController {
ClassB *classB;
NSString stringA;
}
#property (nonatomic, retain) ClassB *classB;
#property (nonatomic, retain) NSString *stringA;
#end
ClassA.m:
stringA = [NSString stringWithString:webView.request.URL.absoluteString];
ClassB.h:
#class ClassA;
#interface ClassA : UIViewController {
ClassB *classA;
NSString stringB;
}
#property (nonatomic, retain) ClassB *classA;
#property (nonatomic, retain) NSString *stringB;
#end
ClassB.m:
- (void)viewWillAppear:(BOOL)animated {
self.stringB = classA.stringA;
}
Of course I did #import for both classes.
For some reason I always get NULL I classB for stringB.
Thanks!
The following aren't clear:
is mainViewController actually an instance of ClassA?
is classA even an instance of ClassA, as you've declared it an instance of ClassB?
what is your real code, as the things you've pasted here don't compile?
when in the ClassA object's lifecycle do you initialise stringA?
did that occur before you tried to use it in your ClassB object?
I would like to comment one thing you have high probability of RetainLoop, while ClassA retains ClassB and ClassB retains ClassA. When do they release?
Second thing, in:
ClassA.m:
stringA = [NSString stringWithString:webView.request.URL.absoluteString];
change to:
self.stringA = [NSString stringWithString:webView.request.URL.absoluteString];
while object returned by [NSString stringWithString:] is set to autorelease, and you need to retain it to be sure that you have valid instance of string.
Please provide more code.