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.
Related
I have a situation where I have copied a string in an instance of a helper class, retained it, and later released it during the dealloc of the view controller instance that alloc'd the helper class. This results in the dreaded EXC_BAD_ACCESS. I then went to Instruments to debug zombies. This gave me the following error:
An Objective-C message was sent to a deallocated 'CFString (immutable)' object (zombie) at address: blah blah
When I then look at the allocation summary within Instruments and work backwards from the zombie detection, the first time my code is listed is in the deallocation of the helper class instance. Here is what the helper class looks like. First the .h file:
#interface channelButtonTitles : NSObject {
NSString *channelTitle;
...
}
#property (nonatomic,copy) NSString *channelTitle;
...
#end
Then the .m file:
#implementation channelButtonTitles
#synthesize channelTitle;
...
- (void)dealloc {
[channelTitle release];
...
}
#end
Now relevant code from the view controller that uses the helper class looks like the following. In the .h file I have an array that will hold multiple objects of the helper class as follows:
#interface MyVC : UIViewController {
NSMutableArray *channelTitles;
...
}
#property (retain, nonatomic) NSMutableArray *channelTitles;
Then in the .m code, I synthesize channelTitles. I also have a dealloc method as follows:
- (void)dealloc {
[channelTitles release];
...
}
Finally, I alloc object instances of the helper class and store them in channelTitles with strings stored in the channelTitle elements of channelButtonTitles as follows:
[channelTitles removeAllObjects];
self.channelTitles = nil;
channelTitles = [[NSMutableArray alloc] init];
...
for (int i=0; i<numberOfTitles; i++) {
// For each mediaItem, get the title and subtitle info
channelButtonTitles *aChannelButtonTitle = [[channelButtonTitles alloc] init]; // create an object to hold the title and miscellaneous data
aChannelButtonTitle.channelTitle = #"some title";
[channelTitles addObject: aChannelButtonTitle]; // add the title
[aChannelButtonTitle release];
}
So, this is a technique I have used many times before, but seems to not be happy now. When the view controller is popped and I return to the root view controller, the dealloc method in my view controller is called. That releases channelTitles which results in calling dealloc on the channelButtonTitles helper class objects that are stored in channelTitles.
Since I have used copy in the property of my helper class, I assume I own this string. Hence, I am releasing it. If I comment out the [channelTitle release] line from my dealloc, the EXC_BAD_ACCESS goes away, but I suspect I have a memory leak now. Please help me see what I am doing wrong.
I'm using a singleton called CSAppData to store data for my iPhone app. I'm storing an object called CSInbox in the singleton. When I logout of my app, I want to clear the data for that object.
Here is my singleton code, including the method for clearing the data:
- (id)init {
self = [super init];
if (self)
{
self.inbox = [[CSInbox alloc] init];
}
return self;
}
+ (CSAppData *)appData {
static CSAppData * appDataInstance;
#synchronized(self) {
if(!appDataInstance) {
appDataInstance = [[CSAppData alloc] init];
}
}
return appDataInstance;
}
+(void) clearData {
CSAppData *appData = [CSAppData appData];
appData.inbox = [[CSInbox alloc] init];
}
However, in one of my view controllers, in the initWithCoder method, I'm storing the inbox variable:
-(id) initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if(self) {
self.inbox = [[CSAppData appData] inbox];
}
return self;
}
So, when the app logs out and the clearData method is called, the view controller is still pointing to the old CSInbox object. And even though I am initializing a new view controller and setting it to the root view controller (in the AppDelegate), like this:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
MainTabControllerViewController *viewController = (MainTabControllerViewController *)[storyboard instantiateViewControllerWithIdentifier:#"mainView"];
[self.window setRootViewController:viewController];
The one child view controller that has the CSInbox is never reinitialized, and is still pointing to that old CSInbox object. (I'm not sure why this is happening.)
So, what is the best way to solve this?
Change the clearData method in the singleton to just reset the properties of the CSInbox object, rather than alloc and init and new one?
Move the self.inbox = [[CSAppData alloc] init]; to the viewDidLoad in the view controller class so it gets set properly upon the second login?
Change the logout function in the AppDelegate so that the root view controller and all other view controllers are released, so they will reinitialize upon the second login?
I'm leaning toward #1 or #3...
As requested, here is CSInbox.h:
#interface CSInbox : NSObject
#property (nonatomic,strong) NSMutableArray *threads;
#property (nonatomic, assign) NSInteger newCount;
#property (nonatomic,strong) NSDate *lastUpdate;
-(void) setThreadsFromJSON:(NSDictionary *)json;
#end
And here is CSInboxViewController.h:
#interface CSInboxViewController : UITableViewController <UITableViewDataSource, UITableViewDelegate, CSThreadViewControllerDelegate>
#property (strong, nonatomic) IBOutlet UITableView *inboxTableView;
#property (strong,nonatomic) CSInbox *inbox;
#end
And CSAppData.h:
#interface CSAppData : NSObject {
CSInbox *inbox;
}
#property(nonatomic,strong) CSInbox *inbox;
+ (CSAppData *)appData;
+ (void)clearData;
#end
I think the answer lies not in destroying the singleton object and recreating it, but to actually clear the instance variables within that singleton object.
You don't show the declaration of [CSAppData inbox], but if it's an NSMutableArray, for example, then you can clear that, and any existing references to the singleton object can remain:
+(void) clearData {
CSAppData *appData = [CSAppData appData];
[appData.inbox removeAllObjects];
}
One way to handle this, complying with the spirit of using a singleton, is having your view controllers access directly your singleton inbox, i.e.: [CSAppData appData].inbox instead of self.inbox. This is a bit wordier, but it would "magically" fix your issue.
If that is not acceptable to you, I would go with option #1 of those you list. Even better, I would make the inbox in the singleton a singleton itself, or make sure it is never replaced by another instance.
EDIT:
Another approach you have, is using KVO in your controller so that it gets notified when the inbox object has changed. Don't know if it is quite worth it, but could be used.
I am developing an ARC enabled project. From a view controller I am pushing MyClass,
- (void)pushMyClass {
MyClass *myClass = [[MyClass alloc] init];
[self.navigationController pushViewController:myClass animated:YES];
}
After doing some operations I am popping MyClass. The problem here is that MyClass is not getting deallocated. Following is how the classes look.
/* MyHelperClassDelegate */
#protocol MyHelperClassDelegate <NSObject>
- (void)helperDidFinishHelping:(MyHelperClass *)helper;
#end
/* MyHelperClass Interface */
#interface MyHelperClass : NSObject {
__weak id <MyHelperDelegate> delegate;
}
#property(nonatomic, weak) id<MyHelperDelegate> delegate;
- (void)startHelping;
#end
/* MyHelperClass Implementation */
#implementation MyHelperClass
#synthesize delegate;
- (void)dealloc {
delegate = nil;
}
/* MyClass */
#interface MyClass : UIViewController <MyHelperClassDelegate> {
MyHelperClass *helper;
}
#implementation MyClass {
- (void)dealloc {
helper.delegate = nil;
}
- (void)getHelp {
helper = [MyHelperClass new];
helper.delegate = self;
[helper startHelping];
}
- (void)helperDidFinishHelping:(MyHelperClass *)helper {
}
}
MyHelperClass calls a web service using NSMutalbleURLRequest & NSURLConnection to fetch some data and saves it to user defaults.
One thing to notice here is, if I comment the line helper.delegate = self;, then MyClass gets deallocated.
What to do to make MyClass get deallocated when it is popped out of navigation controller?
Thanks.
Your delegate code looks correct (except your use of an ivar, you don't show a #synthesize so you may have _delegate and delegate both). Its quite likely that something else is retaining MyClass. What I suggest you do is add a NSLog to your MyClass dealloc. Then push it, and immediately hit the back button and see if its dealloc'd or not. If not, then take a hard look at what you do in viewDidLoad et al and start commenting out sections of that code until you can get the dealloc.
Also, I assume you don't keep a strong reference in the class that pushes the MyClass object.
I agree with Chuck that one cannot say much from the code provided. But one reason why the MyClass object is not deallocated might be that it is retained by your helper object since delegate is declared as strong, and the MyClass object has the property helper also declared as strong. In this case you had a retain cycle, and none of them can be released.
The trick could possibly lie within the fact that you use NSURLConnection. It is not specified how you use this class with the code that you've provided, but please note the special considerations referenced in the NSURLConnection class reference:
Special Considerations: During the download the connection maintains a
strong reference to the delegate. It releases that strong reference
when the connection finishes loading, fails, or is canceled.
I need to get things clear about Objective-C memory management:
If I declare an object in the class header as ivar without #property:
#interface MyFacebooDelegate : UIViewController
{
TableViewController *tableController;
}
...
#end
and some where in the code for example in - (void)viewDidLoad I do :
tableController = [[TableViewController alloc] init];
so where is best way to release it. What if I make the instant object a property what will be the different? and how the memory management will be too
#interface MyFacebooDelegate : UIViewController
{
TableViewController *tableController;
}
...
#end
#property (nonatomic, strong) TableViewController *tableController;
What the following syntax do exactly for the object viewController:
.h
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) ViewController *viewController;
#end
.m
#implementation AppDelegate
#synthesize window = _window;
#synthesize viewController = _viewController;
- (void)dealloc
{
[_window release];
[_viewController release];
[super dealloc];
}
.....
#end
If I want to return an object through a method to another class, do I need to autorelease it in the method body first and then retain it in receiver side?
for example this method what exactly to do in the method body and in the receiver side too:
-(NSString *)getFriendId
{
NSArray *ar = [NSArray arrayWithObjects:#"1",#"2",#"3", nil];
return [ar objectAtIndex:0];
}
I know this a lot but I am really confused and need your help.
1) best way is in dealloc; or right before re-setting it.
2) a property does the retain/release for you. But WARNING! You keep mixing up things. You use "strong" here, which relates to ARC. If you really insist on using classic retain/release (you shouldn't) then use (nonatomic, retain) instead.
3) Your properties get deallocated on dealloc. Again, strong is wrong here.
4) Yes. Ideally you should. Another reason why ARC is awesome, it does this all for you, automatically.
tl;dr: Use ARC. Never go back. (But still learn manual memory management)
ARC is the answer for your all memory management question. Very import note on Strong and Weak property in addition to ,
iOS Strong property: So strong is the same as retain in a property declaration before ARC. For ARC projects I would use strong instead of retain, I would use assign for C primitive properties.
iOS outlets should be defined as declared properties. Outlets should generally be weak, except for those from File’s Owner to top-level objects in a nib file (or, in iOS, a storyboard scene) which should be strong. Outlets that you create will therefore typically be weak by default, because: Outlets that you create to, for example, subviews of a view controller’s view or a window controller’s window, are arbitrary references between objects that do not imply ownership.
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.