How to make a global property (setter and getter)? - iphone

I need to create an object for example NSString and let other classes get/set the value for it.
thx. for help :)

The easiest way to do this is to attach the value as a property to a singleton instance of some class. One singleton instance that already exists in your application is the application delegate. So, just add an NSString property to your application delegate and you can access it from any class in your app (as long as you #import your application delegate).
In your application delegate:
#property(nonatomic, strong) NSString* someString;
In your other classes:
[self doSomethingWithAString:((YourAppDelegateClass*)[[UIApplication sharedApplication] delegate]).someString];

Create a Singleton/Shared class.
#implementation SINGLETON
static SINGLETON *instance = nil;
+(SINGLETON*)sharedInstance{
#synchronized(self) {
if (instance == nil) {
instance = [[SINGLETON alloc] init];
}
return instance;
}
}
EDIT:
This will come handy... Objective-C Singleton problem. Object recognized in one class but not another

Related

Objective-C: Singleton Pattern Usage

suppose I got a singleton class MySingleton as coded below.
Now is a singleton class just like any other class. I can have instance variables that are nonatomic and retain?
I can have: #property (nonatomic, retain) NSString* instanceVar in the .h file
and #synthesize instanceVar in the .m file?
static MySingleton* _sharedMySingleton = nil;
+(MySingleton*)sharedMySingleton
{
#synchronized([MySingleton class])
{
if (!_sharedMySingleton)
[[self alloc] init];
return _sharedMySingleton;
}
return nil;
}
+(id)alloc
{
#synchronized([MySingleton class])
{
NSAssert(_sharedMySingleton == nil, #"Attempted to allocate a second instance of a singleton.");
_sharedMySingleton = [super alloc];
return _sharedMySingleton;
}
return nil;
}
Yes, the instance of a singleton class behaves the same as a standard class, there is just one instance.
The pattern you have is overly complicated, there is no need for +(id)alloc
Here is a simplier pattern:
#implementation MySingleton
static MySingleton* _sharedMySingleton = nil;
+(MySingleton*)sharedMySingleton
{
#synchronized([MySingleton class])
{
if (!_sharedMySingleton)
_sharedSingleton = [[MySingleton alloc] init];
}
return _sharedMySingleton;
}
You bet. To the rest of your application, your singleton looks and works just like any other class. The only difference is that when your application tries to create a new singleton it always receives back the same object. But the singleton can have instance methods and instance variables just like any other class.
Not familiar with the annotations you mentioned because I'm a C++ developer, but a singleton can certainly have instance data. That's one of its values.
Yes you can have instance variables, a singleton is simply a regular class, where there is only one instance at any given time.

Can i have a single NSMutableArray in my multiple views application?

I have a navigational based application which has multiple views. Is it possible to use one single NSMutableArray for the whole applicaiton? Can i add objects to that NSMutableArray in one view and then remove object from the same NSMutableArray from some other view? I tried
myappAppDelegate *appDelegate = (myappAppDelegate *)[[UIApplication sharedApplication] delegate];
but it gives me null when i try to access appDelegate's array. If anyone can give me any idea or helping link or tutrorial. Thanks in advance.
If you are having multiple views in your application, and in that case you want to have a variable accessible to every view, you should always create a Model/Data(singleton) class and define the variable in it. Something like this :
//DataClass.h
#interface DataClass : NSObject {
NSMutableArray *arrGlobal;
}
#property(nonatomic,retain)NSMutableArray *arrGlobal;
+(DataClass*)getInstance;
#end
//DataClass.m
#implementation DataClass
#synthesize arrGlobal;
static DataClass *instance =nil;
+(DataClass *)getInstance
{
#synchronized(self)
{
if(instance==nil)
{
instance= [DataClass new];
}
}
return instance;
}
Now in your view controller you need to call this method as :
DataClass *obj=[DataClass getInstance];
obj.arrGlobal = arrLocal;
This variable will be accessible to every view controller. You just have to create an instance of Data class.
For your type of issue I would use a singleton.
http://en.wikipedia.org/wiki/Singleton_pattern
The appdelegate is a singleton too but you can reduce a bit the number of coded lines if you use your own singleton.
The AppDelegate approach should work, and you should probably figure out why it's not working, even if you go with a singleton.
The statement to get your appDelegate pointer appears to be correct, so I'm guessing that the pointer to the array is either not getting set (and retained) in your myappDelegate class, or you did not create the AppDelegate instance correctly in the first place.
On the Singleton approach add this
instance.arrGlobal = [[NSMutableArray alloc] init];
this way:
#synchronized(self)
{
if(instance==nil)
{
instance= [DataClass new];
instance.arrGlobal = [[NSMutableArray alloc] init];
}
}
return instance;
This way you can initilize the array and use it properly.

