Sending 'void' to parameter of incompatible type 'id' - objective-c

In Objective C, I have:
NSMutableArray *retVal = [[NSMutableArray alloc]initWithCapacity:1];
NSMutableString *justTest = [[NSMutableString alloc]initWithString:#"hello"];
unsigned char ch = //anything
[retVal insertObject:[justTest appendFormat:#"%hhu", ch] atIndex:0]; //error here
X Code 5.1.1 gives me an error in the 4th line(as mentioned as comment) as Sending 'void' to parameter of incompatible type 'id'
What am I doing wrong here? Any help is appreciated.

If you have a read of the Apple Documentation for NSMutableString you will find that the instance method appendString: doesn't actually return a value. It adds a structured string to the end of the receive and that's it.
So when you do [retVal insertObject:[justTest appendFormat:#"%hhu", ch] atIndex:0]; you are actually really doing [retVal insertObject:void atIndex:0]; and obviously you can't pass void in as a parameter which expects a valid object of id.
Here's the method declaration: - (void)appendFormat:(NSString *)format ... which you can see has a return type of void.
So what you need to be doing is you need to make the amendment to the string before you pass it into the insertObject:atIndex: method.
So change to
[justTest appendFormat:#"%hhu", ch]; // Append to existing string, DOESN'T return anything
[retVal insertObject:justTest atIndex:0]; // Pass string in as object at index

appendFormat doesn't return anything, it adjusts the mutable string. You need to do this:
NSMutableArray *retVal = [[NSMutableArray alloc]initWithCapacity:1];
NSMutableString *justTest = [[NSMutableString alloc]initWithString:#"hello"];
unsigned char ch = //anything
[justTest appendFormat:#"%hhu", ch]
[retVal insertObject:justTest atIndex:0]; //error here

[justTest appendFormat:#"%hhu", ch] return void.
You need:
[retVal insertObject:([justTest appendFormat:#"%hhu", ch],justTest) atIndex:0];

Related

Inserting text into the string in iOS

I need my user to enter numbers to type his telephone number.The user can only enter 8 numbers(for eg. XXXXXXXX). I need to change the phone number to be in the format XX-XX-XXXX.
This is what I have tried:
[tfDID.text insertString:#"-" atIndex:2];
[tfDID.text insertString:#"-" atIndex:5];
But it is returning me an error saying:
No Visible #interface for 'NSString' declares the selector 'insertString:atIndex:'
Need some guidance on this. Sorry if this is a stupid question.
No Visible #interface for 'NSString' declares the selector 'insertString:atIndex:'
As you are trying to mutate the textbox's value, which returns you NSString.
NSString object can not be mutated, so convert it into a mutable string then manupulate it.
Make your string NSMutableString.
As,
NSMutableString *tfDIDString=[NSMutableString stringWithString:tfDID.text];
[tfDIDString insertString:#"-" atIndex:2];
[tfDIDString insertString:#"-" atIndex:5];
[UITextField text] is NSString, you need to declare local variable of NSMutableString and perform insertString operations on it
Hope it helps you
Implement <UITextFieldDelegate> and then do:
-(void)textFieldDidChange:(UITextField*)textField
{
if( textField.text.length == 2 || textField.text.length == 5 ){
textField.text = [textField.text stringByAppendingString:#"-"];
}
}
Completely agree with the answer suggesting making it a mutable string.
Just to play devils advocate you could do:
NSString *partOne = [NSString stringWithRange:NSMakeRange(0,2)];
NSString *partTwo = [NSString stringWithRange:NSMakeRange(2,2)];
NSString *partThree = [NSString stringWithRange:NSMakeRange(4,4)];
NSString *formattedNumber = [NSString stringWithFormat:#"%#-%#-%#",partOne,partTwo,partThree];
I've written it out longhand but you could compress the string declarations for the parts in to the stringWithFormat call if you don't mind nesting and sacrifcing a bit of readability.

Get a Objective-C method to return several NSStrings

I need to use a method that returns several strings, different ones, according to a value.
My code looks like:
- (void) returnStringsAccordingToSet:(NSString *)string1: (NSString *)string2: (NSInteger)setNo {
switch (setNo){
case 1:
if (generalStringSettings){
string1 = #"The first string";
string2 = #"The second string";
} else {
string1 = #"The first other string";
string2 = #"The second other string";
}
break;
case 2:
...
break;
case 3:
...
break;
}
}
I call that method with:
NSString *firstString = [[NSString alloc]init];
NSString *secondString = [[NSString alloc]init];
NSUInteger set = 1;
[self getStringsAccordingToSet: firstString: secondString: set];
I can't get it to work! All I get is empty strings. I've got a feeling that the call for the strings is somewhat wrong. Please help.
You can't mae it work because when you do
string1 = #"The first string";
you just override the local parameter and update its reference but nothing outside the callee is modified. So the value is changed just inside the scope of the function.
You should change the signature of the method to
- (NSArray*) returnStringsAccordingToSet:(NSString *)string1: (NSString *)string2: (NSInteger)setNo {
so that it returns a NSArray instead that nothing and then inside the function
case1:
return [NSArray arrayWithObjects:#"The first string",#"The second string",nil];
so you return the array and store it from the caller, then you can access the returned values.
NSArray *v = [returnStringAccordingTo: ..];
[v objectAtIndex:0]
Technically, since ObjectiveC is a superset of C, I guess it is possible to pass a pointer to a pointer to NSString by reference through (but this is discouraged in ObjC):
NSString *string = nil;
[self callMethod:&string];
-(void)callMethod:(NSString**)refToString {
*refToString = #"foobar";
You could do this by filling a NSArray, but just to show how C works I'll show the error that you made.
string1 and string2 are just pointers, so you can pass either mutable or immutable strings as arguments.But the original pointer never gets modified, for example:
NSString* string1=#"Hello"; // Let's suppose string1 is on the 0x8000 address
// And points to the 0x9000 address
NSString* string2=#"How are you?"; // string2: 0x1000 , pointing to 0x2000
[self returnStringsAccordingToSet: string1: string1 string2: string2];
Then when you call a method a copy is made for every pointer that you pass to the method:
- (void) returnStringsAccordingToSet:(NSString *)string1: (NSString *)string2: (NSInteger)setNo {
// string1 and string2 both point on the same address, but they are other pointers
// for example string1 is on 0x7000 and string2 on 0x7400, pointing to 0x9000
// and 0x2000
switch (setNo){
case 1:
if (generalStringSettings){
string1 = #"The first string"; // You just create another instance
string2 = #"The second string"; // of the string, but the original
// pointers aren't affected
} else {
string1 = #"The first other string";
string2 = #"The second other string";
}
break;
case 2:
...
break;
case 3:
...
break;
}
}
The solutions:
Pass a NSMutableString as argument and instead of assigning them, just modify the contents;
Pass a pointer to pointer;
Return a NSArray containing the new strings.
Update note: I personally don't use the method as a first thing to do. I use arrays if that's possible. Not sure if it's OK to use this with ARC. Some Apple methods use this approach to return error value, so it's good to be aware of it.
When method is called, those arguments you pass to function are copied (of course they are not getting the copy message, pointer is copied as far as you pass pointer), and those copies are used inside the function (or more technically initiated with the argument value). So if you change the copy (i.e. try to replace object on pointer, mutable objects are OK to modify) this will not reflect on the value. You may solve this using pointers (i.e. pass pointer to pointer).
So:
NSString* string = #"one string";
NSLog(string); // one string
// [self method:string]
- (void) method: (NSString*) string {
// Local copy created:
// NSString* string = copy of string (second pointer created, object not copied in memory)
string = #"other string";
NSLog(string); // other string
}
NSLog(string); // one string
You may do this like that:
- (void) method: (NSString**)str {
*str = #"Some string";
}
and then:
// string = "old vlalue"
NSString* string = #"old value";
// string = "Some string"
[self method:&string];

EXC_BAD_ACCESS memory issue when accessing instance variables

I'm having issues porting a Java project I worked on a while ago to Objective-C code - I get "Program received signal: "EXC_BAD_ACCESS"", on the first line in this code:
-(Point3D *) unit {
NSLog(#"%#%#", "X: ", x);
double length = [self length];
return [[Point3D alloc] initWithX:x/length andY:y/length andZ:z/length];
}
called from here:
-(id) initWithStart:(Point3D *)start andDirection:(Point3D *)dir {
if ( self = [super init] ) {
NSLog(#"%#%#", #"Direction:", [dir toString]);
printf("Trying to find unit length of direction...\n");
NSLog(#"%#", [[dir unit] toString]);
self.start = start;
self.direction = [dir unit];
}
return self;
}
Console output is:
2011-12-09 17:20:14.021 RayTracerProject[16607:407] Direction:(0,0,20)
Trying to find unit length of direction...
The toString method of the Point3D looks like this:
-(NSString *) toString {
NSNumber *xstring = [NSNumber numberWithDouble:self.x];
NSNumber *ystring = [NSNumber numberWithDouble:self.y];
NSNumber *zstring = [NSNumber numberWithDouble:self.z];
NSString * str = #"(";
str = [str stringByAppendingString:[xstring stringValue]];
str = [str stringByAppendingString:#","];
str = [str stringByAppendingString:[ystring stringValue]];
str = [str stringByAppendingString:#","];
str = [str stringByAppendingString:[zstring stringValue]];
str = [str stringByAppendingString:#")"];
return str;
}
So, from what I can see, my (Point3D *) dir is alive and well when I check what the value is using my [dir toString] call. But when I try to call [dir unit], it seems I no longer have the variables I did in the object, hence the EXC_BAD_ACCESS error.
What am I doing wrong here? I think it's something to do with the way I'm managing (or not) my memory usage, but I don't know what it is.
The NSLog in your method 'unit' should be:
NSLog(#"%# %g", #"X:", x);
Moving the space is optional, but just makes it clearer. The key issues were that you were missing the # before the "X: ", and you need to use %g rather than %# because from your other code x is a double rather than an NSObject. Equivalent—but simpler and thus better—would also be:
NSLog(#"X: %g", x);
Apple's documentation provides the definitive guide to string format specifiers and that and the surrounding documentation explain the use of format specifiers like %# (to reference an Objective C object) and %g (to reference a 64-bit floating-point number) when constructing an NSString.
It seems this line here:
NSLog(#"%#%#", "X: ", x);
should look more like this:
NSLog(#"%#%lf", #"X: ", self.x);
Note a few changes here:
From your other code, it seems you have a property named 'x'. self.x references that property. (You may or may not also have an instance variable named 'x')
Also from your other code, it seems your property 'x' is a double. So the placeholder for a double is %lf, not %#. %# is for NSObjects (and descendants)
NSString literals start with #. So #"X: " is an NSString literal. What you had is a C string.
EDIT
Well, I'm slow as usual. :) Ignore what I said and accept Duncan's answer, which was faster and now cleaner, actually.

How do you enumerate through an array in Obj-C?

I have an array which has several objects (all of different classes) in it. But using enumeration doesn't work on it for some reason.
NSString *arrayString;
NSURL *arrayUrl;
NSProcessInfo *arrayPr;
NSDictionary *arrayDictionary;
NSMutableString *arrayMString;
NSMutableArray *objectArray = [NSMutableArray arrayWithObjects:arrayString,arrayUrl,arrayPr,arrayDictionary,arrayMString,nil];
for( NSString *item in objectArray ){
NSLog(#"Class name is: %#", [item className]);
}
I think it might be something to do with how the objects are been added to the array but i'm new to objective-c and not sure.
you aren't actually populating the array.
NSString *arrayString;
declares a variable, arrayString, of type NSString. it's not initialized (so it deserves to crash when you use the variable -- but may be 0 with some build settings).
so, to assign a variable:
NSString *arrayString = [NSString stringWithFormat:#"sksjdhf %f\n", 3.3];
arrayWithObjects adds objects from the (va list) argument until nil/null/0 is encountered.
you must set up the remainder of your variables/arguments correctly before using them.
this should work as you expect it to:
NSString * str = #"a string";
NSMutableArray *objectArray = [NSMutableArray arrayWithObjects:str, nil];
for (NSObject * item in objectArray) {
NSLog(#"Class name is: %#", [item className]);
}
In the for loop, use an id data type. The id data type is a general purpose data type that can be used to store a reference to any object.
For example:
for ( id item in objectArray ) {
NSLog(#"Class name is: %#", [item className]);
}
Yep, that's how you do it. If you're having trouble, it is not in the enumeration syntax itself.

Add a (double) variable to NSMutableArray

I have a user input variable and i want to add that to an array. The firstStore is a BOOL type t determine if the array has been initialize. So the first time the STORE was called, it would initialize the array. I tried to make num equal to the operand (which is a double) by masking it NSNumber but that doesn't seem to work because i have this error "NSNumber may not respond to +operand", also the program crashes when it hits the line [memArray addObject:num]. I'm new at this stuff so any help would be greatly appreciated.
else if ([operation isEqual:#"Store"]) {
if(!firstStore){
memArray = [[NSMutableArray alloc] init];
NSNumber *num = [NSNumber operand];
[memArray addObject:num];
firstStore = YES;
} else {
//NSNumber *num = [NSNumber operand];
//[memArray addObject:num];
}
}
Try changing [NSNumber operand] to [NSNumber numberWithDouble:operand].
"NSNumber may not respond to +operand" means what is says: There is no such class-method operand. What was it supposed to do?
You dont need a bool to track if memArray is initialized. use:
if(!memArray)
or
if (memArray == nil)