Why does my class variable in my Application Delegate not get instantiated? - iphone

This issue has been bugging me for a while now and somehow I cannot find what I'm doing wrong. I must say I am new to Objective-C and Xcode.
So the issue is that I try to declare an instance variable (NSMutableArray) but for some reason the init function is NEVER reached. The variable is always NULL.
So I have a class named PropertyProvider which contains a NSMutableArray named "properties".
#interface PropertyProvider : NSObject {
NSMutableArray *properties;
}
#property (nonatomic, retain) NSMutableArray *properties;
..
#end
I then instantiate this NSMutableArray in the init method of this PropertyProvider class as the following:
#implementation PropertyProvider
#synthesize properties;
- (id) init {
self = [super init];
NSLog(#"Instantiating PropertyProvider");
if (self) {
properties = [[NSMutableArray alloc] init];
}
return self;
}
.. more code ..
#end
In my Application delegate I try to instantiate the PropertyProvider as "prp":
#implementation MyAppDelegate
#synthesize prp = _prp;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
[[_prp init] alloc];
[self.window makeKeyAndVisible];
return YES;
}
.. more code ..
- (void)dealloc
{
[_prp release];
[super dealloc];
}
But thus, for some reason it never reaches the init method of my PropertyProvider. Why o why?!
Thanks for your help!

The correct way of initializing _prp as an instance of PropertyProvider would be,
_prp = [[PropertyProvider alloc] init];
Since _prp is an instance variable, it is nil by default and hence messages to it don't do anything which is the reason why you're not getting any errors.

Related

ios how can access and modify variable from diffent class?

I have the following:
Class 1:
.h file:
#interface class1 : UIViewController
#property (assign,nonatomic) int number;
.m file:
- (void)viewDidLoad
{
[super viewDidLoad];
number=1;
}
Class2:
.h file:
#interface class2 : UIView{
class1 *Controller;
}
#property (retain,nonatomic) class1 *controller;
but if access the number variable from anywhere in the second class the value is 0 any of you knows why?
Is the class already open and retained somewhere (i.e. your navigation stack)? If so you can then safely access and modify variables because they are already initialized. If not that's probably why you're getting 0 returned.
Another way is to create and maintain a data singleton that all view controllers can draw data from easily and you can then reference that data singleton to modify and change values.
Assuming the view controller is already initialized and active you can then do something like this:
MyViewController *vc = [[MyViewController alloc] init];
[vc setValue:#"100"];
You can use appDelegate method to access the variable from some other class,
example,
in your class2:
.m file
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
Controller = appDelegate.viewControllerClass1;
}
return self;
}
in AppDelegate Class, .m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
self.viewControllerClass1 = [[[Class1 alloc] init] autorelease];
NavigationController=[[UINavigationController alloc]initWithRootViewController:self.viewControllerClass1];
[self.window makeKeyAndVisible];
return YES;
}

toSharedViewController not re-using existing controller

