Objective C - static variables in categories - iphone

I do have a little Problem.
Since my UIViewControlles are all named in the same scheme:
MyView1Controller.h
MyView1Controller.m
MyView1.xib
MyView2Controller.h
MyView2Controller.m
MyView2.xib
MyView3Controller.h
MyView3Controller.m
MyView3.xib
I would now prefer to init my UIViewControllers via a factory method.
Therefore I would implement a Cateogry on UIViewController:
static NSString *standardNibFileName;
#interface UIViewController (FactoryInstantiation)
+ (id) standardViewController;
#end
And in MyView1Controller controller I would declare the static nib file name variable:
static NSString *standardNibFileName = #"MyView1";
#implementation MyView1Controller
Then I could instantiate all my UIViewCOntrollers using the method:
#implementation UIViewController (FactoryInstantiation)
+ (id) standardViewController;
{
if(standardNibFileName != nil) {
NSString *className = NSStringFromClass([self class]);
Class classToIntantiate = NSClassFromString(className);
return [[classToIntantiate alloc] initWithNibName:className bundle:nil];
}
return nil;
}
#end
Init:
MyView1Controller *a = [MyView1Controller standardViewController];
But the static variable is always nil.
Any suggestions on how to solve this issue?
I would appreciate any help!
Thanks in advance.

You can declare a + method instead on UIViewController class and override on the implementing classes
+ (NSString*) getStandardNibFileName {
return #"nibName"
}
Edit: If the implementing class has the same nibName as the base you don't have to override the function.

You have static NSString *standardNibFileName; in .h file as well, give a try removing it, I hope
static NSString *standardNibFileName = #"MyView1"; .m is more than enough

Related

What is the use of init in this singleton class..?

#Interface
//
// Created by macbook on 31.05.12.
//
// To change the template use AppCode | Preferences | File Templates.
//
#import <Foundation/Foundation.h>
#interface CESettings : NSObject
+ (CESettings *)sharedInstance;
- (void)save;
#end
#Implementation
//
// Created by macbook on 31.05.12.
//
// To change the template use AppCode | Preferences | File Templates.
//
#import "CESettings.h"
#interface CESettings ()
#property(nonatomic, strong) NSUserDefaults *userDefaults;
#end
#implementation CESettings
#synthesize userDefaults = _userDefaults;
#pragma mark - Singleton
static CESettings *_instance = nil;
+ (CESettings *)sharedInstance {
#synchronized (self) {
if (_instance == nil) {
_instance = [self new];
}
}
return _instance;
}
- (id)init {
self = [super init];
if (self) {
self.userDefaults = [NSUserDefaults standardUserDefaults];
}
return self;
}
#pragma mark - Methods
- (void)save {
[self.userDefaults synchronize];
}
#end
I have a class used for settings in an app. The class has a method for creating singleton and an init method as well. What is the use for both..? I think if the sharedInstance method is there , there is no need for the init... please correct me if I am wrong..
Any help is appreciated.
The init method is what gets called by new in the call of [self new]. It is essentially the same as
_instance = [[CESettings alloc] init];
but takes less typing and avoids hard-coding the name of the CESettings class.
A better way of implementing singleton is using dispatch_once, like this:
+ (CESettings*)sharedInstance
{
static dispatch_once_t once;
static CESettings *_instance;
dispatch_once(&once, ^ { _instance = [self new]; });
return _instance;
}
From the documentation of NSObject:
+ (id)new
Allocates a new instance of the receiving class, sends it an init
message, and returns the initialized object.
You're calling [self new] in your singleton creator method, which in turn will allocate a new instance and send it an init message.
the sharedInstance class method is only responsible for allocating and initing ONE object and then always returning that.
BUT
you dont have go through that method you can call alloc init yourself and it will also work
so init is needed to keep the semantics of how alloc/init should work

how to make static variable initialized

