I am creating ios app in xcode 4.2.
I have outside file with database. I dont wanna download data in every view. How should i create one global variable for tabbar application? And when should i upload this database before closing of application?
In iOS applications the model data is often kept in a singleton, rather than in a global variable. Here is an article briefly describing singletons in Objective-C.
You can load your data in the class method that initializes your shared singleton. Uploading the data back is a bit trickier, because the singleton itself does not know when to do it. Therefore you should make an instance method -(void)uploadData in your singleton class, and call that method when your application is about to close. applicationWillResignActive: method of your application delegate is a good place to initiate the upload.
I use singletones like this: in class DataBase with some arrays of data i implement share method:
+(id)share
{
static id share = nil;
if (share == nil) {
share = [[self alloc] init];
}
return share;
}
and then in some classes: self.dataBase = [DataBase share];
You can create global variables by doing this
extern NSString *someString;
#interface ......
#property (strong, nonatomic) NSString *someString;
#end
#implementation ......
#systhesize someString;
NSString *someString;
#end
Related
Hi am a experienced Java programmer.I've just stepped into learning Objective-C and XCode.I am unable to understand
a very basic but important thing which is not letting me progress any further.
I want to create objects of classes defined by me and then access and use those objects in the View Controller methods of different
Views in the Storyboard.
I don't know where and how to create the objects so they could be accessed in any View.In Java I can do everything in main but the
structure of Objective C is confusing me.
For example I have a class
#interface list : NSObject
{
NSMutableArray* ary;
}
#end
I want to create objects of this class and use them those objects in the methods of different View Controllers.
How can I do so?
Please can somebody just give me a to the point answer about where to create the objects so they could become accessible in View Controller methods.
I have seen far to complex answers but not basic ones
PS: I'm using XCode 4.4
I think you should read some books or tutorials.
Some books:
http://www.amazon.com/Programming-Objective-C-5th-Developers-Library/dp/032188728X/ref=sr_1_3?s=books&ie=UTF8&qid=1351547991&sr=1-3&keywords=stephen+kochan
http://www.amazon.com/Learn-Objective-C-Java-Developers-Series/dp/1430223693
http://www.amazon.com/Objective-C-Absolute-Beginners-iPhone-Programming/dp/1430228326
For tutorial - just google it - one of them:
http://mobile.tutsplus.com/tutorials/iphone/learn-objective-c-day-1/
Say, you have mainViewController class where you want to use your list class then you can do something as follows:
// mainViewController.h
#import "list.h"
#interface mainViewController
#property (nonatomic, strong) list *objList;
#end
// mainViewController.m
#implementation mainViewController
#synthesize objList = _objList;
- (void) viewDidLoad {
self.objList = [[list alloc] init];
}
- (void) someMethod {
self.objList.ary = ...;
}
#end
I have a singleton that I'd like to use to manage the onscreen animation of my views. Here's my.
#import <Foundation/Foundation.h>
#interface OAI_AnimationManager : NSObject {
NSMutableDictionary* sectionData;
}
#property (nonatomic, retain) NSMutableDictionary* sectionData;
+(OAI_AnimationManager* )sharedAnimationManager;
- (void) checkToggleStatus : (UIView* ) thisSection;
#end
.m file
#import "OAI_AnimationManager.h"
#implementation OAI_AnimationManager
#synthesize sectionData;
+(OAI_AnimationManager *)sharedAnimationManager {
static OAI_AnimationManager* sharedAnimationManager;
#synchronized(self) {
if (!sharedAnimationManager)
sharedAnimationManager = [[OAI_AnimationManager alloc] init];
return sharedAnimationManager;
}
}
- (void) checkToggleStatus : (UIView* ) thisSection {
//get the section data dictionary
NSLog(#"%#", sectionData);
}
#end
You'll see in the .h file I added a NSMutableDictionary and am using #property/#synthesize for it's getter and setter.
In my ViewController I instantiate the animation manager as well as a series of subclasses of UIView called Section. With each one I store the data (x/y w/h, title, etc.) in a dictionary and pass that to the dictionary delcared in animation manager. In the Section class I also instantiate animation manager and add a UITapGestureRecognizer which calls a method, which passes along which section was tapped to a method (checkToggleStatus) in animation manager.
As you can I see in the method I am just logging sectionData. Problem is I am getting null for the value.
Maybe my understanding of singletons is wrong. My assumption was the class would only be instantiated once, if it was already instantiated then that existing object would be returned.
I do need all the other Section classes data as if one animates others animate in response and I can get around it by passing the tapped Section to the animation manager and doing [[Section superview] subviews] and then looping and getting the data from each that way but it seems redundant since that data is available in the ViewController when they are created.
Am I doing something wrong in trying to transfer that data? Is there a better solution? I am open to suggestions and criticisms.
Thanks
h file
#interface OAI_AnimationManager : NSObject
#property (nonatomic, retain) NSMutableDictionary* sectionData;
+(OAI_AnimationManager* )sharedAnimationManager;
- (void) checkToggleStatus : (UIView* ) thisSection;
#end
m file
static OAI_AnimationManager* _sharedAnimationManager;
#implementation OAI_AnimationManager
#synthesize sectionData = _sectionData;
+(OAI_AnimationManager *)sharedAnimationManager {
#synchronized(self) {
if (!_sharedAnimationManager) {
_sharedAnimationManager = [[OAI_AnimationManager alloc] init];
}
}
return _sharedAnimationManager;
}
- (void) checkToggleStatus : (UIView* ) thisSection {
//get the section data dictionary
NSLog(#"%#", _sectionData);
}
#end
Notice I moved your sectionData variable from the header and moved it to the implementation file. A while back, they changed it to where you can synthesize properties and specify their instance variable names along side it... hence:
sectionData = _sectionData;
I also added and underscore to the instance variable... this is a universal convention for private variables and it also will throw a compile error now if you try to type just sectionData as you did in the return statement of checkToggleStatus:. Now you either have to type self.sectionData or _sectionData.
You didn't include the code that creates an instance of your dictionary but I bet you didn't set it as self.sectionData = [[NSDictionary alloc] init] which means it would not retain the value and you would get null the next time you called it. Classic memory management mistake... I know it well because I learned the hard way hehehe
In my application there are lot of view controller in some view controller some variables are there which i want to use in other classes .my variable is not present in application delegate file so i can i make it global to use every where in my application?
In my opinion, how about using singleton pattern? So when you want to use the variables of that class, just get instance and then use the variables.
#interface MySingletonViewController : UIViewController
{
//here your variables
int globalVariables;
}
#property (nonatomic, assign) int globalVariables;
+ (MySingletonViewController *)sharedSingleton;
#end
#implementation MySingletonViewController
#synthesize globalVariables;
static MySingletonViewController *sharedSingleton = nil;
+ (MySingletonViewController *)sharedSingleton
{
#synchronized(self)
{
if (sharedSingleton == nil)
sharedSingleton = [[MySingleton alloc] init];
return sharedSingleton;
}
}
#end
UIViewController is class actually, so we can do this way : ) Hope this helpful.
Sure you can, but using global variables through entire app is definitely broken architecture design.
As Objective-C based on C, you can define variable (in you case - pointer to class) in any *.m file outside implementation part as:
MyVeryOwnClass *g_MyVeryOwnClassPointer = nil;
And access it as:
extern MyVeryOwnClass *g_MyVeryOwnClassPointer;
/* do some operations with your pointer here*/
Or move extern declaration to header file.
PS: You can use singletons. They are not the best solution, but better then using raw variable.
Early warning - code sample a little long...
I have a singleton NSMutableArray that can be accessed from anywhere within my application. I want to be able to reference the NSMutableArray from multiple NIB files but bind to UI elements via NSArrayController objects. Initial creation is not a problem. I can reference the singleton NSMutableArray when the NIB gets loaded and everything appears fine.
However, changing the NSMutableArray by adding or removing objects does not kick off KVO to update the NSArrayController instances. I realize that "changing behind the controller's back" is considered a no-go part of Cocoa-land, but I don't see any other way of programmatically updating the NSMutableArray and letting every NSArrayController be notified (except it doesn't work of course...).
I have simplified classes below to explain.
Simplified singleton class header:
#interface MyGlobals : NSObject {
NSMutableArray * globalArray;
}
#property (nonatomic, retain) NSMutableArray * globalArray;
Simplified singleton method:
static MyGlobals *sharedMyGlobals = nil;
#implementation MyGlobals
#synthesize globalArray;
+(MyGlobals*)sharedDataManager {
#synchronized(self) {
if (sharedMyGlobals == nil)
[[[self alloc] init] autorelease];
}
return sharedMyGlobals;
}
-(id) init {
if(self = [super init]) {
self.globals = [[NSMutableArray alloc] init];
}
return self
}
// ---- allocWithZone, copyWithZone etc clipped from example ----
In this simplified example the header and model for objects in the array:
Header file:
#interface MyModel : NSObject {
NSInteger myId;
NSString * myName;
}
#property (readwrite) NSInteger myId;
#property (readwrite, copy) NSString * myName;
-(id)initWithObjectId:(NSInteger)newId objectName:(NSString *)newName;
#end
Method file:
#implementation MyModel
#synthesize myId;
#synthesize myName;
-(id)init {
[super init];
myName = #"New Object Name";
myId = 0;
return self;
}
#end
Now imagine two NIB files with appropriate NSArrayController instances. We'll call them myArrayControllerInNibOne and myArrayControllerInNib2. Each array controller in the init of the NIB controller sets the content of the array:
// In NIB one init
[myArrayControllerInNibOne setContent: [[MyGlobals sharedMyGlobals].globalArray];
// In NIB two init
[myArrayControllerInNibTwo setContent: [[MyGlobals sharedMyGlobals].globalArray];
When each NIB initializes the NSArrayController binds correctly to the shared array and I can see the array content in the UI as you would expect. I have a separate background thread that updates the global array when content changes based on an external event. When objects need to be added in this background thread, I simply add them to the array as follows:
[[[MyGlobals sharedMyGlobals].globalArray] addObject:theNewObject];
This is where things fall apart. I can't call a willChangeValueForKey and didChangeValueForKey on the global array because the shared instance doesn't have a key value (should I be adding this in the singleton class?)
I could fire off an NSNotification and catch that in the NIB controller and either do a [myArrayControllerInNibOne rearrangeObjects]; or set the content to nil and reassign the content to the array - but both of these seems like hacks and. moreover, setting the NSArrayController to nil and then back to the global array causes a visual flash within the UI as the content is cleared and re-populated.
I know I could add directly to the NSArrayController and the array gets updated, but I don't see a) how the other NSArrayController instances would be updated and b) I don't want to tie my background thread class explicitly to a NIB instance (nor should I have to).
I think the correct approach is to either fire off the KVO notification somehow around the addObject in the background thread, or add something to the object that is being stored in the global array. But I'm at a loss.
As a point of note I am NOT using Core Data.
Any help or assistance would be very much appreciated.
Early warning - answer a little long…
Use objects that model your domain. You have no need for singletons or globals, you need a regular instance of a regular class. What Objects are your storing in your global array? Create a class that represents that part of your model.
If you use an NSMutableArray as storage it should be internal to your class and not visible to outside objects. eg if you are modelling a zoo, don't do
[[[MyGlobals sharedMyGlobals].globalArray] addObject:tomTheZebra];
do do
[doc addAnimal:tomTheZebra];
Dont try to observe a mutable array - you want to observe a to-many property of your object. eg. instead of
[[[MyGlobals sharedMyGlobals].globalArray] addObserver:_controller]
you want
[doc addObserver:_controller forKeyPath:#"animals" options:0 context:nil];
where doc is kvo compliant for the to-many property 'anaimals'.
To make doc kvo compliant you would need to implement these methods (Note - you don't need all these. Some are optional but better for performance)
- (NSArray *)animals;
- (NSUInteger)countOfAnimals;
- (id)objectInAnimalsAtIndex:(NSUInteger)i;
- (id)AnimalsAtIndexes:(NSIndexSet *)ix;
- (void)insertObject:(id)val inAnimalsAtIndex:(NSUInteger)i;
- (void)insertAnimals:atIndexes:(NSIndexSet *)ix;
- (void)removeObjectFromAnimalsAtIndex:(NSUInteger)i;
- (void)removeAnimalsAtIndexes:(NSIndexSet *)ix;
- (void)replaceObjectInAnimalsAtIndex:(NSUInteger)i withObject:(id)val;
- (void)replaceAnimalsAtIndexes:(NSIndexSet *)ix withAnimals:(NSArray *)vals;
Ok, that looks pretty scary but it's not that bad, like i said you don't need them all. See here. These methods dont need to be part of the interface to your model, you could just add:-
- (void)addAnimal:(id)val;
- (void)removeAnimal:(id)val;
and write them in terms of the kvc accessors. The key point is it's not the array that sends notifications when it is changed, the array is just the storage behind the scenes, it is your model class that send the notifications that objects have been added or removed.
You may need to restructure your app. You may need to forget about NSArrayController altogether.
Aaaaaannnnnyyywaaayyy… all this gets you nothing if you do this
[[[MyGlobals sharedMyGlobals].globalArray] addObject:theNewObject];
or this
[doc addAnimal:tomTheZebra];
from a background thread. You can't do this. NSMutableArray isn't thread safe. If it seems to work then the best that will happen is that the kvo/binding notification is delivered on the background as well, meaning that you will try to update your GUI on the background, which you absolutely cannot do. Making the array static does not help in any way i'm afraid - you must come up with a strategy for this.. the simplest way is performSelectorOnMainThread but beyond that is another question entirely. Threading is hard.
And about that static array - just stop using static, you don't need it. Not because you have 2 nibs, 2 windows or anything. You have an instance that represents your model and pass a pointer to that to you viewControllers, windowControllers, whatever. Not having singletons/static variables helps enormously with testing, which of course you should be doing.
How do i send information between two views (and hence, two classes)? Am I looking for my app delegate? Is there a better or alternative way?
If you want to send information back, you can use target-action (the way UIControl does), or you can send NSNotifications, or use a generic delegate protocol. Unless this is information of use throughout your application, putting it into your app delegate may be overkill.
I would use the Application Delegate. Or, if one view owns the other, you can initialize them together and keep the main reference to it in the class.
I always find it useful to have a global Context object to keep global information among views. This information could be, configuration information, device current orientation, database handlers, etc.
For the variables you need cross-access for, you can use Properties.
class VC1 : UIViewController {
NSString* v1;
NSString* v2;
}
#property (copy) NSString *v1;
#property (copy) NSString *v2;
And then, in the other view:
class VC2 : UIViewController {
VC1 *vc1;
}
And in you message implementations in VC2 you can use VC1's v1 and v2 like this:
- (void) someMessage {
NSLog(#"VC1's v1 value is %# and v2 value is %#", [vc1 v1], [vc1 v2]);
}
Hope it helps.