My URL map is as follows:
[map from:#"tt://webPage/(initWithPage:)" toSharedViewController:[WebPageController class]];
and in the WebPageController
- (id) initWithPage:(WebPage)page
{
if (self = [super init])
{
...
Then I called the url several times in my code
tt://webPage/1
tt://webPage/2
tt://webPage/1 (still called the initWithPage: everytime, not cached)
Why it is not cached as it is a SharedViewController?
I believe this is happening to you because TTNaviagtor is broken on iOS 5. see https://github.com/facebook/three20/pull/719/files. Have you tried running the same code on a iOS 4 with the same result?
My recommendation to you is to stop using TTNaviagtor. You can still use the three20 library by pushing and poping TTViewController in the native ios method.
Here's an example on replacing the TTNaviagtor in your app delegate:
#interface AppDelegate : NSObject <UIApplicationDelegate> {
UIWindow* _window;
TTBaseNavigationController* _masterNavController;
WebPageController* _web1Controller;
WebPageController* _web2Controller;
}
#property(nonatomic, retain) UIWindow* window;
#property(nonatomic, retain) TTBaseNavigationController* masterNavController;
#property(nonatomic, retain) WebPageController* web1Controller;
#property(nonatomic, retain) WebPageController* web2Controller;
And
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
#implementation AppDelegate
#synthesize window = _window;
#synthesize masterNavController = _masterNavController;
#synthesize web1Controller = _web1Controller;
#synthesize web2Controller = web2Controller;
///////////////////////////////////////////////////////////////////////////////////////////////////
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
_window = [[UIWindow alloc] initWithFrame:TTScreenBounds()];
TTViewController* controller = [[[MasterViewController alloc] init] autorelease];
_masterNavController = [[TTBaseNavigationController alloc] initWithRootViewController:controller];
[_window addSubview:_masterNavController.view];
}
[_window makeKeyAndVisible];
return YES;
}
then you can push and pop any TTViewController (or your own subclasses of TTViewController) into the _masterNavController. Personally, i think TTNavigator is a bad design pattern, and apple designed their navigation system in different mindset.
why not step into the code and check what happen?
I believe the objects are created in TTURLMap's objectForURL:query:pattern: you can set a break point and see why a new one is created instead re-use old one.
this the implementation of objectForURL:query:pattern: with my comment
- (id)objectForURL: (NSString*)URL
query: (NSDictionary*)query
pattern: (TTURLNavigatorPattern**)outPattern {
id object = nil;
if (_objectMappings) {
// _objectMappings is a NSMutableDictionary and use to cache shared object
object = [_objectMappings objectForKey:URL];
// if object not found than check does _objectMappings contains it with right key
if (object && !outPattern) {
return object;
}
}
NSURL* theURL = [NSURL URLWithString:URL];
TTURLNavigatorPattern* pattern = [self matchObjectPattern:theURL];
if (pattern) {
if (!object) {
// object is not found in the mapping dictionary so create new one, this should only happen once for shared object
object = [pattern createObjectFromURL:theURL query:query];
}
// make sure navigationMode is TTNavigationModeShare
if (pattern.navigationMode == TTNavigationModeShare && object) {
// cache shared object in the mapping dictionary so next time it will re-use the cached one
[self setObject:object forURL:URL];
// if setObject:forURL: is not working than the shared object will not be cached
}
if (outPattern) {
*outPattern = pattern;
}
return object;
} else {
return nil;
}
}

NSDictionary setting to nil when passed to another class (IOS)

