Objective C: How to Write a Instantiating Custom Init - objective-c

Very basic question, but I have an error in my code that can only be answered by one assumption: my class isn't being instantiated!
I haven't written much in Objective C in some time, and I was never really good, so please point out even the most painfully obvious.
I am using:
ObjectSelectionViewController *length = [[ObjectSelectionViewController alloc] initWithMeasureType:0];
ObjectSelectionViewController *mass = [[ObjectSelectionViewController alloc] initWithMeasureType:1];
ObjectSelectionViewController *volume = [[ObjectSelectionViewController alloc] initWithMeasureType:2];
NSLog(#"%#", [length measurementType]);
NSLog(#"%#", [mass measurementType]);
NSLog(#"%#", [volume measurementType]);
The NSLogs return whichever measurement was assigned last, regardless of the separate allocs and inits.
Here is the constructor of the ObjectSelectionViewController class:
#import "ObjectSelectionViewController.h"
#implementation ObjectSelectionViewController
NSString *measurementType;
-(ObjectSelectionViewController*) initWithMeasureType:(int)value
{
switch (value) {
case 0: // Length
measureType = #"Length";
break;
case 1: // Mass
measureType = #"Mass";
break;
case 2: // Volume
measureType = #"Volume";
break;
}
return self;
}
-(NSString*) measurementType
{
return measureType;
}
Thanks for the help, it's driving me crazy!

You need to make measureType an instance variable, so that each object of this type that you create has its own copy:
#interface ObjectSelectionViewController : NSViewController {
NSString * measureType; // Declare an NSString instance variable
}
- (id) initWithMeasureType: (int)value;
#end
As it is, there is only one copy of the variable, and every time you instantiate a new object, its value changes. Since each instance is referring to the same copy, they all get the same value:
ObjectSelectionViewController *length = [[ObjectSelectionViewController alloc] initWithMeasureType:0];
NSLog(#"%#", [length measurementType]); // Prints "Length"
ObjectSelectionViewController *mass = [[ObjectSelectionViewController alloc] initWithMeasureType:1];
NSLog(#"%#", [length measurementType]); // Prints "Mass"
You also need to change your init... method as mentioned by other answerers:
- (id) initWithMeasureType: (int)value {
// Call superclass's initializer
self = [super init];
if( !self ) return nil;
switch (value) {
case 0: // Length
measureType = #"Length";
break;
case 1: // Mass
measureType = #"Mass";
break;
case 2: // Volume
measureType = #"Volume";
break;
}
return self;
}
Since you are assigning a literal string to the instance variable, you do not need to worry about managing its memory; if you were doing anything more complicated, you would probably do well by declaring a property.
Another note: initializer methods should always return id, a generic object pointer, to allow subclasses to work properly.

You need to call [super init] first, like this:
-(id) initWithMeasureType:(int)value
{
if ((self = [super init]))
{
switch (value) {
case 0: // Length
measureType = #"Length";
break;
case 1: // Mass
measureType = #"Mass";
break;
case 2: // Volume
measureType = #"Volume";
break;
}
}
return self;
}

Constructors are a convention in Objective-C rather than a language feature. So, for example, there's no automatic calling of parent constructors (just like you wouldn't expect any other overridden method to call its parent implementations). Similarly, the names used for constructors are just conventions, so the compiler knows nothing of init. With that in mind, what you actually want as your constructor is:
-(id) initWithMeasureType:(int)value
{
if((self = [super init]))
{
switch (value) {
case 0: // Length
measureType = #"Length";
break;
case 1: // Mass
measureType = #"Mass";
break;
case 2: // Volume
measureType = #"Volume";
break;
}
}
return self;
}
Assuming measureType is an instance variable (declared as part of the interface, generally speaking) and not a global then that should do what you want.

In your custom init method you just need to start with:
self = [super init];

- (id) initWithMeasureType: (int)value {
// Call superclass's initializer
self = [super init];
if( !self ) return nil;
switch (value) {
case 0: // Length
measureType = #"Length";
break;
case 1: // Mass
measureType = #"Mass";
break;
case 2: // Volume
measureType = #"Volume";
break;
}
return self;
}

Related

How do I find the minimum value of a binary tree?

I'm trying to find the minimum value of a binary tree. Every time I run my code I get a long 5 digit number like '32675'. I'm pretty sure my understanding of pointers is wrong, but I'm not positive. If I could get some advice I'd really appreciate it. Thanks!
Node definition
#interface Node:NSObject {
#property (nonatomic, strong) Node *left;
#property (nonatomic, strong) Node *right;
#property (nonatomic, assign) int *value;
}
-(id)initWithValue:(int)val {
self = [super init];
if(self) {
self.value = &(val);
self.left = nil;
self.right = nil;
}
return self;
}
Insert Algorithms for Tree
-(void)insertValue:(int)value {
Node *node = [[Node alloc] initWithValue:value];
[self insertNode:node];
}
-(void)insertNode:(Node *)node {
if (root == nil) {
root = node;
} else {
[node insertNode:node];
}
}
Insert Algorithms for Node
-(void)insertNode:(Node *)node{
if (node.value < self.value) {
[self insertOnLeft:node];
} else {
[self insertOnRight:node];
}
}
-(void)insertOnLeft:(Node *)node {
if (self.left == nil) {
self.left = node;
} else {
[self.left insertNode:node];
}
}
-(void)insertOnRight:(Node *)node {
if (self.right == nil) {
self.right = node;
} else {
[self.right insertNode:node];
}
}
3 values go in to my tree:
[tree insertValue:4];
[tree insertValue:6];
[tree insertValue:2];
int min = [tree findMinimum];
Tree's findMinimum method is called
-(int)findMinimum {
assert(root != nil);
return [root findMinimum];
}
Which call's root's findMinimum - root is a node
-(int)findMinimum {
Node *node = self;
int min = 0;
while (node != nil) {
min = *(node.value);
node = node.left;
}
return min;
}
Your are filling your tree not with integers but with stack addresses which are immediately invalid with this code:
self.value = &(val);
Here val is a parameter variable, which will disappear as soon as the method returns. Taking its address should only be done in rare circumstances and that address should never be stored in a location which outlives val.
Change the type of the value property of Node to int and remove the uses of address of (&) and indirection (*) associated with that property.
HTH
The assignment in the initializer of a pointer to the parameter won't work. Unless you have a good reason not to, actually allocate the int into the Node structure by declaring it an int, not an int *. So your initializer will look like this:
-(id)initWithValue:(int)val {
self = [super init];
if(self) {
self.value = val;
}
return self;
}
EDIT If you weren't sorting on insert (which I missed in the OP), you could instead search for the min recursively as follows:
-(int)findMinimum {
if (self.left && self.right)
return MIN([self.left findMinimum], [self.right findMinimum]);
else if (self.left)
return [self.left findMinimum];
else if (self.right)
return [self.right findMinimum];
else
return self.value;
}

Applying an IF STATEMENT to my word generator

When I try adding an "if" statement to my word generator I get an error stating "Expected Expression". If I take the if statement out, it works fine but what I want to do is have several word generators and depending on the value of my variable "variable" determine which word generator is accessed.
Example: If "variable" is equal to 1 then the first word generator is accessed. If "variable is equal to 2 then the second word generator is accessed
Below is the code from my implementation file.
#import "ViewController2.h"
#import "ViewController.h"
#import "ViewController3.h"
#interface ViewController2 ()
#end
#implementation ViewController2
-(IBAction)random {
if (int variable = 3) {
int text = rand() %3;
switch (text) {
case 0:
introLabel.text = #"Test 1";
break;
case 1:
introLabel.text = #"Test 2";
break;
case 2:
introLabel.text = #"Test 3";
break;
default:
break;
}
}
}
-(IBAction)backButton:(id)sender {
ViewController *viewController2 = [[ViewController alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:viewController2 animated:YES];
}
-(IBAction)moreButton:(id)sender {
ViewController3 *viewController2 = [[ViewController3 alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:viewController2 animated:YES];
}
Thanks in advance for any help.
Your question is unclear but I think you are talking about the line:
if (int variable = 3) {
That is invalid Objective-C syntax. Perhaps you want:
if (variable == 3) {
This assumes you have an instance variable named variable (which is a terrible name).
So your random method becomes something like:
-(IBAction)random {
if (variable == 1) {
int text = rand() %3;
switch (text) {
case 0:
introLabel.text = #"Test 1";
break;
case 1:
introLabel.text = #"Test 2";
break;
case 2:
introLabel.text = #"Test 3";
break;
default:
break;
}
} else if (variable == 2) {
// process the 2nd word generator here
} else if (variable == 3) {
// process the 3rd word generator here
}
}
Again, you need to add an instance variable named variable and you set that value somewhere appropriate. Or variable can be another local variable assigned the value of rand() like you do with the text variable.

Modify object type at runtime

Is it possible in Objective C to modify an object type at runtime without the compiler complaining?
e.g.
id object;
in an init method
initWithType:(someEnumType) type
then depending on type set the object to a class type.
How is this done without the compiler flagging errors that object does not declare someMethod?
The most common way to do something like that is in a factory method, rather than an initializer:
typedef enum {
etString,
etNumber
} EnumType;
#implementation MyFactory
+(id)makeNewObjectWithType:(EnumType)et {
id res;
switch (et) {
case etString:
res = [NSString string];
break;
case etNumber:
res = [NSNumber numberWithInt:12345];
break;
default:
res = nil;
break;
}
return res;
}
#end

Objective-C init function not initializing correctly

#interface Set : NSObject
{
// instance variables
int repetitions;
int weight;
}
// functions
- (id)init;
- (id)initWithReps: (int)newRepetitions andWeight: (int)newWeight;
#implementation Set
-(id)init
{
if (self = [super init]) {
repetitions = 0;
weight = 0;
}
return self;
}
-(id)initWithReps: (int)newRepetitions andWeight: (int)newWeight
{
if (self = [super init])
{
repetitions = newRepetitions;
weight = newWeight;
}
return self;
}
#implementation eFit2Tests
- (void)setUp
{
[super setUp];
// Set-up code here.
}
- (void)tearDown
{
// Tear-down code here.
[super tearDown];
}
- (void)testInitWithParam
{
Set* test = nil;
test = [test initWithReps:10 andWeight:100];
NSLog(#"Num Reps: %d", [test reps]);
if([test reps] != 10) {
STFail(#"Reps not currectly initialized. (initWithParam)");
}
NSLog(#"Weight: %d", [test weight]);
if([test weight] != 100) {
STFail(#"Weight not currectly initialized. (initWithParam)");
}
}
For some reason the test at the bottom of this code snippet fails because the values of repetitions and weight are always equal to 0. I come from a background in Java and am clueless as to why this is the case. Sorry for the silly question...
You are setting test to nil, and then sending it initWithReps:andWeight:. This is equivalent to [nil initWithReps:10 andWeight:100], which obviously isn't what you want. nil just responds to any message with itself or 0, so that init message is returning nil and sending reps to nil is returning 0.
To create an object, you want the alloc class method — i.e. Set *test = [[Set alloc] initWithReps:10 andWeight:100]. (And if you're not using ARC, you'll want to release this object when you're finished with it, per the memory management guidelines.)
Where you're initializing your set, replace it with:
Set *test = [[Set alloc] initWithReps: 10 andWeight: 100];
You're getting 0 because that's the default return from a nil object (you've initialized test to nil) - there are no NullPointerExceptions in Objective-C

OCMock: Make a stub do something

I'm getting used to OCMock. Coming from a Java/JMock background I'm now looking for the ability to say [[[myMock stub] returnValueFromCustomMethod] someMockedMethod]; where returnValueFromCustomMethod is defined in the test class. I was originally thinking something along the terms of [[[myMock stub] usingSelector:#selector(myMethod:)] someMockedMethod]; but after writing I wonder if my first approach makes more sense. Either way, could someone show me if and how this can be done?
My original answer was off-track: OCMock doesn't support this! If you wanted to change OCMock to support this, you would need to do something like adding a BOOL returnValueIsFromInvocation field to OCMockRecorder, and add a method to set this up:
- (id)andReturnResultOfInvocation:(NSInvocation *)anInvocation {
returnValueIsFromInvocation = YES;
returnValueIsBoxed = NO;
returnValueShouldBeThrown = NO;
[returnValue autorelease];
returnValue = [anInvocation retain];
return self;
}
Then teach setUpReturnValue to call the invocation (changes are in bold):
- (void)setUpReturnValue:(NSInvocation *)anInvocation
{
if (returnValueIsFromInvocation) {
NSInvocation *returnValueInvocation = (NSInvocation *)returnValue;
[returnValueInvocation invoke];
void *buffer = malloc([[anInvocation methodSignature] methodReturnLength]);
[returnValueInvocation getValue:buffer];
[anInvocation setReturnValue:buffer];
free(buffer);
}
else if(returnValueShouldBeThrown)
{
#throw returnValue;
}
else if(returnValueIsBoxed)
{
if(strcmp([[anInvocation methodSignature] methodReturnType],
[(NSValue *)returnValue objCType]) != 0)
[NSException raise:NSInvalidArgumentException
format:#"Return value does not match method signature."];
void *buffer = malloc([[anInvocation methodSignature] methodReturnLength]);
[returnValue getValue:buffer];
[anInvocation setReturnValue:buffer];
free(buffer);
}
else
{
const char *returnType = [[anInvocation methodSignature] methodReturnType];
const char *returnTypeWithoutQualifiers = returnType + (strlen(returnType) - 1);
if(strcmp(returnTypeWithoutQualifiers, #encode(id)) == 0)
[anInvocation setReturnValue:&returnValue];
}
}
This change is difficult to do by introducing subclasses because you have to override the methods that return OCMockRecorders (like stub, expect and so on) but the concrete subclasses of OCMockObject (OCClassMockObject and OCProtocolMockObject) are hidden by the framework.