I want to save the "dataFilePath" as a static variable so that it can be initialized when first time use "Constants" and no need to instantiate the Class , for example [Constants SDataFilePath]. But the trues is that the init method is not called. How can I do to meet my request? (In Java the construct method will be called the fist time to access the Class).
#implementation Constants
static NSString *dataFilePath;
-(id)init
{
NSLog(#"init!");
if(self = [super init]) {
dataFilePath = [self getDataFilePathWithArg:dbfile];
}
return self;
}
+(NSString *)SDataFilePath {
return dataFilePath;
}
....
#end
Well you could make Constants a singleton. Leave your code how it is and add this method to the .m:
+ (Constants *)sharedConstants
{
static Constants *_sharedConstants = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedConstants = [[self alloc] init];
});
return _sharedConstants;
}
And the method declaration to the .h:
+ (Constants *)sharedConstants;
Then access your variable like this:
[[Constants sharedConstants] SDataFilePath]
That will force an init the first time you access [Constants sharedConstants] (and only the first time). Also, you'll need to change +(NSString *)SDataFilePath to an instance method, not class method:
-(NSString *)SDataFilePath
This cannot be done this way. Any reason why you want this path to be static? You might want to look into setting dataFilePath with a getter, but no setter and instantiating the class as a singleton. That way you can set the path by some internal method and share the instance as a singleton. See here

Objective C access variables from other classes?

I'm trying to get a variable from a class that I made from a table view. Basically what I want this to do is tell the other controller what row was selected so this is what I tried to do.
Table View Class .h file:
#property(nonatomic) NSInteger itemId;
-(NSInteger)itemId;
I would then make methods that set and get the variable in the .m file of the Table View Class
(I synthesized it and did all that stuff, I'm just showing you the methods)
-(NSInteger)itemId {
return self.itemId;
}
And now the table cell selected method...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)
indexPath {
NSUInteger row = [indexPath row];
self.itemId = row;
//segue stuff (if you want me to include this just let me know)
}
Thats all for that class and now the class that I need the value for
View Controller Class .h that is being pushed via segue
#property (nonatomic) NSInteger itemId;
View Controller Class .m
#import "TableViewController.h"
//Skip a few things
#synthesize itemId;
//skip a few things
-(void)viewDidLoad {
TableViewController *tvc = [[TableViewController alloc] init];
itemId = [tvc itemId];
NSLog(#"%i", itemId);
For some reason this doesn't work... When I print out the "itemId" in the "didselectrow" method it returns the right number but when I try to print it out in the other class it just gives me '0'
Any thoughts?
If there are things that I left out that you want to see in my code I'd be more than happy to write it out :) I just wanted to save time and space by cutting down the code a little.
Thanks in advance!
EDIT:
I did find a possible solution but it involves using the delegate. I'm sure there's got to be a better way of doing this so if you have any ideas, just let me know.
TableViewController *tvc = [[TableViewController alloc] init];
Doing this in a different class you create&initialize a new object, so it will be nill(for int 0). For sharing data between classes&view controllers use Delegates and properties.
With a little code i shall explain;
#import <UIKit/UIKit.h>
//YourClassNameAppDelegate.m
#interface YourClassName : UIResponder <UIApplicationDelegate>
{
NSString *uName;
NSDictionary *YourDictionary;
}
#property (copy, readwrite) NSString *uName;
#property (copy, readwrite) NSString *YourDictionary;
And in the other class you want to use this string and dictionary,
//import your delegate class
#import "YourClassNameAppDelegate"
.
.
.
//to me, do this in viewDidLoad(or something like that) method of new view controller
YourClassNameAppDelegate *sharedData= (YourClassNameAppDelegate *)([[UIApplication sharedApplication]delegate]);
.
.
-(IBAction)sharedData{
NSLog(#"Shared String: %#\n And Shared Dictionary: %#, sharedData.uName, sharedData.YourDictionary);
}
The - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *) is not invoked - you have to select a row to change the value of itemId, and that is not possible between the initialization of the table and the itemId = [tvc itemId]; line.
this code may helps you.
#import <UIKit/UIKit.h>
extern int outsider;
#interface ViewController : UIViewController {
}
#property int outsider;
#end
to all the .m files you #import the .h file where you declared the variable in can acces it.
You are creating a new instance of your table view controller within your detail view controller, this is incorrect and is not going to give you a reference to your original table.
Simply create a property on your pushed view controller which holds whatever detail information you want to pass. Then set this in prepareForSegue: in your table view controller, where segue.destinationViewController will give you a pointer to the VC that is about to appear.
Also, fix your accessor method as suggested by Yuji in comments.
I suggest saving the indexPath for the pressed row in a singleton. Here is a guide on how to use the singleton class to store objects and use them across classes.
In this example you can also just declare an instance variable in the singleton and set it when the row is pressed in the appropriate delegate method for your UITableView. This could be done like so:
#import <Foundation/Foundation.h>
#interface Singleton : NSObject {
NSIndexPath *tableViewPath;
}
#import "DataContainerSingleton.h"
#implementation DataContainerSingleton
#synthesize tableViewPath;
static DataContainerSingleton* _theDataContainerSingleton = nil;
+ (DataContainerSingleton*) theDataContainerSingleton;
{
if (!_theDataContainerSingleton)
_theDataContainerSingleton = [[DataContainerSingleton alloc] init];
return _theDataContainerSingleton;
}
- (id)init
{
self = [super init];
if (self) {
tableViewPath = [NSIndexPath indexPathForRow:0 inSection:0];
}
return self;
}
In your view controller with the UITableView just do like this:
#implementation
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
tableViewPath = indexPath;
}
You should adjust your initialization to what would be appropriate for your specific program!
Try this:
#property NSInteger itemId;
Hope this helps you