I am passing an NSDictionary object from one view class to another as I transition from a table view to a normal view to show details:
Passing Controller:
[tweetController setTweet:tweet];
Receiving Controller.h:
#interface TweetViewController : UIViewController {
NSDictionary *tweet;
}
#property (nonatomic, retain) NSDictionary *tweet;
Receiving Controller.m:
#implementation TweetViewController
#synthesize tweet = _tweet;
I then try to use this information to set the properties of some fields in my view:
- (void)viewDidLoad
{
[super viewDidLoad];
tweetLabel.text = [_tweet objectForKey:#"text"];
}
The result is a blank label and if I inspect the value of _tweet at this stage it is nil.
I originally had a method which set the value of tweet which I called at the same location as I am now setting the value. If I inspected the value at this stage it was fine.
I presume that the automagic setter through #synthasize is working, but somewhere else the value is being lost.
Sorry this is my first objective C anything! Thanks for any help in advance.
You are using your "tweet" instance variable, whereas the "tweet" property is synthesized to the "_tweet" variable.
You are probably calling the setTweet method after viewDidLoad executes.
I usually pass this kind of thing into a custom init method.
Alternatively, you could do the set before pushing the detail VC onto the nav stack.
Are you sure that tweetLabel isn't nil?
I've made a few corrections & optimisations to your code. You don't need to declare ivars in the header file anymore, they are generated automatically by #synthesize
- (void)dealloc; is only needed if you're not using ARC.
//.h
#interface TweetViewController : UIViewController
#property (strong, nonatomic) NSDictionary *tweet;
#property (strong, nonatomic) IBOutlet UILabel *tweetLabel
#end
//.m
#implementation TweetViewController
#synthesize tweet = _tweet;
#synthesize tweetLabel = _tweetLabel;
- (void)viewDidLoad {
[super viewDidLoad];
self.tweetLabel.text = [self.tweet objectForKey:#"text"];
}
- (void)dealloc {
[_tweet release];
[_tweetLabel release];
[super dealloc];
}
#end
Note: strong is equivalent to retain
To expand on #Rayfleck's answer, since you are new to Objective-C, your custom init method could look like this:
In TweetViewController.h:
- (id)initWithTweet:(NSDictionary*)tweet;
In TweetViewController.m:
- (id)initWithTweet:(NSDictionary*)tweet
{
self = [super init];
if (self) {
_tweet = tweet;
}
return self;
}
and then in your passing controller you'd allocate and initialize like this:
TweetViewController *tvc = [[TweetViewController alloc] initWithTweet:myTweet];

Correct way to create/use a Singleton NSMutableArray for Xcode 4

I've reviewed (and tried) a bunch of the threads here regarding Singletons and NSMutableArrays. I'm new to Objective-C so please bear with me.
I simply want to create a few arrays that can be accessed from any view/.m file.
What is the best (or most concise) coding for a Singleton?
Below is what I have now and I get
1 warning at .m '#implementation' - "Incomplete implementation"
1 error at usage in a view .m file - "initializer element is not a compile-time constant"
This is the code I have now - my GlobalData.h file:
#import <Foundation/Foundation.h>
#interface GlobalData : NSObject {
NSMutableArray *listOfHeadings;
NSMutableArray *listOfItems1;
NSMutableArray *listOfItems2;
}
#property(nonatomic,retain)NSMutableArray *listOfHeadings;
#property(nonatomic,retain)NSMutableArray *listOfItems1;
#property(nonatomic,retain)NSMutableArray *listOfItems2;
+(GlobalData*)getInstance;
#end
My GlobalData.m file:
#import "GlobalData.h"
#implementation GlobalData
#synthesize listOfHeadings;
#synthesize listOfItems1;
#synthesize listOfItems2;
static GlobalData *instance=nil;
+(GlobalData *)getInstance
{
#synchronized(self)
{
if(instance==nil)
{
instance= [GlobalData new];
}
}
return instance;
}
#end
And in a view .m file (simplified):
#import GlobalData.h
GlobalData *globDat=[GlobalData getInstance]; //error occurs here
Can someone point out the trouble and if there's better coding, please enlighten me - thanks!
EDIT
Here's a few links I've tried to use:
Can i have a single NSMutableArray in my multiple views application?
iPhone help with singleton class
In this case, you might be doing more than you have to. Granted this certainly isn't always the best solution - but you can put your NSMutableArray as a property in your App Delegate and then easily refer to it from any view. By doing it this way - you aren't locking it in as a 'singleton' but there is a 'singleton instance' of it (this helps a great deal for testability).
I have simplified this process here:
YourAppDelegate.h
#property (nonatomic,retain) NSMutableArray *myArray;
YourAppDelegate.m
#synthesize myArray;
YourViewController.m
YourAppDelegate *appDelegate = (YourAppDelegate *)[[UIApplication sharedApplication] delegate];
NSMutableArray *myArrayFromAppDelegate = appDelegate.myArray;
From this point - you can do any manipulation on this value.
Here's the "modern" version of a single method to turn any class into a Singleton (in this case formatted as a code snippet). It works in iOS4.x or higher:
+(<#SingletonClassName#> *) sharedInstance
{
static <#SingletonClassName#> *_sharedClient = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedClient = [[self alloc] init];
});
return _sharedClient;
}
But, do you really need a singleton of a single NSMutableArray? You could use the built-on singleton - your application delegate, which is got to by calling:
MyAppDelegate * appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate.myMutableArray addObject:...];
The error initializer element is not a compile-time constant is not related to how you create your singleton. The error is how you are accessing your singleton. You are doing this outside of a function:
GlobalData *globDat=[GlobalData getInstance];
This means that you are trying to initialize a global variable (globDat) as the value of the expression [GlobalData getInstance]. You can only initialize global variables to expressions that are "compile-time constants". That means things like 0 or "fred" or 8/2. The value of [GlobalData getInstance] cannot be computed at compile-time, so it cannot be used to initialize the global variable.
Instead, you need to just use [GlobalData getInstance] inside your function bodies wherever you are currently trying to use the globDat variable.
As for the warning, Incomplete implementation, I don't see what's missing. Perhaps you didn't post all of the code from GlobalData.h. Anyway, you should be able to click the warning (where it appears on the right side of the editor window) and have Xcode show you what's missing.
This is the way I create my Singleton:
Singleton.h
#import <Foundation/Foundation.h>
#interface Singleton : NSObject {
NSMutableArray *firstMutableArray;
NSMutableArray *secondMutableArray;
}
#property (nonatomic, retain) NSMutableArray *firstMutableArray;
#property (nonatomic, retain) NSMutableArray *secondMutableArray;
+ (id)sharedSingleton;
#end
Sigleton.m
#import "Singleton.h"
static Singleton *sharedMySingleton = nil;
#implementation Singleton
#synthesize firstMutableArray;
#synthesize secondMutableArray;
#pragma mark Singleton Methods
+ (id)sharedSingleton {
#synchronized(self) {
if (sharedMySingleton == nil) {
sharedMySingleton = [[super allocWithZone:NULL] init];
}
return sharedMySingleton;
}
+ (id)allocWithZone:(NSZone *)zone {
return [[self sharedSingleton] retain];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)retain {
return self;
}
- (unsigned)retainCount {
return UINT_MAX;
}
- (oneway void)release {
// Never release
}
- (id)autorelease {
return self;
}
- (id)init {
if (self = [super init]) {
firstMutableArray = [[NSMutableArray alloc] initWithObjects:nil];
secondMutableArray = [[NSMutableArray alloc] initWithObjects:nil];
}
return self;
}
- (void)dealloc {
[firstMutableArray release];
[secondMutableArray release];
[super dealloc];
}
#end
Then, when you want to call your Singleton:
#import "Singleton.h"
Singleton *singleton = [Singleton sharedSingleton];
singleton.firstMutableArray = ...
singleton.secondMutableArray = ...