Accessing Class Data Members in ViewController.m (xCode / Objective C)

I have a class myClass and would like to access its properties, a NSArray *currentOptions (specifically to get the size of currentOptions and access the NSStrings which I've put in it.)
I have a method called generate options which assigns an filled array to *currentOptions. Generate options is called before I try to access *currentOptions. An instance of myClass has also been added to the ViewController via the App delegate. However when buttonOnePressed is called, I keep getting this error:
[myClass currentOptions]: unrecognized selector sent to instance 0x9b10490
Here is the parts of my code:
//TClass.h
#interface TClass : NSObject {
NSArray *currentOptions;
}
#property (nonatomic, retain) NSArray *currentOptions;
#end
//viewController
- (IBAction) buttonOnePressed:(id)sender {
NSLog(#"button1 pressed");
NSLog(#"int: %d",[myClass.currentOptions count]);
//myClass here is the instance of TClass
}
One thing that sometimes causes that error is failing to properly retain myClass. (Aside: "myClass" is a really bad name for a pointer because the thing being pointed to is almost certainly not a class but an object, i.e. an instance of a class.) If you don't retain the object that myClass points to, it will be deallocated. Sometimes, a different object happens to be created at that some location, and you end up sending a message meant for the original object to the new one, which is a different type and doesn't understand the message.
To all who have been following, the problem has been resolved by making the following changes:
1) Synthesized current options TClass.m
#implementation TClass
#synthesize currentOptions;
#end
2) I made currentOptions a NSMutableArray instead of a NSArray. This is because I need to reassign values to current options. Somehow it crashes with NSArray and everything goes smoothly with NSMutable array like such
#implementation TutorialClass
if ([currentOptions count] > 0) {
[currentOptions removeAllObjects];
}
[currentOptions addObject:[options objectAtIndex:0]];
[currentOptions addObject:[options objectAtIndex:1]];
[currentOptions addObject:[options objectAtIndex:2]];
[currentOptions addObject:[options objectAtIndex:3]];
3) And of course, I'll also have to do the following in the init method of TClass.m
currentOptions = [[NSMutableArray alloc] init];
Now its time to get some food. Thanks Caleb :D

Category-like extension for instance variables

