Why do I have to use self. to reference own object - ios

I'm learning iOS development through Stanford's iTunesU program. I am stuck on an unexpected problem I am having.
I have added a clear method, but I am getting this error
//Use of undeclared identifier 'operandStack'; did you mean '_operandStack'?
I know I can fix the problem by using [self.operandStack ...etc instead of [operandStack
Why do I need self? Isn't it implied? Why do I not need to use self when referencing _operandStack?
#import "CalculatorBrain.h"
#interface CalculatorBrain()
//string because we are the only ones interested
#property (nonatomic, strong) NSMutableArray *operandStack;
#end
#implementation CalculatorBrain
#synthesize operandStack = _operandStack;
- (void) setOperandStack:(NSMutableArray *)operandStack
{
_operandStack = operandStack;
}
- (NSMutableArray *) operandStack
{
if(_operandStack==nil) _operandStack = [[NSMutableArray alloc] init];
return _operandStack;
}
- (void) pushOperand:(double)operand
{
NSNumber *operandObject = [NSNumber numberWithDouble:operand];
[self.operandStack addObject:operandObject];
}
- (double) popOperand
{
NSNumber *operandObject = [self.operandStack lastObject];
if (operandObject !=nil)
{
[self.operandStack removeLastObject];
}
return [operandObject doubleValue];
}
- (void) clear
{
//clear everything
[operandStack removeAllObjects];
//***************************
//Use of undeclared identifier 'operandStack'; did you mean '_operandStack'?
}
- (double) performOperation:(NSString *)operation
{
double result =0;
//calculate result
if ([operation isEqualToString:#"+"]) {
result = [self popOperand] + [self popOperand];
} else if ([operation isEqualToString:#"*"]) {
result = [self popOperand] * [self popOperand];
} else if ([operation isEqualToString:#"π"]) {
[self pushOperand:3.14159];
NSNumber *operandObject = [self.operandStack lastObject];
return [operandObject doubleValue];
}
[self pushOperand:result];
return result;
}
#end

Because you have synthesized it (note: in newer Objective-C version the synthesis is automatic) :
#synthesize operandStack = _operandStack;
It means that you generated getter and setter, and that you access the property by calling it _operandStack. If you want to call it operantStack change it to that:
#synthesize operandStack;
If instead you use self.operandStack, you are using the getter/setter generated by the property, not the synthesized one.
Using synthesized and not synthesized properties is different, there isn't a "recommended way" of accessing properties like many people think, they just have different meaning. For example here:
- (void) setOperandStack:(NSMutableArray *)operandStack
{
_operandStack = operandStack;
}
You must use the synthesized property, otherwise you go into an infinite loop. The synthesized property is automatically generated, the non synthesized property is also automatically generated but it can be overridden, like you did in that case, and it can be also accessed externally.

Related

NSString : leak when assigning value to a property

Assuming we don't use ARC.
Suppose we have a very simple class in which we declare 2 NSString properties, like this :
#interface Foo : UIView {}
-(id)initWithArguments:(NSString*)mess title:(NSString*)tit;
#property(nonatomic, retain) NSString *message;
#property(nonatomic, retain) NSString *title;
#end
and in implementation :
#implementation Foo
#synthesize message, title;
-(id)initWithArguments:(NSString*)mess title:(NSString*)tit{
if((self = [super init])){
message = mess; // (1)
self.title = tit; // (2)
(...)
}
return self;
}
-(void)dealloc{
message = nil;
title = nil;
[super dealloc];
}
#end
Now if I call a method from another class, in which I create 2 NSString and an instance of Foo , like this :
-(void)someMethod{
NSString *string1 = [NSString stringWithFormat:#"some text with %d things", 5];
NSString *string2 = [NSString stringWithFormat:#"other text with %d things", 5];
Foo *foo = [[Foo alloc] initWithArguments:string1 title:string2];
}
The whole code works fine and doesn't crash, but, if I profile it with instruments,
it doesn't cause a leak when calling (1)("message = mess;")
it cause a leak when calling (2)("self.title = tit;")
It's very confusing, because stringWithFormat is an autoreleased object, isn't it ?
So, how an autoreleased object can cause a leak when assigning to a property ???
I read somewhere that it's almost always better to use the "self.text = value;" form instead of the "text = value;" form, because the second one may cause a leak.
Actually, in this code it's the contrary.
And... If I use a constant NSString like #"some text", instead of the values returned by [NSString stringWithFormat], there is no leak, of course.
Any idea ?
You have forgotten to invoke the (compiler-generated) setter methods in a few cases:
self.message = mess; // in init method
self.message = nil; // in dealloc method
self.title = nil; // ditto
It's crucial that you use the setter/getter methods in non-ARC code.

Using NSMutableArray to keep Objects

I think that I'm missing some fundamental knowledge on Xcode Objective C programming standards. Unfortunately I couldn't find the appropriate solution to my problem.
The problem is that when I try to keep data in an array of objects it becomes impossible to keep them separately. Adding new objects overwrites the previous objects in array. Here is some code about that:
CustomObject.m file:
#import "CustomObject.h"
NSString * title;
NSString * detail;
#implementation CustomObject
- (void) initCustomObjectWithValues : (NSString *) iTitle : (NSString *) iDetail {
title = [NSString stringWithString:iTitle];
detail = [NSString stringWithString:iDetail];
}
- (NSString *) getTitle {
return title;
}
- (NSString *) getDetail {
return detail;
}
#end
viewDidLoad function in ViewController.m file:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
myMutableArray = [[NSMutableArray alloc] init];
for (int i=0; i<10; i++) {
NSString * tempTitle = [#"title " stringByAppendingString:[NSString stringWithFormat:#"%d",i]];
CustomObject * myCustomObject = [[CustomObject alloc] init];
[myCustomObject initCustomObjectWithValues :[NSString stringWithFormat:#"%#",tempTitle]
:[#"detail " stringByAppendingString:[NSString stringWithFormat:#"%d",i]]];
[myMutableArray addObject:myCustomObject];
}
for (int i=0; i<10; i++) {
NSLog(#"%#",[[myMutableArray objectAtIndex:i] getTitle]);
NSLog(#"%#",[[myMutableArray objectAtIndex:i] getDetail]);
NSLog(#"----------------------------");
}
}
Here, myMutableArray is defined at the top of the ViewController.m file. (To make it global and can be used in other functions in future)
Here what I've got in the logs:
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
title 9
detail 9
----------------------------
As far as I understand each new added object overwrites the olds. First I thought that they are referring to same allocated memory but in debug tool myMutableArray seems like this:
Printing description of myMutableArray:
<__NSArrayM 0x8d8cb60>(
<CustomObject: 0x8d8e990>,
<CustomObject: 0x8d8dd40>,
<CustomObject: 0x8d8d2e0>,
<CustomObject: 0x8d8d470>,
<CustomObject: 0x8d8d350>,
<CustomObject: 0x8d8ddf0>,
<CustomObject: 0x8d8df00>,
<CustomObject: 0x8d8df40>,
<CustomObject: 0x8d8dff0>,
<CustomObject: 0x8d8e0c0>
)
Does anyone have an idea about the solution. It should be something very basic but I can't catch the problem.
Thank you all in advance
using
NSString * title;
NSString * detail;
outside of the #interface part creates global variables. When you assign a variable to title or detail you don't set an instance variable of your object, you change those global variables. And since they are global, they are the same for all objects that reference them.
Turn those global variables into instance variables, or even better use #property.
Your code is bad objective-c overall.
You should not use get in getters that return variables. You should not have methods that start with init and don't return self. You should only call init in [[Foo alloc] init...] situations. You should avoid unnamed parameters in your methods.
And there is no need to create strings from strings from strings.
Here is how I would write it:
// CustomObject.h
#interface CustomObject : NSObject
#property (copy, nonatomic) NSString * title;
#property (copy, nonatomic) NSString * detail;
- (id)initWithTitle:(NSString *)title detail:(NSString *)detail
#end
// CustomObject.m
#import "CustomObject.h"
#implementation CustomObject
- (id)initWithTitle:(NSString *)title detail:(NSString *)detail {
self = [super init];
if (self) {
// use stringWithString: to create #"" strings when title is nil
// if nil is a valid value for those variables you should use
// _title = [title copy];
_title = [NSString stringWithString:title];
_detail = [NSString stringWithString:detail];
}
return self;
}
#end
for (int i=0; i<10; i++) {
NSString *tempTitle = [NSString stringWithFormat:#"title %d",i];
NSString *tempDetail = [NSString stringWithFormat:#"detail %d",i];
CustomObject * myCustomObject = [[CustomObject alloc] initWithTitle:tempTitle detail:tempDetail];
[myMutableArray addObject:myCustomObject];
}
for (int i=0; i<10; i++) {
CustomObject *object = myMutableArray[i];
NSLog(#"%#", object.title);
// or NSLog(#"%#", [object title]); if you don't like dot-notation.
NSLog(#"%#", object.detail);
NSLog(#"----------------------------");
}

Core Data: Automatically Trim String Properties

For my Core Data NSManagedObject, I would like to ensure any NSString properties only contain strings that have been trimmed of whitespace.
I'm aware that I could achieve this by overriding each setter method, like so:
- (void)setSomeProperty:(NSString *)someProperty
{
someProperty = [someProperty stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ((!someProperty && !self.someProperty) || [someProperty isEqualToString:self.someProperty]) return;
[self willChangeValueForKey:#"someProperty"];
[self setPrimitiveValue:someProperty forKey:#"someProperty"];
[self didChangeValueForKey:#"someProperty"];
}
However, this seems like a lot of code to have to write, especially since my managed object is likely to have quite a few NSString properties.
Is there an easier way?
You could create a custom NSValueTransformer for NSString and assign all of your NSString properties to the new transformer in the model editor:
#interface StringTransformer: NSValueTransformer {}
#end
#implementation StringTransformer
+ (Class)transformedValueClass {
return [NSString class];
}
+ (BOOL)allowsReverseTransformation {
return YES;
}
- (id)transformedValue:(id)value {
return value;
}
- (id)reverseTransformedValue:(id)value {
return [value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
#end
If you only need to ensure that the saved data is trimmed then you can implement willSave and use changedValues to check only the changed values. This will also make it easy to do in a loop to minimise code duplication.
You could do it during property validation:
- (BOOL)validateSomeProperty:(id *)inOutValue error:(NSError **)error
{
if (inOutValue)
{
NSString *value = *inOutValue;
*inOutValue = [value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
return YES;
}
Core data will automatically call validateSomeProperty:error: before saving your record, so this will make sure that any data that gets saved is trimmed. It won't stop the on-change events firing if someone changes it from, say, foo to \n\nfoo\n\n, but it does mean that you don't have to fire them by hand.

No visible #interface for 'class-name' declares the selector 'method-name'

I'm writing a method as below in my View Controller:
- (IBAction)expressionEvaluation:(UIButton *)sender {
NSDictionary *testValues = [NSDictionary dictionaryWithObjectsAndKeys:#"x", 2, #"y", 3, #"z", 4, nil];
//the below line gives the error
double result = [self.brain evaluateExpression:self.brain.expression usingVariableValues:testValues];
NSString *resultString = [NSString stringWithFormat:#"%g", result];
self.display.text = resultString;
}
And in my 'brain' class I have declared the not-yet-finished method:
#import <Foundation/Foundation.h>
#interface CalculatorBrain : NSObject
- (void) pushOperand:(double)operand;
- (void) setVariableAsOperand:(NSString *)variableName;
- (void) performWaitingOperation;
- (double) performOperation:(NSString *)operation;
#property (readonly) id expression;
+ (double)evaluateExpression: (id)anExpression
usingVariableValues: (NSDictionary *)variables; //declared here
#end
...in the .h, and in the .m:
+ (double) evaluateExpression:(id)anExpression usingVariableValues:(NSDictionary *)variables {
double result = 0;
int count = [anExpression count];
for (int i = 0; i < count; i++) {
}
return result;
}
Why am I getting this "No visible #interface for 'CalculatorBrain' declares the selector 'evaluateExpression:usingVariableValues'"error? I'm new to objective-c, but I imagine this means it's not seeing my declaration. I'm not sure if it's a syntax/formatting issue, though, because I'm new to the language.
notice that you declare evaluateExpression:usingVariableValues: as class method
+ (double)evaluateExpression: (id)anExpression // + means class method
usingVariableValues: (NSDictionary *)variables; //declared here
and use it like instance method
// assuming self.brain is an instance of CalculatorBrain
[self.brain evaluateExpression:self.brain.expression usingVariableValues:testValues];
so either change the method to
- (double)evaluateExpression: (id)anExpression // - means instance method
usingVariableValues: (NSDictionary *)variables; //declared here
or call it like this
[CalculatorBrain evaluateExpression:self.brain.expression usingVariableValues:testValues];
the "+" sign means class method. You can access it through your class name not an instance of it.
like
[MyClass methodName];
a "-" sign means instance method. You can access it through an instance of your class (after having allocated-inited it.
like
MyClass *myInstance = [[MyClass alloc] init];
[myInstance methodName];
MOXY is right.
+ (double)evaluateExpression: (id)anExpression;
is a class method, and is sent to the class. You typically use class methods for things like constructing new objects:
+ (NSString*) stringWithString: (NSString*) s;
What you want is an instance method:
- (double) evaluateExpression: (id) anExpression;
which you would send to an instance on your object.

Usage of the "copy" property attribute to maintain an immutable NSString

I am very new to iOS development and programming in Objective-C. I have been doing the exercises on the app dev library.
This is the current exercise that I am trying to understand.
3. Test what happens if you set a mutable string as the person’s first name, then mutate that string before calling your modified sayHello method. Change the NSString property declarations by adding the copy attribute and test again.
I attempt to do this however, the NSString that I modify does in fact change despite the use of the copy property attribute.
Here are my declarations and implementations as well as my test code.
XYZPerson.h
#import <Foundation/Foundation.h>
#interface XYZPerson : NSObject
#property (copy) NSString *firstName;
#property NSString *lastName;
#property NSDate *dob;
- (void)sayHello;
- (void)saySomething:(NSString *)greeting;
+ (id)init;
+ (id)personWithFirstName:(NSString *)firstName lastName:(NSString *)lastName dob:(NSDate *)dateOfBirth;
#end
//XYZPerson.m
#import "XYZPerson.h"
#implementation XYZPerson
#synthesize firstName = _firstName;
#synthesize lastName = _lastName;
#synthesize dob = _dob;
- (void)sayHello {
[self saySomething:#"Hello World!"];
NSLog(#"This is %# %#", self.firstName, self.lastName);
}
- (void)saySomething:(NSString *)greeting {
NSLog(#"%#", greeting);
}
+ (id)init {
return [self personWithFirstName:#"Yorick" lastName:#"Robinson" dob:8/23/1990];
}
+ (id)personWithFirstName:(NSString *)firstName lastName:(NSString *)lastName dob:(NSDate *)dateOfBirth{
XYZPerson *person = [[self alloc] init];
person.firstName = firstName;
person.lastName = lastName;
person.dob = dateOfBirth;
return person;
}
#end
//Test code
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "XYZPerson.h"
#import "XYZShoutingPerson.h"
int main(int argc, char *argv[])
{
#autoreleasepool {
XYZPerson *guy = [XYZPerson init];
[guy sayHello];
//I thought that this change would never be made, but it is everytime I run the code.
guy.firstName = #"Darryl";
[guy sayHello];
XYZShoutingPerson *girl = [XYZShoutingPerson init];
[girl sayHello];
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
Consider this shorter example (which runs in CodeRunner btw):
#import <Foundation/Foundation.h>
#interface Person : NSObject
#property (nonatomic,strong) NSString *name; // strong should be copy
#end
#implementation Person
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
Person *p = [Person new];
NSMutableString *name = [[NSMutableString alloc] initWithString:#"Alice"];
p.name = name;
NSLog(#"%#",p.name); // prints Alice
[name appendString:#"xxx"];
NSLog(#"%#",p.name); // prints Alicexxx
}
}
I'm pointing the name to a mutable string, then appending some characters. As a result, the name has changed inside the object. However, if you replace strong with copy when declaring the property, a new immutable string will be created just for the Person object.
The moral of the story is, using copy prevents side effects when someone passes an object and then that object changes.
The message -[NSString copy] results in a copy when a mutable string is passed (NSMutableString) or retain when it is immutable (NSString). Therefore, always copy when declaring NSString properties:
#property (nonatomic,copy) NSString *string; // OK
#property (nonatomic,strong) NSString *string; // strong should be copy
I ran into this problem when I was doing the same book. I added copy and the exact same thing happened, it kept mutating when I appending something to the NSMutableString variable that I used for firstName. Then I read this section:
If you need to set a copy property’s instance variable directly, for example in an initializer method, don’t forget to set a copy of the original object:
-(id)initWithSomeOriginalString:(NSString *)aString {
self = [super init];
if (self) {
_instanceVariableForCopyProperty = [aString copy];
}
return self;
}
So, I went back into my XYZPerson.m and looked at my init code.
I changed:
- (id)initWithFirstName:(NSMutableString *)aFirstName lastName:(NSString *)aLastName
dateOfBirth:(NSDate *)aDate {
self = [super init];
if (self) {
_firstName = aFirstName;
_lastName = aLastName;
_dateOfBirth = aDate;
}
return self;
}
To:
- (id)initWithFirstName:(NSMutableString *)aFirstName lastName:(NSString *)aLastName
dateOfBirth:(NSDate *)aDate {
self = [super init];
if (self) {
_firstName = [aFirstName copy];
_lastName = aLastName;
_dateOfBirth = aDate;
}
return self;
}
And presto-chango: it worked the correct way! It made a copy of the NSMutableString that I had used that did not mutate when I appended something to the end of it before the method call.
I think you are misunderstanding of what copy does.
NSMutableString *string = [NSMutableString stringWithString:#"test"];
XYZPerson *guy = [XYZPerson init];
guy.firstName = string;
guy.lastName = string;
[string replaceCharactersInRange:NSMakeRange(1, 1) withString:#"x"];
[guy sayHello];
Output
This is test txst
In this example, firstName is copy do it doesn't change when string is changed, lastName is not copy so it value is changed when the mutable string string is changed.
What happened here is lastName and string are the same object, so when string is changed lastName is changed as a side effect. This is considered very bad and you never want this behavior. Using copy makes sure firstName and string are different objects and changes to string cannot effect firstName.

Resources