Access Variables in different classes and Globals in objective c - objective-c

in my app i have multiple classes :D, the thing is i need to access a variable from class a in class b. For this purpose i use synthesize, i declare my variable in my .h between interface brackets, then declare a property of the variable wich is a NSString. Then i use synthesize :). But when i access it in class b i have a NSString equal to nil :§. When i breakpoint to check if the NSString get filled it works the NSLog shows the right string in the classe a, but in class b i got nil :§. Secondly i would like to have your opinion : it is better to use a global like NSUserDefaults or a old school c global declaration or this "synthesize" way of doing it ? If anyone would like to help me please, thank you a lot :).
//polymorphViewController.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "LCYDataBackedTableView.h"
#import "RootViewController.h"
#import "LCYLockSettingsViewController.h"
#import "LockScreenAppDelegate.h"
#import "MyWebViewController.h"
#import "MemberViewController.h"
#import "SharedViewController.h"
#import "UploadedViewController.h"
#interface PolymorphViewController : LCYDataBackedTableView{
NSString *secondId;
}
#property (readwrite,nonatomic) NSString *secondId;
#end
//in class a .h
#interface firstViewController : UITableViewController <UISearchBarDelegate, UISearchDisplayDelegate>{
IBOutlet UITableView * newsTable;
UISearchBar *searchBar;
UISearchDisplayController *searchDisplayController;
...
NSString *idNumber;
}
#property (readwrite, retain) NSString *idNumber;
- (void)launchIt:(NSString *)typeofdata sharedOrUploaded:(BOOL)smogogo;
#end
//in class a .m
#implementation firstViewController
#synthesize idNumber
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
int storyIndex = [indexPath indexAtPosition: [indexPath length] - 1];
idNumber = [[stories objectAtIndex: storyIndex] objectForKey: #"sizing_id"];
PolymorphViewController *polymorph = [[PolymorphViewController alloc]init];
[[self navigationController] pushViewController:polymorph animated:YES];
[polymorph viewDidLoad];
}
//in class B .m
-(void)dataByChoice: (NSString *)command{
self.title = #"Test";
myWebView = [[UIWebView alloc] initWithFrame:self.view.frame];
[self.view addSubview:myWebView];
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *defaults = [prefs stringForKey:#"myKey"];
NSString *defaults2 = [prefs stringForKey:#"mySecondKey"];
NSString *username = defaults;
NSString *password = defaults2;
firstViewController *WC = [[firstViewController alloc]initWithNibName:nil bundle:nil];
NSLog(WC.idNumber); <-- equals nil :(
NSString *idName = WC.idNumber;
NSString *partialUri = [#"http://www.google.com" stringByAppendingString: idName];
NSString *finalUri = [partialUri stringByAppendingString: command];
NSURL *url = [NSURL URLWithString:finalUri];

your class be should look like this .h file
#interface Class_B : UIViewController
#property (copy,nonatomic) NSString *someValue;
#end
.m file
#implementation Class_B
#synthesize someValue = _someValue;
- (void) viewDidLoad {
_someValue = #"This is my come value";
}
#end
and calling class (Class_A) it should be like this
- (void) viewDidLoad {
Class_B *cls_b = [[Class_B alloc] initWithNibName:#"Class_B_nib" bundle:nil];
NSLog(#"Some value echo :%#",cls_b.someValue);
}

Use NSUserDefaults when you need to store locally data among sessions of the app, I would not use it just to share variables among classes.
#synthesize directive is used to generate accessors methods for properties (nowadays, unless you want to change the name of setters and getters, you don't even need it) declared with #property directive.
If you just need to access the property from another object then a property declared in the interface file (.h) is perfect. If you need to share a property across the app, I am thinking about the singleton pattern but there are definitely many ways to do it.

Related

How to create a key per initiated object using NSUserdefaults

I have a custom NSObject and I instantiate this object using a manager.
My NSObjets are passed into a mutable array so that I can access their properties(name) via uipickerview once save method is initialised.
When I close my app, all my objects disappear as expected.
I would like to save my objects in the NSUserDefaults so I have implemented the NSUserdefaults in my manager. This is to save my objects so that they are loaded when the application is launched in the uipickerviewcontroller.
However my application crashes when I load the uipickerviewcontroller.
Here is the log after the crash:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[PacsServer encodeWithCoder:]: unrecognized selector sent to instance 0x7fd0a2e7fa20'
PacsServer.h
#import <Foundation/Foundation.h>
#interface PacsServer : NSObject
#property ( nonatomic) NSString *pacsName;
#property (nonatomic) NSString*username;
#property ( nonatomic) NSString*password;
#property (nonatomic) NSString *wadoUrl;
#property ( nonatomic) NSString *apiDirectory;
#property ( nonatomic) NSString *dataArrayIncludesUsernamePasswordWadoLoginWebServiceAndSecurity;
#end
pacsserver.m
#import "PacsServer.h"
#implementation PacsServer
#end
manager.h
#import <Foundation/Foundation.h>
#interface PacsServerManager : NSObject
+(instancetype)pacsManager ;
#property NSMutableArray *pacsServers;
#end
Manager.m
#import "PacsServerManager.h"
#import "PacsServer.h"
#implementation PacsServerManager
+(instancetype)pacsManager {
static id instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
-(id) init {
if(self =[super init]){
PacsServer *server= [[PacsServer alloc] init];
_pacsServers = [NSMutableArray arrayWithObjects:server, nil];
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
NSData *serverData = [NSKeyedArchiver archivedDataWithRootObject:_pacsServers];
[standardUserDefaults setObject:serverData forKey:#"server"];
[standardUserDefaults setObject:_normalArray forKey:#"Servers" ];
[standardUserDefaults synchronize];
}
return self;
}
#end
Here in the save method below I get the username, password and other fields from the text fields. To be able to save all these into keychain.
-I first save these in an array then pass this array to NSData using datawithJsonObject.
Then save this array as NSString into keychain
Add nsobjects via manager
#import "AddPACSViewController.h"
#import "PacsServerManager.h"
#import "PacsServer.h"
#import <KeychainItemWrapper.h>
#interface AddPACSViewController ()
#end- (void)saveForAddPacs:(id)sender {
.
.
.
.
NSString *pacsDataArray = [keychain objectForKey:(__bridge id)kSecValueData];
PacsServer *newPacsServer = [[PacsServer alloc] init];
newPacsServer.pacsName= pacsNameFromAddPacsView;
newPacsServer.dataArrayIncludesUsernamePasswordWadoLoginWebServiceAndSecurity = pacsDataArray;
[[PacsServerManager pacsManager].pacsServers addObject:newPacsServer];
[self performSegueWithIdentifier:#"Save" sender:self];
}
Access the objects in uipickerview
pickerviewcontroller.m
- (NSString*)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
NSLog(#"Pacs data in settingsview:%#", [PacsServerManager pacsManager].pacsServers);
PacsServer *server = [[PacsServerManager pacsManager].pacsServers objectAtIndex:row];
NSString *name = server.pacsName;
NSLog(#"Servername:%#, and the data %#", server.pacsName, server.dataArrayIncludesUsernamePasswordWadoLoginWebServiceAndSecurity);
NSLog(#"%#", [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]);
return name;
}
Any help would be appreciated.
Thank you.
You need to address a couple of things here.
1) The exception is caused because of this line:
NSData *serverData = [NSKeyedArchiver archivedDataWithRootObject:_pacsServers];
When you archive any object, that object needs to implement the <NSCoding> protocol. When you archive aggregate objects (like NSArray and NSSet) each of the objects that are contained in the array or set need to implement the <NSCoding> protocol also. In this case PacsServer is being sent an encodeWithCoder: message which it doesn't implement.
See this tutorial on NSCoding.
2) Is it really a good idea to be persisting these objects in the NSUserDefaults? NSUserDefaults is really designed for small bits of information like whether you should show a help item, or is this the first time the user has launched your app. Perhaps you'd be better off saving this data in a separate .dat file or even using a different method like CoreData or sqlite (if you have a large amount of data).

NSString works with one method but not the other

I have two different methods of trying to grab a variable from another class. The first one, which I would prefer using doesn't work - The second one, which I don't prefer does. Could someone please explain why?
AppDelegate.h
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate> {
NSString *someString;
}
#property (assign) IBOutlet NSWindow *window;
#property (retain, nonatomic) NSString *someString;
- (void)manualSetVariable;
#end
AppDelegate.m
#import "AppDelegate.h"
#import "GrabFromAppDelegate.h"
#implementation AppDelegate
#synthesize someString;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
someString = #"The Variable";
NSLog(#"In AppDelegate (1): %#",someString);
GrabFromAppDelegate *getThis = [[GrabFromAppDelegate alloc] init];
getThis.varSet = someString;
}
- (void)manualSetVariable { // THIS METHOD WORKS (2)
someString = #"The Variable";
NSLog(#"In AppDelegate(2): %#",someString);
}
#end
GrabFromAppDelegate.h
#import <Foundation/Foundation.h>
#interface GrabFromAppDelegate : NSObject {
NSString *varSet;
IBOutlet NSTextField *variable;
}
#property(retain, nonatomic) NSString *varSet;
- (IBAction)showVariable:(id)sender;
- (IBAction)manuallyGrabVariable:(id)sender;
#end
GrabFromAppDelegate.m
#import "GrabFromAppDelegate.h"
#import "AppDelegate.h"
#implementation GrabFromAppDelegate
#synthesize varSet;
- (IBAction)showVariable:(id)sender {
if (varSet != NULL) {
[variable setStringValue:[NSString stringWithString:varSet]];
NSLog(#"Got String Using (1): %#",varSet);
}
}
- (IBAction)manuallyGrabVariable:(id)sender { // THIS METHOD WORKS (2)
AppDelegate *getString = [[AppDelegate alloc] init];
[getString manualSetVariable];
if ([getString someString] != NULL) {
[variable setStringValue:[NSString stringWithString:[getString someString]]];
NSLog(#"Got String Using (2): %#",[getString someString]);
}
}
#end
This is horribly wrong:
AppDelegate *getString = [[AppDelegate alloc] init];
That allocates an AppDelegate instance, but it's not [[NSApplication sharedApplication] delegate], so the getString instance will never execute the applicationDidFinishLaunching method and your someString iVar will never get set. If you'd set someString within an init function, [[AppDelegate alloc] init] would, of course, call init. But since getString isn't attached to an NSApplication instance, there's nothing delegating the applicationDidFinishLaunching method to it.
If you want to get a pointer to the application delegate, you can do:
AppDelegate *getString = [[NSApplication sharedApplication] delegate];
or, for brevity's sake:
AppDelegate *getString = [NSApp delegate];
In second method you are calling method from app delegate and value is set at that time in someString variable and you get response. However if you are setting value for a varibale in appDelegate using an instance the set value will passed to that particular instance of controller class not to all instances. Hence either you create a shared instance of that variable or call that particular instance
to get the value somestring by showVariable method.

My own method isn't being found by the compiler

I recently started learning Objective-C and Cocos-2D. I tried to define my own method for automating the creation of sprites.
I added my own class where I'll create other automation methods as well. Anyhow my .h file looks like this:
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface ActionsClass : CCNode {
}
#property (nonatomic, strong) CCSprite* createSprite;
#property (nonatomic, strong) CCSprite* spriteName;
#property (nonatomic, strong) NSString* pngName;
#property (nonatomic) CGPoint* spriteCoordinate;
- (CCSprite *)createSprite: (CCSprite *)spriteName: (NSString *)pngName: (CGPoint *)spriteCoordinate;
#end
And the .m is:
#import "ActionsClass.h"
#implementation ActionsClass
#synthesize createSprite = _createSprite;
#synthesize spriteName = _spriteName;
#synthesize pngName = _pngName;
#synthesize spriteCoordinate = _spriteCoordinate;
- (CCSprite *)createSprite: (CCSprite *)spriteName: (NSString *)pngName: (CGPoint *)spriteCoordinate
{
if (!_createSprite)
{
_createSprite = [[CCSprite alloc] init];
_spriteName = [CCSprite spriteWithFile:_pngName];
_spriteName.position = ccp(_spriteCoordinate->x, _spriteCoordinate->y);
[self addChild:_spriteName];
}
return _createSprite;
}
#end
In the main .m file where I want to call the method:
[self createSprite: saif: #"saif.png": ccp(100,100)];
This would give the warning that xcode didn't find the instance method createSprite and defaults it to id
Thanks a lot and sorry if the font or the formatting of the question aren't super neat.
Your method declaration is wrong, so you wont be able to call it.
It should be:
- (CCSprite *)createSprite:(CCSprite *)spriteName pngName:(NSString *)pngName coord:(CGPoint *)spriteCoordinate;
And called like:
[self createSprite:someSprite pngName:somePNGName coord:someCoord];
Edit: I didn't see that you were trying to call this from another class. To do that you will need to import the ActionsClass header file, and call this method on an instance of ActionsClass, e.g.
ActionsClass *actionsClassObject = [[ActionsClass alloc] init];
[actionsClassObject createSprite:someSprite pngName:somePNGName coord:someCoord];

Use of undeclared identifier in Objective C

I'm very new to Objective C and am having trouble with some very basic things.
In AppDelegate.m, I'm getting the following errors:
Use of undeclared identifier 'health'
Use of undeclared identifier 'attack'
Code (respectively):
[Troll setValue:100 forKeyPath:health];
[Troll setValue:10 forKeyPath:attack];
I'm not really sure how to declare the identifiers.
AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSObject *Troll = [[NSNumber alloc]init];
[Troll setValue:100 forKeyPath:health];
[Troll setValue:10 forKeyPath:attack];
return YES;
}
#end
AppDelegate.h
#import `<UIKit/UIKit.h>`
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#end
#interface Troll : NSObject {
NSNumber *health;
NSNumber *attack;
}
#end
Keys are strings, and not something else (like dangling syntactic garbage). Furthermore, '100' and '10' are not objects. Even after this, you don't want to set the properties of the class itself but of its instances. Try
[troll setValue:[NSNumber numberWithInt:100] forKeyPath:#"health"];
[troll setValue:[NSNumber numberWithInt:10] forKeyPath:#"attack"];
instead.
The first thing to say is that you are not instantiating a Troll object, but a NSNumber... why? You would have to do Troll *troll = [[[Troll alloc] init] autorelease];
The way to set and get attributes from classes uses to be by declaring properties on the class. This way the compiler will manage the memory for you (retains and releases). Another way would be accessing directly your ivars.
However, if you want to use the setValue:forKeyPath: you have to use a NSString for the second paremeter, which is the name of the variable.
#interface Troll : NSObject
{
NSNumber *_health;
NSNumber *_attack;
}
#property (nonatomic, retain) NSNumber *health;
#property (nonatomic, retain) NSNumber *attack;
#end
#implementation Troll
#synthesize health = _health;
#synthesize attack = _attack;
- (void)dealloc
{
[_health release];
[_attack release];
[super release];
}
- (void)customMethod
{
//using properties
[self setHealth:[NSNumber numberWithInteger:100];
[self setAttack:[NSNumber numberWithInteger:5];
//accessing ivars directly - remember to release old values
[_health release];
_health = [[NSNumber numberWithInteger:100] retain];
[_attack release];
_attack = [[NSNumber numberWithInteger:5] retain];
}
#end
Good luck!
Although you are defining a class called Troll with a 'health' and 'attack', you aren't instantiating one. You probably want one of the following in your application didFinishLaunchingWithOptions:
Troll *troll = [[Troll alloc]init];
troll.health = [NSNumber numberWithInt:100];
troll.attack = [NSNumber numberWithInt:10];
OR
Troll *troll = [[Troll alloc]init];
[troll setHealth:[NSNumber numberWithInt:100]];
[troll setAttack:[NSNumber numberWithInt:10]];
These two are equivalent.

iPhone Application Delegate variable is not being retained

I am having a very odd issue retrieving/retaining a variable in my iPhone application delegate (AppDelegate). Initially, I can step through and see that my values are passed to logfile (the NSString variable in question), but when logfile is retrieved from another class (see the code below), it faults.
Here is my AppDelegate.h file:
#import < UIKit/UIKit.h >
#interface AppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *_window;
MainViewController *_mainViewController;
NSString *logFile;
}
#property (nonatomic, retain) NSString *logFile;
#property (nonatomic, retain) ProductClass *item;
#property (nonatomic, retain) UIWindow *window;
-(void)appendToLog:(NSString *)textToLog;
#end
Here is my AppDelegate.m:
#import "AppDelegate.h"
#import "MainViewController.h"
#implementation AppDelegate
#synthesize window = _window;
#synthesize logFile;
- (void) applicationDidFinishLaunching:(UIApplication *)application {
_window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
_mainViewController = [[MainViewController alloc] init];
UINavigationController *_navigationController = [[UINavigationController alloc] initWithRootViewController:_mainViewController];
//Initialize the product class
[self appendToLog:#"Application loaded"];
[_window addSubview:_navigationController.view];
[_window makeKeyAndVisible];
}
-(void)appendToLog:(NSString *)textToLog {
//Append the log string
if(logFile!=nil) {
NSString *tmp = [[logFile stringByAppendingString:textToLog] stringByAppendingString:#"\n"];
logFile = tmp;
}
else {
NSString *tmp = [textToLog stringByAppendingString:#"\n"];
logFile = tmp;
}
}
#end
When I use the call (from another class):
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSString *s = [appDelegate logFile];
"logfile" comes back as "out of scope" so the local variable "s" is mush.
What am I doing wrong here? It isn't making sense to me.
You should replace logFile = tmp; with self.logFile = tmp; because you need to use the "self." prefix when assigning an ivar in order for the code to call the proper settor method. As it is, you're just assigning the ivar to an autoreleased object instance, instead of retaining it. The "self." prefix ensure that the code does the right thing. Without it, you're just assigning the variable without retaining it.
I would recommend prefixing logfile with self in your assignment statements in your AppDelegate. For example, self.logfile = ...
From the UIApplication class reference - UIApplication assigns and does not retain the delegate.
You need to initialize your instance of AppDelegate first.