Can't include "self" in Objective-C description method? - iphone

I have a very straight forward class with mostly NSString type properties. In it, I wrote a trivial implementation of the description method. I found that whenever I try to include "self" in the description, it crashes my iPhone app. An example is something such as the following:
- (NSString *)description
{
NSString *result;
result = [NSString stringWithFormat:#"me: %#\nsomeVar: %#", self, self.someVar];
return result;
}
As soon as I remove the first parameter to the format string, self, it works as expected.

Use %p for self, then it will display the address of self. If you use %#, then it will call description on self, which will set up an infinite recursion.

You can use [super description] instead of self to avoid the infinite recursion, like so:
- (NSString *)description
{
return [NSString stringWithFormat:#"%#: %#", [super description], [self someVar]];
}

You do realise that sets up an infinite recursion.
Your description implementation is implicitly calling itself when you pass in self, which then calls itself, and so on.
Your crash is mostly likely due to stack space running out... a "stackoverflow" if you will. Fitting considering the site :-)

Related

How to call a non-void function? Xcode

How do I call a non-void function? Normal [self methodName]; works. But how do I do this for a method that returns an NSString. I keep getting an error. For example:
+ (NSString *)formulateYQLRequestFor:(NSArray *)tickers
How do I call this? [self formulateYQLRequestFor]; gives me an error.
Sorry about the formatting, for some reason safari won't let me indent.
Thanks!
+ designates a class function. You call it with the class name, not an instance.
Instead of:
[self formulateYQLRequestFor:myArray];
Do this:
[MyClassName formulateYQLRequestFor:myArray];
Alternatively, you can do this:
[[self class] formulateYQLRequestFor:myArray];
You don't have to do anything with the return value if you don't want to. At least with ARC, the return value will be automatically released. However, since it's unlikely that the function does anything on its own, you probably should do something with the return value:
NSString *returnValue = [[self class] formulateYQLRequestFor:myArray];
// Do something with returnValue
Finally, if you want to call the function without passing in an array, you still need the array parameter, but perhaps the function will accept nil for the array:
NSString *returnValue = [[self class] formulateYQLRequestFor:nil];
There are two problems with your call to [self formulateYQLRequestFor];
Firstly, the method takes a parameter, which you haven't provided. Because of this, the compiler is looking for the method called formulateYQLRequestFor instead of formulateYQLRequestFor: This is significant, because the : is part of the method name in Objective-C. So you are trying to call a method that doesn't exist.
Secondly, self is sending a message to an instance of your class. The + in the method signature indicates that you have a class method, and so self does not respond to the method you are trying to call.
The correct way to do this is:
NSString *resultString = [[self class] formulateYQLRequestFor:someArray];
where someArray is a valid NSArray parameter.
I don't know what - (NSString *)formulateYQLRequestFor: does with the NSArray, but if it isn't necessary you can just call [self formulateYQLRequestFor:nil];. Alternatively you can call it with an empty array [self formulateYQLRequestFor:[NSArray array]];.

Calling a method in another method?

I've got a delegate method which just has a bit of code in there that puts a % sign on the end of the number entered.
- (void)textFieldDidEndEditing:(UITextField *)UItextfield {
NSString *oldTextFieldValue = UItextfield.text;
UItextfield.text = [NSString stringWithFormat:#"%# %%",oldTextFieldValue];
}
Could I instead of having that, have the following action
-(IBAction)Calculate:(UITextField *)UITextfield;
{
NSString *oldTextFieldValue = UItextfield.text;
UItextfield.text = [NSString stringWithFormat:#"%# %%",oldTextFieldValue];
}
And then in the Delegate function, call that action? Something like
-(void)textFieldDidEndEditing:(UITextField *)UItextfield {
[self Calculate:self]
}
I tried that, it doesn't work. I know it'll get me to the same result but I just want to know if it can be done. I think i'm asking can a method (Calculate) be called in another method (textFieldDidEndEditing) and how.
You are providing self as the method argument which is the instance of the class you are in. Which in this case is wrong since the method argument should be an instance of UITextField. Try instead [self Calculate:UItextfield] in your method.
Calling other methods from methods happen all the time in most programming languages.
It's a great way to split code up and reuse code in different places without having to copy/paste.
(This might be too basic for you. Sorry in that case)
Things may be easier to understand if you use standard naming conventions too. ('likeThis' for variables and method names; 'LikeThis' for class names)
- (void)textFieldDidEndEditing:(UITextField *)textField {
NSString *oldTextFieldValue = textField.text;
textField.text = [NSString stringWithFormat:#"%# %%",oldTextFieldValue];
}
textField here, is the pointer to the UITextField object which just finished editing.
You want to pass this object to your new 'other' method.
[self calculate:textField];
self is a pointer to an instance of the current class. For example, in a UIViewController subclass called 'MyViewController', self refers to the current instance of this class.
Since the -calculate method is an instance method (beginning with a '-') it requires you to use self. The variable textField is passed after the colon.
- (void)calculate: (UITextField*)textField {
NSString *oldTextFieldValue = textField.text;
textField.text = [NSString stringWithFormat:#"%# %%",oldTextFieldValue];
}
Use only the IBAction keyword when you want the method to be called from a UIComponent in an xib or storyboard.

ObjC object changes to random objects: a memory issue

long time listener, first time caller.
I have a basic memory issue I don't understand, that I'm sure just about any one of you will see in a second. I'm playing around, trying to learn various ways of using UIWebViews, getting strings from URLs, and so on. Specifically, I'm trying to obtain one URL from another. In other words, I have uploaded an html page to the web, containing a URL. The address for that page is coded into the app, giving me a "hook" into the app - I can change the contents of that page and send the app a new URL any time I want. Make sense?
So...retrieving the URL? No problem. Passing it into a string for later use - no problem. But when I set up a tap gesture recognizer, which should take that string, convert it back to an NSURL, and open it in Safari, I get a runtime crash. An NSLog call tells me that the string in question keeps being assigned to all sorts of random things.
The relevant bits of my code follow. I'm sure some of you will tell me there are much better ways to do what I want - and that's certainly welcome. But I'd also really love to know what I'm doing wrong for this particular implementation, as I'm sure it's a basic misunderstanding that I'd like to correct.
Thanks in advance. (And sorry about the formatting of the code block - haven't quite got the hang of posting on here!)
#import "Messing_With_Web_ViewsViewController.h"
#implementation Messing_With_Web_ViewsViewController
#synthesize tapView;
NSString *finalURL;
- (void)viewDidLoad {
[super viewDidLoad];
NSString *firstString = #"http://www.my_web_address.html";
//Of course, I have the correct address here.
NSURL *firstUrl = [NSURL URLWithString:firstString];
NSError * error;
finalURL = [NSString stringWithContentsOfURL:firstUrl
encoding:NSASCIIStringEncoding error:&error];
if ( finalURL )
{
NSLog(#"Text=%#", finalURL);
//everything fine up to here; console prints the correct
contents of "my web address"
}
else
{
NSLog(#"Error = %#", error);
}
//Taps
UITapGestureRecognizer *tapRecognizer;
tapRecognizer=[[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(foundTap:)];
tapRecognizer.numberOfTapsRequired=1;
tapRecognizer.numberOfTouchesRequired=1;
[tapView addGestureRecognizer:tapRecognizer];
[tapRecognizer release];
}
- (void)foundTap:(UITapGestureRecognizer *)recognizer {
NSLog(#"Trying to load %#", finalURL);
//at this point the app either crashes, or the console shows a random memory object
[[UIApplication sharedApplication] openURL:[NSURL URLWithString: finalURL]];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
}
- (void)dealloc {
[finalURL release];
[super dealloc];
}
#end
finalURL = [NSString stringWithContentsOfURL:firstUrl
encoding:NSASCIIStringEncoding error:&error];
The line above creates an instance of NSString which you do not own (because you did not call a method whose name includes 'new', 'alloc' 'retain', or 'copy' on it). That finalURL with therefore eventually be destroyed when it is no longer needed. By the time your -foundTap: method runs finalURL has been deallocated and you are just referencing the memory location where it used to be and which now may contain some other object or random data.
Read the memory management guidelines again and also learn to run the static analyzer which should point out mistakes like this.

instance variables not accessible

Serious Problem here... i'm getting ECX_BAD_ACCESS if i try to NSLog an instance variable of my custom object. Following Function is called in my ViewController, payload holds String Data which is pulled from a url.
- (void) initVcardWithData:(NSString *)payload {
NSLog(#"1. initVcardWithData");
aVCard = [[vcardItem alloc] initWithPayload:payload];
VCardViewController *aVCardViewController = [[VCardViewController alloc] initWithVCard:aVCard];
[self presentModalViewController:aVCardViewController animated:YES];
[aVCard release];
}
So far so good. The initWithWithVCard function is as follows, theVCard and theVCardN are defined in #implementation and also set as a #property (nonatomic, retain) in (.h).:
-(id)initWithVCard:(vcardItem *)aVCard {
if(self = [super init]) {
theVCard = [aVCard retain];
theVCardN = [theVCard.PersonName retain];
}
NSLog(#"---- vCardViewController :: initWithVcard :: FirstName: %#", theVCard.PersonName.FirstName);
return self;
}
If i access the theVCardN object in my ViewController aVCardViewController within ViewDidLoad everything works like a charm. I set some labels with data from that object.
If i then try to access the instance variables from theVCardN within a function which is called from an IBAction which is connected to a button in View, i get an EXC_BAD_ACCESS error at the debugger console. The Function which tries to pull data from the instance variables is as follows:
-(IBAction)addressbookButtonTapped {
NSLog(#"RETAIN COUNT FOR theVCard: %i", [theVCard retainCount]);
NSLog(#"RETAIN COUNT FOR theVCardN: %i", [theVCardN retainCount]);
NSLog(#"Save to Adressbook: %#", theVCardN.FirstName);
//[self dismissModalViewControllerAnimated:YES];
}
The RetainCounter for theVCardN right before calling NSLog outputs "1". The NSLog Line then returns EXC_BAD_ACCESS in Debugger Console.
Any idea ?
Do not call -retainCount. Absolute retain counts are useless.
retainCount returns the absolute retain count of an object. The actual value will be an implementation detail that is very often completely out of your control as the system frameworks may do any number of things internally to cause the retain count to be modified in ways you don't expect.
It is useless for debugging and their are a wealth of tools that are specifically focused on tracking down these kinds of issues.
First, if there is a crash, there is a backtrace. Post it. Probably not that interesting in this case, but, still, always look to the backtrace to at least confirm that it is crashing where/how you think it is.
From the evidence posted, it sounds like theVCardN.FirstName is either set to garbage or the underlying string has been over-released. Turn on zombie detection mode and see if that is the case. Since it is crashing on FirstName, then show the code related to creating/storing the FirstName.
Also, instance variables and methods should always start with a lowercase letter; PersonName should be personName & FirstName should be firstName.
Maybe i'm reading the code wrong or misunderstanding your class structure, but it looks like you logging:
NSLog(#"Save to Adressbook: %#", theVCardN.FirstName);
Above, where you say it is still working, you are logging:
theVCard.PersonName.FirstName
Are you missing the "PersonName"? Meaning you should be logging:
NSLog(#"Save to Adressbook: %#", theVCardN.PersonName.FirstName);

NSString self modifying category dilemma

Both work, but which one would you use and why?
#implementation NSString (Extender)
-(NSString *) stringByTrimmingPrefix:(NSString *)strPrefix
{
while ([self hasPrefix:strPrefix])
{
self = [self substringFromIndex:strPrefix.length];
}
return self;
}
#end
or
#implementation NSString (Extender)
-(NSString *) stringByTrimmingPrefix:(NSString *)strPrefix
{
NSString *returnValue = [NSString stringWithString:self];
while ([returnValue hasPrefix:strPrefix])
{
returnValue = [returnValue substringFromIndex:strPrefix.length];
}
return returnValue;
}
#end
Option #2.
NSString is intended to be an immutable object. All of the standard "stringBy" methods on NSString return new autoreleased NSStrings.
While #1 also ends up returning a new NSString, it is at best semantically incorrect and at worst altering the referenced object of what was supposed to be an immutable object.
Firstly, your Objective-C method definition is exactly equivalent to this C function:
NSString* stringByTrimmingPrefix(NSString* self, SEL _cmd, NSString* strPrefix)
{
...
}
As you can see, self is just another function parameter; you can re-assign it to anything you want and it won't affect the state of the original NSString* instance it was originally pointing to.
So there's nothing wrong with what you're doing in your first implementation, in that respect.
However, neither of your implementations are very efficient, and both have bugs (what happens if you pass a prefix with more than one character?)
I would use rangeOfString:options:range: to find your prefixes, so that you're only creating at most one extra NSString instance.
See Searching, Comparing, and Sorting Strings
so this just chops off a single letter from the front of a string (if that letter is there more than once, it will cut off as many as there are)?
Since NSString is immutable, I don't there is a difference either way, performance wise. In the first case you are replacing self with a new string object each time, and in the second case, you are replacing returnValue with a new string object each time. I guess since the first option saves a line of code i would use that one.