Objective-C exercises undeclared identifier error - ios

I'm currently teaching myself Objective-C as a first language. I understand the difficulty involved, but I'm quiet a persevering individual. I've began to do the exercises on the Apple Objective-C documentation. My goal is to have my program log out my first and last name instead of a generic Hello World greeting.
I keep receiving a Use of Undeclared identifier error. I'm trying to figure out what is causing the error.
Here is the introClass.h
#import <UIKit/UIKit.h>
#interface XYZperson : NSObject
#property NSString *firstName;
#property NSString *lastName;
#property NSDate *dateOfBirth;
- (void)sayHello;
- (void)saySomething:(NSString *)greeting;
+ (instancetype)person;
-(int)xYZPointer;
-(NSString *)fullName;
#end
Here is IntroClass.m
#import "IntroClass.h"
#implementation XYZperson
-(NSString *)fullName
{
return[NSString stringWithFormat:#" %# %#", self.firstName, self.lastName];
}
-(void)sayHello
{
[self saySomething:#"Hello %#", fullName]; //use of undeclared identifier "fullName"
};
-(void)saySomething:(NSString *)greeting
{
NSLog(#"%#", greeting);
}
+(instancetype)person{
return [[self alloc] init];
};
- (int)xYZPointer {
int someInteger;
if (someInteger != nil){
NSLog(#"its alive");
}
return someInteger;
};
#end

The problem is that fullName is the name of a method. It should be invoked on self with square brackets.
Since saySomething: expects a single parameter, you need to either (1) remove the #"Hello %#" portion of the call, like this:
-(void)sayHello {
[self saySomething:[self fullName]];
};
or to make a single string from #"Hello %#" and [self fullName], like this:
-(void)sayHello {
[self saySomething:[NSString stringWithFormat:#"Hello %#", [self fullName]]];
};

You are passing back a string of first and last name but I don't see anywhere that you had set a value for them. As others noted try
-(void)sayHello
{
_firstName = [NSString stringWithFormat:#"John"];
_lastName = [NSString stringWithFormat:#"Doe"];
//if you want to see what's happening through out your code, NSLog it like
NSLog(#"_firstName: %# ...", _firstName);
NSLog(#"_lastName: %# ...", _lastName);
NSString *strReturned = [self fullName];
NSString *concatStr = [NSString stringWithFormat:#"Hello %#", strReturned];
NSLog(#"strReturned: %# ...", strReturned);
NSLog(#"concatStr: %# ...", concatStr);
[self saySomething:concatStr];
};
-(NSString *)fullName
{
return[NSString stringWithFormat:#" %# %#", self.firstName, self.lastName];
}

use
[self saySomething:#"Hello %#", self.fullName]];
or
[self saySomething:#"Hello %#", [self fullName]];

Related

Parsing id to NSString

When parsing API responses, sometimes I can not rely on strings being embedded in quotation marks. ID's are a good example of this, where some API's will send the numerical ID as a string while some will send it as a number.
What is a good practice when parsing such a value? If I simply parse it to an NSString like so:
NSString *myID = (NSString *)message["myID"];
I can end up with an NSString object that somehow contains (long)123.
And using stringValue would cause issues when the value is actually already sent as a string (since NSString does not have a stringValue function).
A way that works, but is somewhat ugly, is this:
id myID = (NSString *)message["myID"];
if ([myID respondsToSelector:#selector(stringValue)])
{
myID = [myID stringValue];
}
You could do something like:
id myID = message["myID"];
if ([myID isKindOfClass:[NSString class]]) { ... }
else { ... }
As long as this logic is encapsulated inside data parser and is opaque for your api users (i.e. they will always get a string) any approach is fine, e.g.:
- (NSString*)parseID:(NSDictionary*)message {
id rawID = message["myID"];
if ([rawID isKindOfClass:[NSString class]]){
return rawID;
} else if ([rawID isKindOfClass:[NSNumber class]]) {
return [(NSNumber*)rawID stringValue];
} else {
// We might still want to handle this case.
NSAssert(false, #"Unexpected id type");
return nil;
}
}
Alternative is to define stringValue in extension, so any possible objet will respond to selector:
#implementation NSString(JSONStringParsing)
- (NSString *)stringValue {
return [self copy];
}
#end
Why not just use description?
NSArray *objects = #[
#NSIntegerMin,
#NSIntegerMax,
#"123456789"
];
for (id object in objects) {
NSString *stringObject = [object description];
NSLog(#"%# -> %# | %#", [object className], [stringObject className], stringObject);
}

Recursive method Objective-C

I'm checking is first letter of string is 0, if it is remove it and call again method to check is there is still 0. I've debugged this and it seems like when it accomplish number without 0, it goes backwards. Code:
-(NSString *)deleteZerosOnFirst:(NSString *)card
{
NSString *firstLetter = [card substringToIndex:1];
if ([firstLetter isEqualToString:#"0"]) {
card = [card substringFromIndex:1];
[self deleteZerosOnFirst:card];
NSLog(#"CARD: %#", card);
return card;
}
else {
NSLog(#"CARD: %#", card);
return card;
}
}
The main problem is that you're not using the result of the recursion. The line of code where you call yourself should say this:
card = [self deleteZerosOnFirst:card];
Also, you're calling deleteZerosOnFirst before you do the NSLog. Reverse the order of these two lines. That will at least give you your debug output in the right sequence.
Here's your recursive call:
[self deleteZerosOnFirst:card];
That doesn't modify the string that card references. It creates and returns a new string. You're ignoring the returned string. You want this:
card = [self deleteZerosOnFirst:card];
But this is really a lot simpler:
#implementation NSString (withoutLeadingZeroes)
- (NSString *)withoutLeadingZeroes {
NSString *s = self;
while ([s hasPrefix:#"0"]) {
s = [s substringFromIndex:1];
}
return s;
}
#end

autoreleasepool for nested methods

Say I have following code:
- (void) abc
{
#autoreleasepool
{
NSString *str = [NSString stringWithFormat:#"ABC %d", 12];
[self pqr];
}
}
- (void) pqr
{
NSString *str2 = [NSString stringWithFormat:#"PQR %d", 14];
}
For the above code, after the execution of method abc, will only str be released or str2 will also be released?
Both. If you are not sure, create your custom class and override dealloc implementation and add there some log message to not only believe me but experienced it on your own.

Undefined selector with Objective-C runtime - blocks

I'm trying to create a function where multiple times I do the same thing. I've deceided to go with a block this time. However after writing following code:
- (BOOL)readyForProcessing {
void (^notDeclaredError)(id) = ^(id missingObject) {
NSString *missingObjectName = NSStringFromSelector(#selector(missingObject));
NSString *errorDescription = [NSString stringWithFormat:#"You need to provide %# property", missingObjectName];
[self configureErrorWithLocalizedDescription:errorDescription];
};
if (!self.delegate) notDeclaredError(self.delegate);
return (self.error == nil);
}
I get a warning in the line, where I declare missingObjectName.
Undeclared selector 'missingObject'
I know it will probably try to make a NSString from missingObject instead of delegate. How to pass it this way, that the output will be delegate and the code will be inside the block?
Here you are:
- (BOOL)readyForProcessing {
void (^notDeclaredError)(SEL) = ^(SEL missingObject) {
NSString *missingObjectName = NSStringFromSelector(missingObject);
NSString *errorDescription = [NSString stringWithFormat:#"You need to provide %# property", missingObjectName];
[self configureErrorWithLocalizedDescription:errorDescription];
};
if (!self.delegate) notDeclaredError(#selector(delegate));
return self.error ? NO : YES;
}

isEqualToString always returns False

A little background here before I get started, basically we are looking to compare a UDP response with a string stored in Parse's database for our app. This issue is that I can't seem to get the strings to be considered equal by the isEqualToString function. Here's the code I have running now, I have tried a few work-arounds I've seen in other questions but it still doesn't work.
- (BOOL) onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port
{
if(tag == TAG_SINGLE_GRILL)
{
NSString *grillId = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if(grillId.length > 11)
{
grillId = [grillId substringToIndex:11];
}
grillId = [NSString stringWithFormat:#"%#", grillId];
if([grillId hasPrefix:#"GMG"])
{
for(int i = 0; i < [parseGrills count]; i++)
{
NSString *parseGrillId = [[parseGrills objectAtIndex:i] grillId];
parseGrillId = [NSString stringWithFormat:#"%#", parseGrillId];
//If we match the id, add it to found grills
if([grillId isEqualToString:parseGrillId])
{
//do stuff
}
}
}
NSLog(#"Grill ID : %#", grillId);
}
return TRUE;
}
parseGrills is an NSMutableArray with a very basic Grill object, I use synthesize for the properties, otherwise the .m file is essentially empty.
#import <Foundation/Foundation.h>
#interface Grill : NSObject
#property (nonatomic) NSString* grillId;
#property (nonatomic) NSString* ipAddress;
#end
Here's a screen shot of the debugger after it returns false
Any help would be greatly appreciated. Thanks.
I guess that they are of different encoding.
I have run this experiment and see that if the encoding is different, it will return NO. So, try converting parseGrillId to utf8 with the code below.
NSString *s1 = [NSString stringWithCString:"HELLO123" encoding:NSUTF8StringEncoding];
NSString *s2 = [NSString stringWithCString:"HELLO123" encoding:NSUTF16StringEncoding];
NSString *s3 = [NSString stringWithUTF8String:s2.UTF8String];
if ([s1 isEqualToString:s2]) {
NSLog(#"s1 == s2");
}
if ([s1 isEqualToString:s3]) {
NSLog(#"s1 == s3");
}
Will print s1 == s3.

Resources