This is my first time using this site and I am quite new to Objective-c. I'm sure this is a simple question but for some reason I am having a lot of issues. The app is designed to have the user enter a string via textfield, then it will pick the rest of the sentence and display it. The issue appears to be that my *name will be retained after the keyboard method and work once in the changelabel method. Then if i press the button again, invoking the changelabel method, the name appears to have been released and crashes the app.
#import
#import "Array.h"
#interface RandomBoredViewController : UIViewController {
UILabel *label;
UIButton *button;
UITextField *textField;
Array *array;
NSString *name;
NSString *description;
NSMutableString *whole;
}
#property (nonatomic, retain) IBOutlet UILabel *label;
#property (nonatomic, retain) IBOutlet UIButton *button;
#property (nonatomic, retain) IBOutlet UITextField *textField;
#property (nonatomic, retain) Array *array;
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *description;
#property (nonatomic, retain) NSMutableString *whole;
-(IBAction) keyBoard;
-(IBAction) changeLabel;
#end
and my .m
#import "RandomBoredViewController.h"
#implementation RandomBoredViewController
#synthesize label;
#synthesize checker;
#synthesize button;
#synthesize textField;
#synthesize array;
#synthesize name;
#synthesize description;
#synthesize whole;
-(IBAction) changeLabel {
NSLog(#"Button being pushed");
description = [array getString];
NSLog(#"%#",description);
NSLog(#"%#",name);
name = [NSString stringWithString:name];
whole = [NSMutableString stringWithString:name];
NSLog(#"%#",name);
NSLog(#"%#",whole);
[whole appendString:description];
NSLog(#"%#",name);
NSLog(#"%#",whole);
label.text = whole;
NSLog(#"%#",name);
}
-(IBAction) keyBoard {
name = [NSString stringWithString:textField.text];
NSLog(#"%#",name);
label.text = [NSString stringWithString: name];
[textField resignFirstResponder];
}
- (void)viewDidLoad {
[super viewDidLoad];
array = [[Array alloc]init];
[array createArray];
NSLog(#"%i",[array arrayCount]);
whole = [[NSMutableString alloc]init];
name = [[NSString alloc]init];
}
- (void)dealloc {
[super dealloc];
[label release];
[button release];
[textField release];
[array release];
//[name release];
[description release];
}
#end
You are setting name to an autoreleased instance of NSString, this is probably what's causing your app to crash.
Use
self.name = [NSString stringWithString:textField.text];
Your synthesized mutator will retain the NSString and prevent it from being released.
Taking one thing in microcosm, the code you've posted creates two things named name — an instance variable and a property.
Instance variables are directly accessed storage. They have no behaviour.
Properties are named attributes accessed via getters and setters. So they may have arbitrary behaviour. They may report the values of instance variables or values calculated from instance variables or values calculated or obtained by any other means. Relevantly, the setters may retain, assign or act in any other way.
Instance variables may be accessed only by the instance of a class they belong to. Properties are usually intended to be accessed by anyone.
Since retaining is a behaviour and you've ascribed it to your name property, setting something to it would result in a retain. Instance variables can't have behaviour, so setting a value to it doesn't result in a retain or anything else.
As a result, this line:
name = [NSString stringWithString:name];
Creates a new string and returns a non-owning reference. Which means it'll definitely last for the duration of this autorelease pool (ie, you explicitly may pass it as an argument or return it safely, assuming you haven't taken manual control of your autorelease pools).
You store that reference to your instance variable. Instance variables don't have behaviour so the reference is stored but you still don't own that object. It's still only safe to use for the duration of that autorelease pool.
So when you access it in that method it's safe. When you access it later it's unsafe.
If instead you'd gone with:
self.name = [NSString stringWithString:name];
Then you'd have set that string to be the new value of the property. Because your property has the retain behaviour, you'd subsequently have completely safe access to the string object, until you say otherwise.
Because you've got a property with exactly the same name as an instance variable, you could subsequently access it either as just name or as self.name. Similarly you could have stored directly to the instance variable rather than via the property if you'd ensured you had an owning reference manually.
As suggested above, use of ARC is a way to get the compiler to figure all this stuff out for you.
That issue is what causes your code to crash — you end up trying to access a reference that has ceased to be valid. If you'd taken ownership of it then it would have continued to exist at least as long as you kept ownership.
try using self.name
sometimes this stuff confuses me as well and for that you might want to consider using arc in which case most of this stuff can be avoided.
when using properties you should always use self.propertyName vs propertyName (only), it uses the accessors (get propertyName, set propertyName) as opposed to directly accessing that pointers value.
take in mind there are 2 exceptions to the rule, init and dealloc which should NOT use self.
self.name = [NSString stringWithString:name];
you technically should also have an init method
to initialize your variables, and i believe you should call [super dealloc] last not first in your dealloc method, but thats not your problem and might not matter (just what I do when I dont use arc)
When you change your instance variable in changeLabel, you should release the previous value and retain the new one. You may use the accessors to perform the memory management stuff for you. Also, I think you should invoke [super dealloc] after releasing the instance variables in your implementation of dealloc.
If you're not familiar with Cocoa memory management (and even if you are), the best is to enable ARC (Automatic Reference Counting) and let the compiler deal with it.
Related
I have a problem understanding the Objective-C and the ARC.
As I understood the strong pointers will be dealloced automatically for you, so you don't have to think about it (dealloced in dealloc method, or after the last time the object was used ?).
So I wrote a little app, with 2 viewControllers and a NavigationController, which enters one view and then goes back.
The dealloc method was called, but the property, which I set at viewDidLoad method, wasn't deallocated, it is still pointing to some object.
Code:
The first viewController has a button, which by pressing it, performs a segue to another viewController. No code there.
SecondViewController.m
#interface SecondViewController ()
#property (nonatomic, strong) NSString *myString;
#end
#implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"%#", _myString);
_myString = #"it works";
}
- (void)dealloc {
NSLog(#"%#", _myString);
// and now it is deallocating the _myString property ???
}
#end
Then, I tried to do another thing.
The idea was to create a weak pointer, which points to the same memory address, as the strong pointer. I though, that the weak pointer should be nil in any case.
Since the dealloc method is called, all weak pointers should be niled
Since the strong pointer was used only in viewDidLoad, it should be deallocated way before the dealloc method.
The problem is, it is not deallocated.
Why ?
Code for secondViewController:
#interface SecondViewController ()
#property (nonatomic, strong) NSString *myString;
#property (nonatomic, weak) NSString *test;
#end
#implementation SecondViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%#", _myString);
_myString = #"it works";
_test = _myString;
}
- (void)dealloc
{
NSLog(#"%#", _test);
}
#end
Deallocation of the properties happens at the end of the dealloc method. If you overwrite the dealloc method, the properties won't yet be deallocated inside that method.
You could test this by creating a weak property in your first view controller, assign the strong property of the second view controller, then log the value of it when the application returns to the first view controller.
The simplest way to illustrate weak references is with the following example...
Given the following two classes:
#interface ObjectWithStrongRef : NSObject
#property (strong) NSString *ref;
#end
#interface ObjectWithWeakRef : NSObject
#property (weak) NSString *ref;
#end
We will create an instance of ObjectWithWeakRef with a scope larger than that of ObjectWithStrongRef, assign the latter's ref property a value, then have the former's ref point to this same object, then we will check ref in both scopes.
int main(int argc, const char * argv[]) {
ObjectWithWeakRef *weak = [[ObjectWithWeakRef alloc] init];
#autoreleasepool {
ObjectWithStrongRef *strong = [[ObjectWithStrongRef alloc] init];
strong.ref = [NSString stringWithFormat:#"Hello %#", #"World"];
weak.ref = strong.ref;
NSLog(#"Weak.Ref = %#", weak.ref);
}
NSLog(#"Weak.Ref = %#", weak.ref);
}
Note, that we can't simply assign ref to a literal string. Objective-C tends to keep these around in memory so it can do some memory optimizations, but when we use stringWithFormat:, it'll create an autoreleasing string.
At the first NSLog statement, strong.ref maintains a strong reference to the string object, so when we log weak.ref, the object is not yet deallocated, so it correctly logs "Hello World".
Between the first and second NSLog call, we've exited the #autoreleasepool, within which the strong object was scoped (if we put an NSLog message in ObjectWithStrongRef's dealloc, we'd see it called here). Because strong has deallocated as we exit the #autoreleasepool, there are no longer any strong references to the string object we have references to--we only have weak's weak reference to the memory, so the string object also deallocates (just after strong has deallocated).
So in the second NSLog call, we'll see Weak.Ref = (null) printed.
I am trying to print (nslog) the name of a photo embedded in PhotoView object that I created. I have created 2 viewcontrollers class and the PhotoView class extending the UIButton class to populate a grid in one of the former viewcontrollers.
In PhotoView.h I have
#interface PhotoView : UIButton
#property (assign, nonatomic) NSString *photoName;
...
in PhotoView.m I have
self.tag = [[data objectForKey:#"PhotoID"] intValue];
self.photoName = [data objectForKey:#"PhotoName"];
After printing out the value of tag and photoName on the same file, everything looks good.
Problem start when I try to print the value of photoName from another class after clicking on the PhotoView
-(void)didSelectPhoto:(PhotoView*)sender
{
NSLog(#"%#", [NSString stringWithFormat:#"%#", sender.photoName]);
}
After clicking on the photoView, I get EXC_BAD_ACCESS error.
However, If I do
NSLog(#"%#",[NSNumber numberWithInt:sender.tag]])
I don't get this error.
What could possibly be wrong?
Thanks in advance.
Two remarks:
[NSString stringWithFormat:#"%#", sender.photoName] - No please! No! Don't do that! It's not only superfluous and wastes CPU cycles, but it also heavily decreases readability. if you have a string, you don't have to duplicate it like this, just use the string object directly:
NSLog(#"%#", sender.photoName);
The actual error you have is this:
#property (assign, nonatomic) NSString *photoName;
I. e. you have an assign property, so it doesn't retain its value. When your string object goes out of scope, it's deallocated (and since it's not weak, it isn't automatically set to nil, but it holds whatever garbage value that invalid pointer is, hence the crash). Write instead
#property (retain, nonatomic) NSString *photoName;
if you're not using ARC, and
#property (strong, nonatomic) NSString *photoName;
if you are.
My app runs well until I stop it and restart - whereupon the archive file - highScores.archive is present. Then, the app balks at encoding - I get a EXC_BAD_ACCESS at the first line (for a long time, it didn't happen until I got to the date object I was encoding.
My guess is that I need to put in a retain in a couple of places, but I don't know where.
The code:
FlipHighScores.h
...
#interface FlipHighScores : NSObject <NSCoding> {
//NSString *themeChosen;
NSInteger newHighScore;
NSInteger newScoreStartLevel;
NSInteger newScoreFinishLevel;
NSDate *scoreDateCreated;}
#property (copy, nonatomic) NSString *themeChosen;
#property (nonatomic) NSInteger highScore;
#property (nonatomic) NSInteger scoreStartLevel;
#property (nonatomic) NSInteger scoreFinishLevel;
#property (nonatomic, readonly, strong) NSDate *scoreDateCreated;
...
FlipHighScores.m
...
#synthesize themeChosen = _themeChosen;
#synthesize highScore = _highScore;
#synthesize scoreStartLevel = _scoreStartLevel;
#synthesize scoreFinishLevel = _scoreFinishLevel;
#synthesize scoreDateCreated = _scoreDateCreated;
...
-(void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_themeChosen forKey:#"_themeChosen"];
NSLog(#"Theme Chosen is %#", _themeChosen);
[aCoder encodeInt:_highScore forKey:#"_highScore"];
[aCoder encodeInt:_scoreStartLevel forKey:#"_scoreStartLevel"];
[aCoder encodeInt:_scoreFinishLevel forKey:#"_scoreFinishLevel"];
NSLog(#"Date Created in encodeWithCoder is %#", _scoreDateCreated);
[aCoder encodeObject:_scoreDateCreated forKey:#"_scoreDateCreated"];}
-(id)initWithCoder:(NSCoder *)aDecoder {
if (self) {
_themeChosen = [aDecoder decodeObjectForKey:#"_themeChosen"];
_highScore = [aDecoder decodeIntForKey:#"_highScore"];
_scoreStartLevel = [aDecoder decodeIntForKey:#"_scoreStartLevel"];
_scoreFinishLevel = [aDecoder decodeIntForKey:#"_scoreFinishLevel"];
_scoreDateCreated = [aDecoder decodeObjectForKey:#"_scoreDateCreated"];
}
return self;}
-(NSString *)description {
NSDate *date = _scoreDateCreated;
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[dateFormatter setDateStyle:NSDateFormatterShortStyle];
NSString *dateString = [dateFormatter stringFromDate:date];
//NSLog(#"dateString from description is %#", dateString);
NSString *descriptionString = [[NSString alloc] initWithFormat:#"%d %# S:%d F:%d D:%#", _highScore, _themeChosen, _scoreStartLevel, _scoreFinishLevel, dateString];
return descriptionString:}
What I find confusing is that if I delete the save file - highScores.archive and run the app, it runs without problem. I stop and kill the app and then start it again - the first time encoding is called it crashes.
At the line where I encode the themeChosen object. I have read a few posts about decoding issues being fixed with a "retain" or changing to . format (why that would help, I don't really understand). But this is encoding. Decoding will probably be the next question...
I am not using ARC on this project. Maybe when I rebuild the whole thing from scratch...
Oh, I forgot to mention that everything was running smoothly as far as I had tested until I added in tracking the Theme variable. Then things went a smidge awry as mentioned here.
I think your problem is in your -initWithCoder. You're taking the result of -decodeObjectForKey: and directly assigning it to the synthesized ivar. Methods that don't have the word "copy" in their names are generally assumed to return autoreleased objects.
If you directly assign an autoreleased object to a variable, that object will be released in the next run loop, will dealloc itself, and now your variable points to junk memory. When you try to access it, you'll get an exec_bad_access.
What you should be doing is taking advantage of the accessor methods that #synthesize creates for you. Instead of
_themeChosen = [aDecoder decodeObjectForKey:#"_themeChosen"];
you should write
[self setThemeChosen:[aDecoder decodeObjectForKey:#"_themeChosen"]];
or, if you positively must use an equal sign, you could use the syntactic sugar of "dot notation":
self.themeChosen = [aDecoder decodeObjectForKey:#"_themeChosen"]
Which will ultimately be translated into close to the same thing.
The key is: the synthesized setter does more than simply assign the object to the ivar. It also retains the object (actually, in this case, it copies the object because you specified copy in your #property declaration). This is only one of the many, many, many reasons you should never access ivars directly and always use accessors -- especially now that they're essentially written for you automagically by #property/#synthesize.
UPDATE:
You're going to find you have trouble with scoreDateCreated seeing as you declared it as being readonly in its #property declaration. Why's it read only? It doesn't appear to be a derived value, so you're clearly going to have to assign something to it.
If you want it read/write in your object, but only want to expose a read only interface, you can redeclare the #property as read/write in an anonymous category at the top of FlipHighScores.m. So it looks read only to anything that includes the header, but is actually read/write inside your object's implementation.
Either I don't understand the Instruments Leaks tool at all, or I am going mad. I have run the tool on my iphone app, and it shows a couple of leaks. If I understand it correctly, for one of the leaks, it says that it is an NSDate object allocated by my method "writeHeading". The method that allocates the object is: "dateWithTimeIntervalSinceReferenceDate:". However, my writeHeading method does not use that method. In fact, that method is not used anywhere in my whole application.
Does anybody have an idea what could be going on here?
Here is the code of writeHeading:
- (void) writeHeading:(CLHeading *)heading
{
if (self.inFlight) {
[log writeHeading:heading];
} else {
IGC_Event *event = [[IGC_Event alloc] init];
event.code = 'K';
event.timestamp = heading.timestamp;
event.heading = heading;
[self addEvent:event];
[event release];
}
}
Here is a screenshot of Instruments:
And here is the definition of IGC_Event (as asked by multiple responders):
#interface IGC_Event : NSObject {
int code;
CLLocation *location;
CLHeading *heading;
NSString *other;
NSDate *timestamp;
}
#property int code;
#property (nonatomic, retain) CLLocation *location;
#property (nonatomic, retain) CLHeading *heading;
#property (nonatomic, retain) NSString *other;
#property (nonatomic, retain) NSDate *timestamp;
#end
#implementation IGC_Event
#synthesize code;
#synthesize location;
#synthesize heading;
#synthesize other;
#synthesize timestamp;
#end
Assuming no ARC, you need to make sure IGC_Event objects release their timestamp and other references that may have been retained or copied.
So in IGC_Event you need a dealloc something like this:
- (void) dealloc {
[timestamp release];
[location release];
[heading release];
[other release];
[super dealloc];
}
Leaks is just telling you where that timestamp object was created, not where you should have released it.
That may not be the only place you are leaking of course, but that's 4 potential leaks right there.
When the compiler runs your code, there are the methods directly called by you (which in your screenshot have a little person next to them) and then the methods that are invoked in the core frameworks as a result. The method in question results from this piece of code:
event.timestamp = heading.timestamp;
You could manage this process yourself if you wanted to:
NSDate *eventTimestamp = heading.timestamp;
event.timestamp = eventTimestamp;
Incidentally, storing that timestamp is entirely redundant and uses unnecessary memory, since you also store the heading with all its properties in event.heading so at any time you can access that timestamp with event.heading.timestamp. However, you may have other reasons for storing it separately.
Do you have the implementation of the IGC_Event class? Is it possible that the setter for its timestamp property is calling dateWithTimeIntevalSinceReferenceDate:? (Not an unreasonable thing to do, so far as I can tell. That would ensure that its timestamp is of class NSDate itself, and not a subclass. It would also ensure that it's independent of the timestamp that was passed in.)
(Disclaimer: I'm really not much of an Objective-C-er. If this seems like a stupid question, then it probably is!)
We have a lot of staff that are relatively new to iOS programming and memory management in general. I want to build an app with a couple of labels showing retain counts and a some buttons to increment and decrement those retain counts.
Does anyone know of anything out there already that would work or have any advice on setting this up so it will get my point across? I have a working version, but it doesn't seem to be working the way I think it should.
ViewController.h
#import <UIKit/UIKit.h>
#interface MemoryTestingViewController : UIViewController {
UILabel *retainCount;
UILabel *descLabel;
UIButton *addRetain;
UIButton *addRelease;
UIButton *access;
NSMutableString *myString;
}
#property (nonatomic, retain) IBOutlet UILabel *retainCount;
#property (nonatomic, retain) IBOutlet UILabel *descLabel;
#property (nonatomic, retain) IBOutlet UIButton *addRetain;
#property (nonatomic, retain) IBOutlet UIButton *addRelease;
#property (nonatomic, retain) IBOutlet UIButton *access;
#property (nonatomic, retain) NSMutableString *myString;
-(IBAction)pressedRetain:(id)sender;
-(IBAction)pressedRelease:(id)sender;
-(IBAction)pressedAccess:(id)sender;
#end
ViewController.m
-(IBAction)pressedAccess:(id)sender {
descLabel.text = #"Accessing myString, did we crash";
myString = [NSMutableString stringWithFormat:#"Accessing myString"];
retainCount.text = [NSString stringWithFormat:#"%i", [myString retainCount]];
}
-(IBAction)pressedRetain:(id)sender {
descLabel.text = #"Adding 1 to retain count for myString";
[myString retain];
retainCount.text = [NSString stringWithFormat:#"%i", [myString retainCount]];
}
-(IBAction)pressedRelease:(id)sender {
descLabel.text = #"Adding 1 release to myString";
[myString release];
retainCount.text = [NSString stringWithFormat:#"%i", [myString retainCount]];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
// init our variable string
myString = [[NSString alloc] init];
descLabel.text = #"myString retain count after alloc/init";
// fill our label with myString's retain count starting out
retainCount.text = [NSString stringWithFormat:#"%i", [myString retainCount]];
[super viewDidLoad];
}
When this runs, it seems fine, but crashes whenever I try press the retain button. If anyone has any advice how to clean this up a bit, I would appreciate it. Ideally I would like them to press the access button when the retain count hits zero and have the app crash, but the access button should work as long as the retain count is 1 or better. Thanks.
The retainCount of an object is tricky business.
If you were to continue down this path, you should be aware of the following details:
retainCount can never return 0
messaging a dangling pointer is not guaranteed to crash
retain count cannot be known once you have passed an object through any system API due to implementation details
any subclass of any system class may have an unknown retain count due to implementation details
retain count never reflects whether or not an object is autoreleased
autoreleases is effectively thread specific while the retain count is thread global
some classes are implemented with singletons some of the time (NSString, certain values of NSNumber)
the implementation details change from platform to platform and release to release
attempting to swizzle retain/release/autorelease won't work as some classes don't actually use those methods to maintain the retain count (implementation detail, changes per platform/release, etc..)
If you are going to teach retain/release, you should be treating the retain count as a delta and focus entirely on "If you increase the RC, you must decrease it".
retainCount is notoriously unreliable and the values it returns can be very strange. Check this post out: