In my app, when the user launched the app, I create an instance of a class in my AppDelegate and call a method in the class which compares all of the user's iOS contacts to find which ones are using my app, and puts those contacts into an NSMutableArray
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
GetContactClass *contact = [[GetContactClass alloc] init];
[contact getAllContacts];
...
}
GetContactClass.h
#property (retain, nonatomic) NSMutableArray *appContacts;
At the end of the getAllContacts method, I NSLog out appContacts and it works fine.
However, later in the app I try to set an NSMutableArray in a ViewController to equal appContacts, but I get a (null) array.
ViewController.m
self.searchableContacts = [[NSMutableArray alloc] init];
GetContactClass *contact = [[GetContactClass alloc] init];
self.searchableContacts = contact.appContacts;
What am I doing wrong here?
You're creating an entirely new instance which hasn't been asked to collect all contacts, so it hasn't stored them. By the look of the code the instance which has stored them has been destroyed. As you're running this on the main thread you might as well just ask the new instance to get contacts and delete the code for the old one. It's better however to run the contacts collection on a background thread and keep the result till you need it, in a retained instance variable.
If you want use this data in all app you must use a Pattern Design Singleton:
In objective-c:
#implementation Settings
+ (id)sharedInstance {
static Settings *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
Other way is change retain to strong but this is a bad idea and put this property in appDelegate and call all times APPDelegate get property.
Related
I have an app where i want to create a temporary cache which stores key and value.I have done the following
My code is : IN appDelegate.h
#property (strong, nonatomic) NSMutableDictionary *articleCache;
In appDelegate.m
#synthesize articleCache;
and i am calling it in viewController.m
here i need to store the data so that it is cleared only when the app is terminated and is accessible anywhere in the app otherwise.
every time i visit an article i add it to the array so that next time i wont have to fetch it from the network thereby speed up the process.
the Problem is when i set the temp NSMutableDictionary the content gets added but for checkCache.articleCache i get nil.
#define DELEGATE ((AppDelegate*)[[UIApplication sharedApplication]delegate])
this is my viewDidLoad method:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
//[self loadfeeds];
[self.activityIndi startAnimating];
AppDelegate *checkCache = DELEGATE;
NSString *link = self.webUrl;
//check if the article is already opened and cached before
if([[checkCache.articleCache allKeys] containsObject:link])
{
NSLog(#"Key Exists");
NSString *contents = [checkCache.articleCache valueForKey:link];
[self loadDataOnView:contents];
}
else
{
NSOperationQueue* aQueue = [[NSOperationQueue alloc] init];
[aQueue addOperationWithBlock:^{
NSLog(#"Key not Exists");
[self startParsing];
}];
}
}
In parser method at the end i do the following i.e to store the article..
but if i add it directly to the checkCache.articleCache nothing is added what should i do?? but it gets added to temp.. do i access the articleCache incorrectly??
AppDelegate *checkCache = DELEGATE;
NSMutableDictionary *temp = [[NSMutableDictionary alloc] init];
[checkCache.articleCache setObject:Content forKey:url];
[temp setObject:Content forKey:url];
So how can i solve it??
or Suggest me how can i use NSCache for the same problem. thanks a lot.
It might be a silly question but i m quite new to ios thanks.
In App delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.articleCache = [NSMutableDictionary new];
return YES;
}
When you have to set the object in cache.
AppDelegate *checkCache = DELEGATE;
[checkCache.articleCache setObject:obj forKey:#"Key1"];
To get the object back:
AppDelegate *checkCache = DELEGATE;
id obj = [checkCache.articleCache objectForKey:#"Key1"];
Though there are better ways to get this done.
I'm confused about how to use singleton effectively. I want my singleton class to behave like [NSUserDefaults standardUserDefaults]. But what I've observed is each time it is creating new object and I see a different memory address each time.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
MyManager *sharedManager = [MyManager sharedManager];
if(sharedManager.name.length==0) {
sharedManager.name = #"manager";
}
return YES;
}
+ (id)sharedManager {
static MyManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;}
each time at launch, if clause is executing.
What you want to achieve is not what a singleton does.
A singleton is an object for which there is only one instance while your application is running. If you call [MyManager sharedManager] 10 times in a row, you'll get the same address each time.
Once you quit the application, like any other object the singleton is destroyed. Next time you start the application, you'll get a brand new singleton being created without any of the old data.
NSUserDefaults is able to remember data even after quitting and restarting the application because it is persisting that data to disk (i.e. writing a file somewhere.) If you want to have similar behaviour, you'll also need to save you data to a file and read that file on startup.
I have a DataManager class which returns a shared instance:
+ (DataManager *)sharedInstance;
{
static DataManager *sharedInstance = nil;
static dispatch_once_t pred;
dispatch_once(&pred, ^{
sharedInstance = [[DataManager alloc] init];
});
return sharedInstance;
}
In here I keep track of my managedObjectContext, managedObjectModel, persistentStoreCoordinator.
I also have a method where I pull out items for displaying:
- (NSArray *)getItems
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Item"];
return [[self managedObjectContext] executeFetchRequest:fetchRequest error:nil];
}
Now in my main app I have a view controller when I call this getItems and then modify items individually. So for example set item.itemName = #"testName"; and then call my save method.
I also have an iOS 8 where in my TodayViewController I also call the getItems method. I have an NSNotification which detects for managedObjectContext saves.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(refetchItems) name:NSManagedObjectContextDidSaveNotification object:[[DataManager sharedInstance] managedObjectContext]];
These refetched items does get called but returns the outdated NSManagedObjects. So for example the itemName has not changed to #"testName".
Where am I going wrong? Let me know if you need to see any other code.
Thanks!
You may try the following for refreshing particular ManagedObject. And if you want to refresh a list of ManagedObject then loop each object and execute the command.
[_managedObjectContext refreshObject:act mergeChanges:YES];
Or for iOS verion 8.3 and above you can make use of the following method for updating all the ManagedObject in the context at once as follows.
[_managedObjectContext refreshAllObjects];
It works a bit, but only for data UPDATE, not for adding or deleting data.
If it is not working, you can add also
[_managedObjectContext reset];
after that, you have to read "reassign" all variables, that you have loaded from your core data store.
Another solution (slower and more ugly)
If the above is not working, another solution would be to delete current context and create it again.
I just set
_persistentStoreCoordinator = nil;
_managedObjectModel = nil;
_managedObjectContext = nil;
I have CoreDataManager class with this properties
#property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
#property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
And in class I have manually created setters. If I nill all variables, due to setters, they are inited again once I read them outside my core data manager class.
You can improve this by using NSUserDefault store. It is being updated correctly. In main app, if you change smething, set flag in NSUserDefault. In extension, read this and if flag is marked, reset core data. This way, you will save some ticks and make things faster a bit.
For allocation of NSUserDefault (in both apps - extension and main) use this - after that, you can read data from it as usuall and they should be in sync
NSUserDefaults *prefs = [[NSUserDefaults alloc] initWithSuiteName:GROUP_NAME]; //share with extension
I'm learning Objective C & Xcode, by doing my first App.
First a user has to sign in. And then he can do several things, like joining a group or changing his data (username, email,...).
The Login is finished and it works fine.
To the question:
Is it possible to set a variable which I can reach from every View Controller?
I tried it with the segues, but I think it's very hard to define this in every View Controller.
I'm searching for a global variable which I can reach from everywhere in the App, is this possible?
Or is there an other method to solve this?
Thank you for the help!
Emanuel
I would personally advise you to use the Singleton pattern.
I would not recommend putting everything into AppDelegate, this class is not meant for that.
Instead, what you can do is create a dedicated class (with a name like "ApplicationState", or whatever suits you), and put the properties you need in its header file, and having your singleton management code in the .m (and the prototype in the header)
If you need the singleton management code, here it is:
+ (ApplicationState*)sharedInstance
{
static ApplicationState* sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
Then, if you have in the header:
#property (nonatomic, strong) NSObject* object;
+ (ApplicationState*)sharedInstance;
You will be able to get this variable from anywhere by including the ApplicationState header file, and call:
[[ApplicationState sharedInstance] object];
You can use NSUserdefault if its simple .
Save:
[[NSUserDefaults standardUserDefaults]setObject:username forKey:#"userName"];
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(#"username saved = %#", username);
Read:
NSString *username = [[NSUserDefaults standardUserDefaults] objectForKey:#"userName"];
NSLog(#"username retrieved = %#", username);
You can use AppDelegate as well.
Define a property in your AppDelegate.h,
#property (nonatomic, strong) NSString *userName;
And then in your view controller, after importing AppDelegate.h,
AppDelegate *appDel = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSLog(#"%#", appDel.userName);
You can put them in plist or you can create Objective-C class initialize your variables within and import header in .pch file, then this class with data will available in every ViewController
hi i am new to ios dev ,I am trying to set a value for nsstring from delegate class and access from other class ,the value i get is null.i dont know what mistake i am doing?
//token class header file
#interface TokenClass : NSObject
{
NSString *tokenValue;
}
#property (nonatomic,strong) NSString *tokenValue;
//token class main file
#implementation TokenClass
#synthesize tokenValue;
#end
//App Delegate
TokenClass *token = [[TokenClass alloc]init];
[token setTokenValue:#"as"];
when i access tokenvalue in some other classs i get null value.
can any one point me what mistake i am doing?Am i using # property correctly?
There are a lot of ways to achieve what you want:
1. Usually I am using NSUserDefaults to save small amount of data which I will need even the user closed the app. There are a lot of information how to use it. See my answer here.
2. In your UIViewController class (e.x. your rootViewController) create #property which will hold your TokenClass. Then you will get tokenValue by self.tokenClass.tokenValue
3. The other way is create a singleton class which will be available during the whole run loop of your application. A Singleton candidate must satisfy three requirements:
controls concurrent access to a shared resource.
access to the resource will be requested from multiple, disparate
parts of the system.
there can be only one object.
+(TokenClass*) sharedTokenClass {
static dispatch_once_t pred;
static TokenClass *_sharedTokenClass = nil;
dispatch_once(&pred, ^{
_sharedTokenClass = [[TokenClass alloc] init];
});
return _sharedTokenClass;
}
You will use it it from any place you want by
[TokenClass sharedTokenClass]tokenValue];
If I were you, I would use the first variant.
PS. I strongly recommend you to read some memory management articles to get the point of object's lifecycle.
You need to use Singleton class to expose variables or objects to the entire project or create global variables. Create sharedInstance of TokenClass class and create property which can be accessed anywhere
in your .h file
//token class header file
#interface TokenClass : NSObject
#property (nonatomic,strong) NSString *tokenValue;
//create static method
+ (id)sharedInstance;
in .m file
#import "TokenClass.h"
#implementation TokenClass
#pragma mark Singleton Methods
+ (id)sharedInstance {
static TokenClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (id)init {
if (self = [super init]) {
tokenValue = [[NSString alloc] init];
}
return self;
}
#end
now in your appDelegate
#import TokenClass.h
#implementation AppDelegate
in `didFinishLaunchingWithOptions:`
[TokenClass sharedInstance] setTokenValue:#"as"];
in any class you can get value using
NSLog(#"tokenValue = %#", [[SingletonClass sharedInstance] tokenValue]);