This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Class methods which create new instances
I am wondering how to emulate the instantiation of classes like NSString, NSArray and such like this: [NSArray arrayWithObject:object]... in hope of eliminating inits and allocs.
I may not be familiar with what that actually does. According the the Documentation, [NSSArray array] creates and returns an empty array. What does that really mean, any allocations?
I want to be able to have a custom NSObject class and do: [CustomObj customObjWithData:data]
Thanks!
First write a corresponding custom init... method:
- (id)initWithFoo:(Foo *)aFoo
{
// Do init stuff.
}
Then add a custom factory method that calls alloc and your custom init... method:
+ (id)customObjWithFoo:(Foo *)aFoo
{
return [[[self alloc] initWithFoo:aFoo] autorelease];
}
If compiling with ARC, omit the autorelease call.
Those are class-methods. Usually, they also have instance init* methods as well. For example...
- (id)initWithData:(NSData*)data
{
// Call appropriate super class initializer
if (self = [super init]) {
// Initialize instance with the data
}
return self;
}
+ (id)customObjWithData:(NSData*)data
{
return [[self alloc] initWithData:data];
}
Now, you can call it...
CustomObj *obj = [CustomObj customObjWithData:data];
You would need to do something like...
+ (CustomObj *)customObjWithData:(NSData *)data {
return [[[CustomObj alloc] initWithData:data] autorelease];
}
...and implement an initWithData: to handle initialization of variables.
Al lot of answers here are correct. I self always create a method for easy alloc en instantiation through Storyboard-instantiation:
//FooViewController.m
+ (id)create
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Storyboard" bundle:nil];
return [storyboard instantiateViewControllerWithIdentifier:#"FooViewController"];
}
+ (UINavigationController *)createWithNavagionController
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Storyboard" bundle:nil];
FooViewController *fooViewController = [storyboard instantiateViewControllerWithIdentifier:#"fooViewController"];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:fooViewController];
return navigationController;
}
And if you need parameter, create class-methods like: createWithName:(NSString *)name
Related
Is there any way to get the result of:
NSObject *a = [[NSObject alloc] init];
[a class];
Without instantiating?
I wish to pass Class object to function as argument, and check it then. Like this:
- (void) pushControllerOfClass: (Class) aClass
{
if([aClass isSubclassOfClass: *what should I write here?*]) {
...
}
}
Intuitively, I've tried to write
if([aClass isSubclassOfClass: UIViewController]) {
But it doesn't work.
Thx for future response.
Update:
Is such a function considered bad in ObjectiveC?
I've refactored code from Nahavandipoor's book iOS 5 Programming Cookbook.
It was like that:
- (void) pushSecondController{
SecondViewController *secondController = [[SecondViewController alloc]
initWithNibName:nil
bundle:NULL];
[self.navigationController pushViewController:secondController animated:YES];
}
As for me, this is kind of bad function:
It doesn't parametrized when it should be.
Try with:
if([aClass isSubclassOfClass:[UIViewController class]])
In answer to your question, you don't need an instance of an object to get its class. You can get it directly from the class like:
[NSString class];
[YourClass class];
However, if all you are wanting is a method that can push any view controller you give it, then you can just use UIViewController as the parameter type:
- (void)pushAnyViewController:(UIViewController *)viewController
{
[self.navigationController pushViewController:viewController animated:YES];
}
This will take any view controller that is a subclass of UIViewController and push it onto the navigation stack.
If you really want the method to handle the allocations as well you can do:
- (void)pushViewControllerClass:(Class)viewControllerClass
{
if ([viewControllerClass isSubclassOfClass:[UIViewController class]])
{
id viewController = [[viewControllerClass alloc] initWithNibName:nil bundle:nil];
[self.navigationController pushViewController:viewController animated:YES];
}
}
And call it like this:
Class viewControllerClass = [MyViewController class];
[self pushViewControllerClass:viewControllerClass];
Just make sure that the nib is named the same as the class if you are using one.
You can write [UIViewController class] to get the class object.
There should be a class method on most classes to get the class.
If you find yourself doing this often, perhaps revisit your design?
I have the following situation, i can't resolve:
#interface Deck : NSObject
#interface MasterDeck : Deck
#interface PlayerDeck : Deck
Inside MasterDeck class, as part of initialization, i call
[self cutDeckImageIntoCards]; // We don't get to execute this method
Call results in an error [PlayerDeck cutDeckImageIntoCards]: unrecognized selector sent to instance
Indeed, PlayerDeck does not have this method .. but why is it being called at all?
After looking at MasterDeck's initialization i added a few debugging statements:
static MasterDeck *gInstance = NULL;
+(MasterDeck *) instance {
#synchronized(self) {
if (gInstance == NULL) {
gInstance = [[self alloc] init];
}
}
return gInstance;
}
-(id) init {
if (gInstance != NULL) {
return gInstance;
}
// MasterDeck
self = [super init];
// PlayerDeck
if (self) {
// Lots of stuff
[self cutDeckImageIntoCards]
// Some more stuff
}
gInstance = self;
return gInstance;
}
Ok, so MasterDeck is PlayerDeck because' Deck thinks it is a PlayerDeck ... Deck confirms
Deck is created as follows:
static Deck *gInstance = NULL;
+(Deck *) instance {
#synchronized(self) {
if (gInstance == NULL) {
gInstance = [[self alloc] init];
}
}
return gInstance;
}
-(id) init {
if (gInstance != NULL) {
return gInstance;
}
self = [super init];
if (self) {
// Do something
}
NSLog(#"Deck thinks it's a %#", [[self class ]description]); // PlayerDeck
gInstance = self;
return gInstance;
}
So, again
#interface Deck : NSObject
Assuming above Singleton Implementation, why would Deck think it's actually a PlayerDeck?
So the way you've written this, if you create the PlayDeck instance first, then the Deck instance is now a PlayDeck.
And then if you go to create the MasterDeck instance, your call to [super init] dutifully returns that previous PlayDeck instance.
So why is Deck a singleton at all? Deck has two subclasses that are singletons, but are you really looking for a singleton Deck also?
At a minimum, you can make this sort of work by not setting gInstance from within each init. Let the class method do that. Just return self from each of the init's. Also, remove the check for gInstance being not null, other Deck's init will always return Deck's instance once you have an instance of Deck.
But beyond that, I would rethink this idea a bit. Hope that helps.
You'll probably want to separate your singleton class from the actual class.
Try implementing it as in this example,
+(id) instance {
static dispatch_once_t pred;
static MasterDeck *sharedInstance = nil;
dispatch_once(&pred, ^{
sharedInstance = [[MasterDeck alloc] init];
});
return sharedInstance;
}
What happens if you replace [[self alloc] init] with [[MasterDeck alloc] init]?
It may be that somehow self is PlayerDeck. To make sure, you could NSLog([self description]) just before calling + alloc.
Edit
I assume that the interesting part of the code you have above is part of the #implementation of MasterDeck. My suggestion would be to try a lot more logging, including determining what super and [self class] are before calling [super init], although these may be misleading...
Also, as a side note, I believe that you should call [self release] in init if you are returning the previously-created instance.
What does the [super init] method look like? Can you step into it, or is it the default initializer?
Edit 2
I think you're doing singletons wrong. If you initialize a PlayerDeck, that would create a singleton in Deck which is an instance of PlayerDeck. Then later, when you initialize a MasterDeck, calling [super init] will return the instance already created by the PlayerDeck.
It looks like you try to be clever, but fact is - often the computer is even smarter. :)
Your deck class caches an instance in gInstance - in fact, it looks like it may store a Deck, a PlayerDeck, or a MasterDeck, depending on what and how you call / instantiate first. After that, this very instance is returned by that init method.
I strongly suggest to get this code clean and readable. I bet there are numerous problems with this code - but your problem is already a good example. Your logic (which should be simple, I guess) can surely be implemented much easier.
Note - I'm not against singletons, but this sort of code stacking is an absolute no-go. It's hard to get more dependency logic into those lines. ;)
I have an object name usr. I want to change views and I want to pass it along to the new view. How can I pass this object?
RVUser *usr = [[RVUser alloc] init];
UIViewController* exampleController = [[exampleClass alloc] initWithNibName:#"RVListsController" bundle:nil];
if (exampleController) {
exampleController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:exampleController animated:YES];
if (exampleController.title == nil) {
NSLog(#"enters");
//exampleController.title = #"Revistas Destacadas";
}
[exampleController release];
}
}
One way of doing it is to declare a property of type RVUser on exampleClass and assign it to that property after creating exampleController.
You should set properties BEFORE using [self presentModalViewController:exampleController animated:YES];
RVUser *usr = [[RVUser alloc] init];
UIViewController* exampleController = [[exampleClass alloc] initWithNibName:#"RVListsController" bundle:nil];
if (exampleController) {
exampleController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
if (exampleController.title == nil) {
NSLog(#"enters");
//exampleController.title = #"Revistas Destacadas";
}
//TODO: Set exampleController properties, declared as #property (nonatomic, release) RVUser *passedUsr;
//exampleController.passedUsr = usr;
//[usr release];
[self presentModalViewController:exampleController animated:YES];
}
[exampleController release];
You'll want to declare the instance of ExampleController as your custom UIViewController instead of UIViewController - this will give you code completion and compile time warnings if you call a method/property on it that doesn't exit. Then, change your definition of exampleClass (read the naming convention guide also) to have a property of type RVUser, like so:
#property (nonatomic, retain) RVUser *user;
And in the implementation file:
#synthesize user;
Now you can pass your object to that controller before you display it:
ExampleController.user = usr;
You really should read the intro guides on the Apple developer site, they cover this and a lot more that you need to know if you want to write iOS apps.
I have two controllers, First- and SecondViewController. I want to share some methods of FirstViewController to use them in my SecondViewController.
This is how I create SecondViewController in FirstViewController:
sms = [[SecondViewController alloc] init];
UINavigationController *navController = [[UINavigationController alloc]
initWithRootViewController:sms];
[self presentModalViewController:navController animated:YES];
I thought of passing the current instance of the FirstViewController to the SecondViewController which extends UIViewController. By default the initWithNibName method of SecondViewController is called.
How do I achieve this in objective-c?
Im not completely sure I understand the problem... as part of the issue has to do with how you are instantiating SecondViewController... posting that code could help.
but to answer your question as you have asked it.... "how to pass FirstViewController into SecondViewController"...
in your SecondViewController.h create your own init method
-(id) initWithFirstViewController:(UIViewController *)theFirstViewController;
and in the .m file...
-(id) initWithFirstViewController:(UIViewController *)theFirstViewController
{
self = [super initWithNibName:#"myNibName" bundle:nil];
self.firstViewController = theFirstViewController; //this assumes you have created this property. Also, do NOT retain the first view controller or you will get circular reference and will secondviewcontroller will leak.
return self;
}
then.. and here is the key.. make sure you are calling the correct init method to instantiate SecondViewContoller
SecondViewController *svc = [[SecondViewController alloc]initWithFirstViewController:self];
now.. having said that.. looking at your SO rating, I have a feeling you already knew this.. and the real question may be... why is the initWithNibName being called when you are not actually calling it explicitly?
Not 100% sure what you are after. The content of your question about sharing methods doesn't seemt o match the question of passing in a parameter at init.
With regard to methods, you can call a method from your parentViewController
if ([self.parentViewController respondsToSelector:#selector(someMethod)]) {
[self.parentViewController someMethod];
}
If you want to pass a parameter in on any class at init, you will want to write a custom init method with any additional parameters you want. That custom method should begin with the appropriate [self init] call. You can even have multiple custom init methods which can be handy.
Here is an example for class that downloads json or xml feeds.
- (id)initWithID:(NSString *)useID delegate:(id)setDelegate urlString:(NSString *)urlString feedIsJSON:(BOOL)feedIsJSON failedRetrySecondsOrNegative:(float)failedRetrySecondsOrNegative refreshSecondsOrNegative:(float)refreshSecondsOrNegative {
if ((self = [super init])) {
// Custom initialization
processing = NO;
self.delegate = setDelegate;
self.feedID = [NSString stringWithFormat:#"%#", useID];
self.feedURLString = [NSString stringWithFormat:#"%#", urlString];
self.isJSON = feedIsJSON;
if (failedRetrySecondsOrNegative>=0.0f) {
retryFailedSeconds = failedRetrySecondsOrNegative;
} else {
retryFailedSeconds = kFailedRefresh;
}
if (refreshSecondsOrNegative>=0.0f) {
refreshSeconds = refreshSecondsOrNegative;
} else {
refreshSeconds = kSuccededRefresh;
}
}
return self;
}
Hope this helps.
Normally I don't have any problem adding a setter method to a class.
However, I'm trying to add to library usage class, which must be causing the problem.
Heres the class I've added it to...
#interface GraphController : TKGraphController {
UIActivityIndicatorView *indicator;
NSMutableArray *data; //I've added
NSString *strChartType; //I've added
}
-(void)setContentType:(NSString*)value; //I've added
#end
#implementation GraphController
-(void)setContentType:(NSString*)value { //I've added
if (value != strChartType) {
[value retain];
[strChartType release];
strChartType = value;
NSLog(#"ChartType=%#", strChartType);
}
}
Heres where I'm getting a warning..
UIViewController *vc = [[GraphController alloc] init];
[vc setContentType:myGraphType]; //Warnings on this line see below
[self presentModalViewController:vc animated:NO];
[vc release];
myGraphType if defined in my constant class.
* Warnings *
warning: 'UIViewController' may not respond to '-setContentType:'
warning: (Messages without a matching method signature
I know the first warning appears when you haven't added the method to the implementation. but I have.
Where am I going wrong ?
UIViewController *vc = [[GraphController alloc] init];
means that vc points to an instance of GraphController but the variable itself is of type UIViewController *, and UIViewController doesn’t declare a -setContentType: method.
Replace that with
GraphController *vc = [[GraphController alloc] init];
to tell the compiler you’re working with a GraphController instance, and it will recognise your -setContentType: method.
You just have to let the compiler know that you're working with a class that it knows responds to the method. You can do it a couple of ways but the easiest one if you just want to eliminate the warning is to cast the object in line before you make the method call.
UIViewController *vc = [[GraphController alloc] init];
[(GraphController *)vc setContentType:myGraphType]; //No warning should appear now.
[self presentModalViewController:vc animated:NO];
[vc release];