Using interface builder to Create Loabable Views

New Method of Getting Items out of Nib Files.
Example in answer below.
This new Answer is the latest change in this project and Question
I want to create a UIView subclass and I want to create an Interface builder file for the UIView. How would I connect the files owner to itself. I want to be able to Design the view and have it create the connections but I dont have a need for a view controller.
Other than loading the nib into an array can I use the nib file as a connector to the view itself ?
I hope i am Asking this properly. I will elaborate tho.
Creating a UIViewController I am given the choice to create an xib file for it. At which time i can add the UIViews to the xib and those connections would be made. However the files owner does not appear to have a UI Editor associated to it so I want to create a UIView that will become a sub view in another view. I dont really want to create it with code because Its a pain to layout the items manually. So I want to use the same construct to create a small uiview to put into another
EDIT:
I Edited this again when I found another even better way to load nib classes. I figured some stack overflow users would want to see this.
I wanted to find an elegant way to load a UIView from a nib. and with some info from stack overflow user PeyloW I have managed to do just that.
I am going to post my results as the information will lead to a solution but this solution feels exceptionally elegant to me.
First of all, I created a Xib File named "TableViewCells.xib"
(Name Matches the Class Name therefore [[self class] description] == "TableViewCells")
Next I created the Classes that I am using for the views in the Nib.
Classes dont matter, Just Make sure the "Custom Class" is selected for the Class Objects in interface builder
Finally I created the Class "TableViewCells" to match the xib filename
My new Header File.
(Each of the header files imported associate to the items in the single xib file.)
#import <Foundation/Foundation.h>
#import "TitleCell.h"
#import "ToggleCell.h"
#import "ButtonCell.h"
#import "TextCell.h"
#interface TableViewCells : NSObject {
UINib *uiNibHolder;
}
#property (nonatomic, readonly) NSArray *nibArray;
// These are the classes that will be loadable.
- (TitleCell*) titleCell;
- (ToggleCell*) toggleCell;
- (ButtonCell*) buttonCell;
- (TextCell*) textCell;
- (id) loadViewByClass: (id) viewClass;
#end
and the Class file.
loadViewByClass: is the method that finds the item in the nib file.
the convenience accessors have the correct type for the class objects to be instantiated into.
NOTE: I would Not Recommend loading a Bunch of Views into this, the more views you load the more you have to create when you load the nib file.
#import "TableViewCells.h"
#implementation TableViewCells
#synthesize nibArray;
- (void) unloadProperties{
[uiNibHolder release];
}
- (NSArray *)nibArray{
return [uiNibHolder instantiateWithOwner:nil options:nil];
}
- (id) init{
if ((self = [super init]))
{
// Im using the class name for the Xib file.
// This means it will look in TableViewCells.xib
// You can change the class name, or pass a Nib name to this class
uiNibHolder = [[UINib nibWithNibName:[[self class] description] bundle: [NSBundle mainBundle]] retain];
}
return self;
}
- (TitleCell *)titleCell{
return [self loadViewByClass:[TitleCell class]];
}
- (ToggleCell *)toggleCell{
return [self loadViewByClass:[ToggleCell class]];
}
- (ButtonCell *)buttonCell{
return [self loadViewByClass:[ButtonCell class]];
}
- (TextCell *)textCell{
return [self loadViewByClass:[TextCell class]];
}
- (id)loadViewByClass:(Class)viewClass{
for (id item in self.nibArray)
{
if ([item isKindOfClass:viewClass])
{
return item;
}
}
return nil;
}
- (void)dealloc{
[self performSelector:#selector(unloadProperties)];
[super dealloc];
}
#end
Being able to load by class is incredibly nice. And if the class is not in the xib file it will return nil.
This could be adapted to use multiple nib files depending on the type of views you were loading. Using a lazy load property technique you could load those nibs only when needed leaving the memory footprint small and still allowing you to get the classes loaded through one convenient class loader.
As a demonstration I used a TableView to load a handfull of views
#synthesize tableViewCellLoader = _tableViewCellLoader;
- (TableViewCells *)tableViewCellLoader{
if (_tableViewCellLoader == nil){
_tableViewCellLoader = [[TableViewCells alloc] init];
}
return _tableViewCellLoader;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
switch (indexPath.row) {
case 0:
return self.tableViewCellLoader.toggleCell;
break;
case 1:
return self.tableViewCellLoader.textCell;
break;
case 2:
return self.tableViewCellLoader.titleCell;
break;
case 3:
return self.tableViewCellLoader.buttonCell;
break;
}
return nil;
}
As you can see I lazy loaded my TableViewCellLoader and if I wanted to I could make the TableViewCells class lazy load UINib objects only when their classes were called.
I love convenience in code.
And Still,
Thanks again PeyloW.
Files Owner is only a proxy object, it is not required to be specified at run-time, but will always be visible at design-time in Interface Builder.
You may ignore hooking anything up to Files Owner if you do not need it. The at run-time load the NIB-file using something like this:
NSArray* rootObject = [[NSBundle mainBundle] loadNibNamed:#"MyNib"
owner:nil
options:nil];
What you pass for the owner argument is what passes as the Files Owner proxy when loading the nib, nil works just fine.
The rootObject array contains all root level objects, but no proxies. Do note that the array is autoreleased, so if you need the loaded objects to stay around you must retain the array, or just the particular elements you are interested in.
Not that using loadNibNamed:owner:options: is IO bound. If performance is needed then you should use an instance of UINib to cache the NIB file in memory and instantiate objects from it.
UINib* myNib = [[UINib nibWithNibName:#"MyNib"
bundle:[NSBundle mainBundle]] retain];
// And then instantiate using:
NSArray* rootObject = [myNib instantiateWithOwner:nil
options:nil];
You can do this, but there is no reason you can't create IBOutlets in your UIView and connect directly to them. So in the xib file you would have a root UIView, make sure you change the class to your custom class. Then you can can set the outlets directly onto the view, you are not required to use the file owner.
Newest Answer by me
After much time and playing with this possibility I have tried a number of scenarios and came to the conclusion that the best possible scenario is to create the UINib for each request and get the object in question. A one time pass gives the best results. If you have an object you plan to make very often it is best to put it into its own nib, rather than in a nib with multiple different classes as each one will be created each time the UINib is initialized.
Here is the class that I use to accomplish this task.
LSObjectLoader.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface LSObjectLoader : NSObject {
UINib *nibHolder;
NSString *currentNibName;
NSArray *nibArray;
}
- (id) loadViewFromNib: (NSString*) nibName ofClassType:(Class) classType;
#end
LSObjectLoader.m
//
// LSObjectLoader.m
// APMobile
//
// Created by jason.mckinley on 2/8/12.
// Copyright (c) 2012 LoKi-Systems. All rights reserved.
//
#import "LSObjectLoader.h"
#implementation LSObjectLoader
- (id) loadViewFromNib: (NSString*) nibName ofClassType:(Class) classType
{
if (![currentNibName isEqualToString:nibName])
{
currentNibName = [nibName copy];
nibHolder = [UINib nibWithNibName:currentNibName bundle:[NSBundle mainBundle]];
}
nibArray = [nibHolder instantiateWithOwner:nil options:nil];
for (id item in nibArray)
{
if ([item isKindOfClass:classType])
{
return item;
}
}
return nil;
}
#end
Finally a couple ways to implement this.
Method 1 Loading Directly.
LSObjectLoader *oLoader = [[LSObjectLoader alloc] init];
MyClass *thisInstance = [oLoader loadViewFromNib:#"TableViewCells"
ofClassType:[MyClass class]];
Method 2 Class loading itself
Nib File "TableViewCells.xib" (Contains a "MyChatCell" class object)
Class File "MyChatCell.h/MyChatCell.m"
Header
#import <UIKit/UIKit.h>
#interface MyChatCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UILabel *name;
#property (weak, nonatomic) IBOutlet UILabel *message;
#property (weak, nonatomic) IBOutlet UIImageView *image;
+ (MyChatCell*) newChatCell;
#end
Implementation
#import "MyChatCell.h"
#import "LSObjectLoader.h"
#implementation MyChatCell
+ (LSObjectLoader*) nibLoader{
static LSObjectLoader *_objectLoader;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_objectLoader = [[LSObjectLoader alloc] init];
});
return _objectLoader;
}
+ (MyChatCell *)newChatCell{
return [[self nibLoader] loadViewFromNib:#"TableViewCells" ofClassType:[self class]];
}
- (id)initWithCoder:(NSCoder *)aDecoder{
self = [super initWithCoder:aDecoder];
if (self){
// This is the init code for nib loading
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
Method 3 Custom Loader Class
Nib File "MyChatCells.xib" (Contains "MeChat" and "ThemChat" class objects)
Class File "MyChatCells.h/MyChatCells.m"
Header File
#import <UIKit/UIKit.h>
#import "MeChat.h"
#import "ThemChat.h"
#interface MyChatCells : NSObject
+ (MeChat*) newMeChat;
+ (ThemChat*) newThemChat;
#end
Implementation File
#import "MyChatCells.h"
#implementation MyChatCells
+ (LSObjectLoader*) nibLoader{
static LSObjectLoader *_objectLoader;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_objectLoader = [[LSObjectLoader alloc] init];
});
return _objectLoader;
}
+ (MeChat*)newMeChat{
return [[self nibLoader] loadViewFromNib:NSStringFromClass([self class])
ofClassType:[MeChat class]];
}
+ (ThemChat*)newThemChat{
return [[self nibLoader] loadViewFromNib:NSStringFromClass([self class])
ofClassType:[ThemChat class]];
}
#end

Release static int variable

How to "clean" static int variables in a view class method? Every time a get back to this view I need those vars "zeroed". The [self.view removeFromSuperview];
instruction does not seem enough to free up memory from those vars.
Thank you. Have a great 2010!
These int vars are declared static in a view method. They are not global in the view class.
If you don't want a static value to stick around, don't make it static.
You'll have to manually do this by defining a setValue method similar to:
#interface MyClass
{
// ...
}
+ (NSString *)myVar;
+ (void)setMyVar:(NSString *)newVa;
#end
#implementation MyClass
static NSString *myVar;
+ (NSString *)myVar { return myVar; }
+ (void)setMyVar:(NSString *)newVar { myVar = newVar; }
#end