Load data in AppDelegate, but can not access later in UIView? What's wrong?

I create a new "View-based Application" project and modify the didFinishLaunchingWithOptions: method as follow.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// Add the view controller's view to the window and display.
[self.window addSubview:viewController.view];
[self.window makeKeyAndVisible];
downloader = [[InternetIOProcess alloc] init];
[downloader initWithServer:[NSURL URLWithString:#"http://www.test.com"] ];
return YES;
}
InternetIOProcess is a NSObject with two variables, and a method:
#interface InternetIOProcess : NSObject {
NSMutableArray* downloadingFile;
NSURL* serverAddress;}
#property (nonatomic,retain) NSMutableArray* downloadingFile;
#property (nonatomic,retain) NSURL* serverAddress;
-(void) initWithServer:(NSURL*) server;
the implementation of InternetIOProcess is:
#implementation InternetIOProcess
#synthesize downloadingFile,serverAddress; //,serviceuploadingQueue,;
-(void) initWithServer:(NSURL*) server
{
downloadingFile = [NSMutableArray array];
serverAddress = server;
}
And then, I write a IBAction in UIViewController response to a button "touch up inside" event:
-(IBAction) test:(id)sender
{
MyAppDelegate* d = (MyAppDelegate*)[UIApplication sharedApplication].delegate;
InternetIOProcess* thedownloader = d.downloader;
//value of "thedownloader" incorrect.
}
Try to access "thedownloader" here, its member "downloadingFile, serverAddress" both give random bad values!
Anybody know why can't I access this object?
The problem lies here with you not retaining the array and the server and bad naming convention of init. It looks like your custom init method is not getting called.
-(void) initWithServer:(NSURL*) server
{
downloadingFile = [NSMutableArray array];
serverAddress = server;
}
Try making the following changes
//InternetIOProcess.h add
-(id) initWithServer:(NSURL*) server;
//InternetIOProcess.m change
-(id) initWithServer:(NSURL*) server
{
self = [super init];
if(self != nil)
{
downloadingFile = [[NSMutableArray array] retain];
serverAddress = [server retain];
}
return self;
}
//MyAppDelegate.m
downloader = [[InternetIOProcess alloc] initWithServer:[NSURL URLWithString:#"http://www.test.com"]];
Just checking, your UIViewController contains
#import "MyAppDelegate.h"
and
#import "InternetIOProcess.h"
And your MyAppDelegate contains
#import "InternetIOProcess.h"
Also, are you getting any compiler warnings?
Is downloader a property with the retain attribute on the delegate class? I don't see you specifying retain when you allocate the instance.
In your initWithServer: method, use self.downloadingFile and self.serverAddress so that the objects are retained.