I have viewController with a global property 'notesArray", and I want to get it from AppDelegate through singleton.
Here is my appViewController.h
#import <UIKit/UIKit.h>
#interface AppViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
{
NSMutableArray *notesArray;
}
#property (nonatomic, strong) NSMutableArray *notesArray;
+ (AppViewController*)sharedManager;
#end
Here is my AppViewController.m
#implementation AppViewController
#synthesize notesArray;
+ (AppViewController*)sharedManager {
static AppViewController *sharedManager;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedManager = [[self alloc] init];
});
return sharedManager;
}
...
#end
My app works fine, but I need to save it's state in NSUserDefaults, so in AppDelegate/applicationDidEnterBackground: I make
- (void)applicationDidEnterBackground:(UIApplication *)application {
[[NSUserDefaults standardUserDefaults] setObject:[AppViewController sharedManager].notesArray forKey:#"savedNotes"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
But the problem is that [AppViewController sharedManager].notesArray = nil.
I tried to set breakpoint to applicationDidEnterBackground: and all data from [AppViewController sharedManager] = nil.
Few observations:
notesArray is not a global property. It is a property on instances of AppViewController. Now in #2 we see that you intend it to be a singleton, but since ObjC doesn't really have a good way to prevent someone else from creating a second object of this type, are you sure you're not creating a second copy e.g. by creating an object of that type in a XIB? This is probably not your problem (see #3), but I just wanted to cover all the bases.
Your sharedManager method implies that you're seeing AppViewController as a singleton. That's an unusual thing to do with a view controller. Usually you would have several view controllers, and the singleton would be part of the model they share. Even better, you'd just give each view controller a pointer to the model object that has this array, and if you ever need a second list, everything will just work. Also, why is it called "manager"? It's a view controller.
Have you stepped through your code in the debugger to see what is going on? What value does notesArray have while you're trying to use it in applicationDidEnterBackground:? I do not see anywhere in your code that you initialize notesArray. So you just have an empty pointer to an object (i.e. notesArray is likely nil). You probably meant to add an init method to your singleton's class that creates an NSMutableArray instance and remembers its address in notesArray?
Related
I am getting confused about how to use shared instance.
This is my set up:
+ (ViewController *)sharedInstance {
static ViewController *_sharedInstance = nil;
if (_sharedInstance == nil) {
_sharedInstance = [[ViewController alloc] init];
}
return _sharedInstance;
}
I also have a property in the ViewController.h file called
#property (nonatomic, retain) CBPeripheral * selectedPeripheral; and under the interface in the ViewController.m file CBPeripheral * _selectedPeripheral; and I make sure to synthesize them
So whenever I do this. It works.
[ViewController sharedInstance].selectedPeripheral = _selectedPeripheral;
NSLog(#"_sharedInstance %#", [[ViewController sharedInstance] selectedPeripheral]);
But if I do this? Without doing the line above. It returns null?
I thought the synthesize would make it so I don't have to assign it again?
NSLog(#"_sharedInstance %#", [[ViewController sharedInstance] selectedPeripheral]);
You are not assigning any value to _selectedPeripheral, thus, is has no value.
Try setting _selectedPeripheral inside your singleton initializer.
I don't know anything about the code, but it implies something needs to be selected. You may have an issue in your selection code as well, but you're class won't magically create objects because it is a singleton ;-)
EDIT
#synthesize only creates the instance variavle (the underscored), so it is available from inside your class. Note this is only necessary, if you overwrite getter and setter as well.
Also note that you posted the getter of the singleton and you have problems with a property. Please poste the code we need to see to help you without guessing.
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.
Say I have the following singleton:
#interface ABCSingleton: NSObject
#property (nonatomic, copy) NSString *name;
#property (nonatomic, strong) ABCViewController *mainViewController;
#end
#implementation ABCSingleton
+ (ABCSingleton *)sharedInstance {
static ABCSingleton *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [ABCSingleton new];
});
return instance;
}
- (void)doSomething {
}
#end
If doSomething contained this code:
- (void)doSomething {
self.mainViewController.tapBlock = ^() {
self.name = #"abc";
};
}
... it would create a retain cycle since ABCSingleton owns mainViewController, which owns tapBlock, which owns ABCSingleton.
What if instead of using self, I used sharedInstance?
- (void)doSomething {
self.mainViewController.tapBlock = ^() {
[ABCSingleton sharedInstance].name = #"abc";
};
}
Would it still create a retain cycle? (An explanation as to why, or why not, would be appreciated!)
To the specific question, this is a retain loop in the first case, and equivalent to a retain loop in the second case (equivalent in that mainViewController will never be released).
This indicates a deeper design problem. A singleton should never reference a view controller. Singletons are by nature model objects. Model objects should never reference controller objects directly. See Model-View-Controller for an introduction. This is a key design pattern in Cocoa.
Instead, the view controller should know about the model (the singleton in this case). doSomething should modify the model, and the view controller, when it comes on the screen, should read the model to set tapBlock. The view controller can also observe the model (via KVO, notifications, or delegation) while it is onscreen to know when to change its values.
I used to declare variale in appdelegate and make it as sharable in all the classes (If the variable is global ) .
appDelegate = (StoryAppDelegate*)[[UIApplication sharedApplication]delegate];
this was the code am using normally to access appdelegate variable. Now am trying a story board application and its not working for me . while declaring appdelegate it shows an error "Unknown type name StoryAppDelegate".
StoryAppDelegate*ss;
this is the code am using .
Any help appreciated .
Just don't use the app delegate. That isn't what it's for.
Instead, create a specific class to own the responsibility + knowledge, make it a singleton and have all classes that require it get it via it's 'sharedController' (or whatever you call it) class method.
Or use a 'constants' file with a static variable or something (just not the app delegate).
Storyboard is used only for design, not changes to the code.
For that, you'd use:
AppDelegate *app;
in the header file of the view's controller.
And in the implementation file,
app=(AppDelegate *)[[UIApplication sharedApplication]delegate];
Then you can just use app.yourVariable
I would recommend using a singleton instance of your global variable as they have bailed me out of your exact situation multiple times. Here is an example I'm currently using to implement a singleton. This methodology is also ARC-safe as well
mySingleton.h
#import <Foundation/Foundation.h>
#interface mySingleton : NSObject {
}
+ (NSMutableDictionary *) myMutableDict;
#end
mySingleton.m
#import "mySingleton.h"
#implementation mySingleton
+ (NSMutableDictionary *)myMutableDict
{
static NSMutableDictionary *singletonInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singletonInstance = [[NSMutableDictionary alloc]init];
});
return singletonInstance;
}
#end
As long as you include mySingleton.h in all of your view controllers you can access the data via [mySingleton myMutableDict]. For example: [[mySingleton myMutableDict] setObject:myObject forKey:myKey]; This will of course work with any object type.
It seems the case of circular dependency.
use #class StoryAppDelegate;
instead of
#import "StoryAppDelegate.h"
in your header file.
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.