Is there a way to somehow emulate category behavior for a class regarding to it's instance variables, not methods ?
I have a ClassA, and I want to keep its name after extending it with new methods AND ivars from other cllass (ClassB).
Of course, I can inherit ClassA, but resulting class will have different name.
For methods addition, it's not a problem - category would be a good solution.
UPDATE: ClassA used as file owner for a XIB, and these fields to be extended are IBOutlets. So I need them at build phase.
Since the iPhone uses the modern Objective-C runtime, you can use associative references to add data to instances without having to declare instance variables. See the documentation for objc_setAssociatedObject etc.
If you wrap the calls to the runtime in standard accessor methods, it will be very easy to use.
I've investigated this question playing around associative references (thanks to Ole), with methods static variables, methods swizzling, and finally come to this simple solution (no runtime stuff). I simply use "categorized" class only to return a pointer to a derived class, which of course can contain additional ivars. Doing so I achieve one unexpected benefit: I can call super's class methods, which is impossible when extending through categories.
Example of a class extension (tested):
ClassA+ClassB.h
#protocol _ClassB_Protocol
#optional // to avoid warnings
- (IBAction) onClick:(id)sender;
#property (nonatomic, retain) IBOutlet UIButton *aButton;
#end
#interface ClassA (_ClassA_Category) <_ClassB_Protocol>
#end
#interface ClassB: ClassA <_ClassB_Protocol> {
UIButton *aButton; // _ivar_ to add
}
#end
ClassA+ClassB.m
#implementation ClassA (_ClassA_Category)
// this will be called first on [ClassA alloc] or [ClassA allocWithZone:(NSZone *)zone]
+(id) alloc {
if ([self isEqual: [ClassA class]]) {
return [ClassB alloc];
} else {
return [super alloc];
}
}
#end
#implementation ClassB: ClassA
#synthesize aButton;
-(void) dealloc {
[aButton release];
[super dealloc]; // this is impossible for an ordinary category
}
- (void) onClick:(id)sender {
// some code here
}
#end
Now we have in the same time:
ClassB "extends" ClassA (category way);
ClassB inherits ClassA (ClassB can call ClassA methods);
ClassB can be accessed through ClassA name (category way)
I put Martin's example into a trivial app replacing ClassA with NSData, ClassB with XXData, and onClick with getIvar, and invoked it (Mac OS X 10.6.6, Xcode 4 Final) with:
NSData * data = [NSData data];
NSLog(#"%#", [data getIvar]);
It fails with "-[NSConcreteData getIvar]: unrecognized selector sent to instance" ..
It fails because "alloc" in the NSData category (which returns the pointer to the derived class) is not called by the above code. If, instead, "alloc" is called explicitly, as in:
NSData * data = [[NSData alloc] init];
NSLog(#"%#", [data getIvar]);
then all is well.

How to update a variable in one class from another?

I need to set a variable in Class A from Class B. To test this, I have a while loop running in Class A that continuously prints the variable via NSLog. However, no matter what I try, I cannot get Class B to update the variable in Class A in such a way that Class A can read the changes made by Class B. I am pretty sure I have everything hooked up properly in IB. Here's how I have things set up:
//Class A
#interface AppDelegate : NSObject {
NSString *teststring;
}
#property(readwrite,nonatomic,retain) NSString *teststring;
#end
#implementation AppDelegate
#synthesize teststring;
-(id)init{
self = [super init];
if(self) {
teststring = [[NSString alloc] init];
}
return self;
}
-(void)awakeFromNib
{
while(1){
NSLog(#"teststring is %#",teststring);
usleep(500000);
}
}
#end
//Class B
#class AppDelegate;
#interface otherClass : NSObject {
AppDelegate *appdel;
}
-(IBAction)doTest:(id)sender;
#end
#implementation otherClass
-(void)awakeFromNib
{
appdel = [[AppDelegate alloc] init];
}
-(void)doTest:(id)sender
{
appdel.teststring = #"Test";
NSLog(#"Set teststring to %#",appdel.teststring); //this works
}
#end
You are thinking too much about classes (as seemingly some sort of “department” of code) and not enough about objects, and both your wording and your problem demonstrate this.
You have your instance of otherClass creating a second instance of the AppDelegate class. You already had one AppDelegate instance, which is the actual application delegate (because, I assume, you have it in your nib and you have it hooked up to the application's delegate outlet there); now, in -[otherClass awakeFromNib], you are creating another.
You then tell this second AppDelegate instance to set its teststring property to #"Test", and then you ask your second AppDelegate instance for the value of that property, and your second AppDelegate instance dutifully shows you the value you gave it.
The first instance doesn't have the same value for its teststring property because the otherClass object never gave that instance a value for its teststring property. Note that the variables you define in the #interface section are instance variables, which is why different instances of a class can and usually will have different values in those variables. Properties are likewise per-instance, being usually backed by these instance variables.
AppDelegate A (the real application delegate, created in the nib) and AppDelegate B (created by the otherClass object, not anything's delegate) are two separate instances of AppDelegate, with separate teststring variables.
Thus, the solution: Have the otherClass instance talk to the real application delegate, not an AppDelegate instance that it created itself. You could ask the application for its delegate, or (if the otherClass object is in the MainMenu nib) give it an outlet to the application delegate, just like the application has.
However, piling too much stuff into your application delegate class is bad design; each class should have one specific purpose, and generally should fit neatly within one of the Model, View, and Controller classifications. So, assuming your otherClass object should be a controller, move the property into otherClass and make that object the controller of whatever needs the property.