I am working a big iOS project, the design is not a nice as I would like it to be, but I must stick to it. (life can be a bitch sometimes).
The thing is that we have a Library that basically let's you browse a catalog. You have a filter, where you specify a certain search criteria, and you are presented with a list were you can press on the items that you are interested. When you press an item, you can see a more detailed description of it.
The company were a work for, sells this same software to many different companies that have different catalogs. The idea is that the Library has all the main functionality, and the project that use it, might in some way extend or completely override some of the given interfaces.
To give you an example, imagine my library has 2 classes that manages 2 views. They would be "FilterViewController" and "DetailsViewControllers". In some place of the code this classes gets instantiated. It would look something like this
My approach is something like this:
ProjectA side
// Here I configure the library
Library.FilterViewClass = ProjectAFilterViewController;
Library.DetailsViewClass = ProjectADetailViewController;
ProjectB side
Library.FilterViewClass = ProjectBFilterViewController;
Library.DetailsViewClass = nil;
Library side
// Did the user configure the library?
if(self.FilterViewClass == nil){
// I alloc the default ViewController
myFilterViewController = [[FilterViewController alloc] init];
}else{
// Here I would like to alloc the custom ViewController
myFilterViewController = [[Library.FilterViewClass alloc] init]; // DaProblem!
}
The problem with that approach is that I actually don't know if it's possible to instantiate object programmatically. Or at least I don't know how. Maybe I am using the wrong approach, some direction would be appreciated. Txs in advance!
To get class from string you can use this function
Class cl = NSClassFromString(#"MyClass");
To get class of existing variable just call class method.
Class cl = [obj class]; // assuming obj1 is MyClass
Now you can create instance of MyClass
MyClass *myClass = (MyClass*)[[cl alloc] init];
...
[myClass release];
Use
myFilterViewController = [[[Library.FilterViewClass class] alloc] init];
You can also instantiate from a class name, should that be useful to you:
id obj = [[NSClassFromString(#"MyClass") alloc] init];
Class someClass = [Foo1 class];
Foo * someObject = [[someClass alloc] init];
[someObject bar];
Class someClass2 = [Foo2 class];
Foo * someObject2 = [[someClass2 alloc] init];
[someObject2 bar];
Interface+Implementation:
#interface Foo : NSObject
- (void)bar;
#end
#interface Foo1 : Foo
#end
#interface Foo2 : Foo
#end
#implementation Foo
- (void)bar {
NSLog(#"abstract foo");
}
#end
#implementation Foo1
- (void)bar {
NSLog(#"foo1bar");
}
#end
#implementation Foo2
- (void)bar {
NSLog(#"foo2bar");
}
#end
Output:
2011-11-24 11:24:31.117 temp[21378:fb03] foo1bar
2011-11-24 11:24:31.118 temp[21378:fb03] foo2bar
Related
We're all familiar with the following pattern for instantiating instances of a class:
+ (instancetype)createInstance {
return [[self alloc] init];
}
This works because "self" in this case refers to the class rather than an object built from the class blueprint.
We also know this declaration, most commonly used in avoiding retain cycles:
typeof(self) someStrongSelf = self;
This is allows self's type to be dynamic and that code could be copy-pasted wherever needed no matter the class.
My question deals with combining the above two patterns when instantiating from a class method:
+ (instancetype)createInstance:(MyObject*)dependency {
typeof(self) instance = [[self alloc] init];
instance.dependency = dependency;
return instance;
}
This won't work because self is a class, and a typeof(class) is just a Class, but is there some mechanism for local variables equivalent to instancetype that would allow me the same flexibility as typeof(instance)? For example:
+ (instancetype)createInstance:(MyObject*)dependency {
instanceof(self) instance = [[self alloc] init]; //desired keyword
instance.dependency = dependency;
return instance;
}
If I really wanted this formalized, I know an alternative to this would be defining a protocol that does basically the same thing as above, but I'm curious if Objective-C allows the desired declaration style out of the box.
I understand what you're looking for, but there is no instanceof(self) pattern. The following achieves what you want, though admittedly doesn't have the elegance of typeof(self) pattern:
#interface Foo: NSObject
#property (nonatomic, copy) NSString *string;
#end
#implementation Foo
+ (instancetype)fooWithString:(NSString *)string {
Foo *foo = [[self alloc] init];
foo.string = string;
return foo;
}
#end
#interface Foobar: Foo
// perhaps some more properties here
#end
#implementation Foobar
// and perhaps some more methods here
#end
This implementation demonstrates that the convenience method still allows subclassing. I.e., you can do:
Foobar *foobar = [Foobar fooWithString:#"baz"];
And the resulting object will be a Foobar instance.
I have a class like so:
#interface Foo : NSObject <FooDataProvider>
...
#end
and somewhere along the line in another class I have a method with the interface:
-(void) doStuff:(id<FooDataProvider>)fooProvider{
...
}
And yet somwhere else I have
-(void) passMeClasses:(Class)theClass
{
<do stuff based on the class>
}
Usually I pass things to this method simply like
Foo* f = [[Foo alloc] init];
...
[bar passMeClasses:[f class]];
But Im not sure how to pass things I only have the id.. like so
id<FooDataProvider> f = [[Foo alloc] init];
....
[bar passMeClasses:[f ?????]];
or alternatively how to do it from the doStuff method
-(void) doStuff:(id<FooDataProvider>)fooProvider{
Class c = [fooProvider howDoIGetMyClass];
bar passMeClasses:c];
}
Can someone help me in determining the class from the id?
Sorry for the long winded explanation, but hopefully its clear!
[f class]
class is a method. It's called on the object. The object will return its class (or it should; it can technically return something else and sometimes does). The type of the variable is completely irrelevant at runtime. At runtime all object pointers are id. That's why method signature type encodings only designate "an object goes here." (See #.) They can't express the specific type of the object.
Is it possible at Objective C at init method to return an instance of different classes?
I'm having a Class called: MyCustomClass. I also have two other different classes called Class 1 and Class2. What I'm trying to implement is: When I call [[MyCustomClass alloc] initWithSomeParameters to create instance of Class1 or Class2 depending on some condition.
MyCustomClass.m:
#import "MyCustomClass.h"
#import "Class1.h"
#import "Class2.h"
-(id) initWithSomeParameters: (id) params{
id myClass;
if (someCondition){
myClass = [[Class1 alloc] initWithSomeParameters:(id) params];
[myClass setSomething:something];
}else{
myClass = [[Class2 alloc] initWithSomeParameters:(id) params];
[myClass setSomething:something];
}
return myClass;
}
...and later I call
id myCustomClass = [[MyCustomClass alloc] initWithSomeParameters:(id) params];
Is this a wrong approach? If so, what would be the correct one?
Several others have mentioned this, but the result of calling [[MyClass alloc] init] must always be nil or a kind of MyClass. It doesn't have to specifically be an instance of MyClass; one of its descendants is possible, as with NSArray or NSString. In code, this requirement would look like:
MyClass *a = [[MyClass alloc] init];
NSAssert((a==nil) || [a isKindOfClass:[MyClass class]], #"This must always hold true.");
I've never attempted to implement this, but it would probably have to look something like this:
- (id)initAsSubclass:(NSString *)sublcassName
{
Class c = NSClassFromString(subclassName);
self = [[c alloc] init];
if (self) {
// Do Custom Init Here
}
return self;
}
The keys would be:
DO NOT perform [super init].
Create a completely new object with +alloc.
Assign the newly created object to self.
If not using ARC, perform [self autorelease], before replacing the value. (If the object that is currently executing code becomes deallocated, it can cause issues. -autorelease will defer that until this section is complete.)
You should make some kind of controller, which initializes correct classes. You can also achieve same that using class methods.
ANd in genreal this given implementation is bad, because you alloc memory once [MyCustomClass alloc] and then in -(id)initWithSomeParameters:(id)params you are allocating memory again. So, even different address will be retruned, that isn't agains apple guidelines, some apple classes also have such behavior, but they do it because of optimizations. But here it is wrong.
Its not a good approach. Its better use some helper class or us factory pattern and provide parameters to method. Then depending on parameters create an object of class and return.
Its not good approach to create object of different class in init method of different class.
Edit:
if You want to show UIView or UIAlertView depending on iOS version do like this.
#interface AlertHelper : NSObject
+ (id)getAlert;
#end
///
#implementation AlertHelper
+(id)getAlert{
NSString *version = [[UIDevice currentDevice] systemVersion];
int ver = [version intValue];
if (ver < 7){
//For iOS 6
return something;
}
else{
//for ios 7
return something
}
}
#end
The way to do it is like this:
Create Base class like:
#import "Base.h"
#import "Class1.h"
#import "Class2.h"
#implementation Base
+ (id)classWithParams:(id)params
{
id retVal = nil;
if (condition_based_on_params_means_creating_class1)
{
retVal = [[Class1 alloc] initWithSomeParameters:params];
}
else
{
retVal = [[Class2 alloc] initWithSomeParameters:params]
}
return retVal;
}
#end
Class1 inherits from Base:
#interface Class1 : Base
{
}
- (id)initWithSomeParameters:(id)parameters;
#end
Class2 inherits from Base:
#interface Class2 : Base
{
}
- (id)initWithSomeParameters:(id)parameters;
#end
Ultimately you will have:
Base* a = [Base classWithParams:yourParams];
What is the difference of using:
+ (id) myMethod;
// Rather than
- (id) myMethod;
Using a + declares the method as a class method, or a method that can be called directly on the class, where the class is the object. So when you have this:
#implementation Foo
+(NSString*)method1 {
return #"Foo";
}
-(NSString*)method2 {
return #"Foo";
}
#end
The methods are called in different ways:
[Foo method1]; //=> #"Foo"
Foo* f=[[Foo alloc] init];
[f method2]; //=> #"Foo"
One other thing to note is that class methods don't have access to an instance, which means they can't access any kind of instance variables.
#Linuxios pretty much summed up the concept of class and instance method. However, since you mentioned getters and setters in your title, I want to point out that in Objective-C you can use properties instead of writing your own accessor methods. For example,
In the header file, you will have something like this:
#interface MyObject : NSObject
#property (nonatomic,retain) NSSet* mySet;
#end
In the m file, you wil have something like this:
#implement MyObject
#synthesize mySet;
#end
To access the set in another class you can do it like this:
myObject.mySet; // assuming myObject is an instance of the MyObject class
The top one is a class method (no instance required)
The second one is a instance variable (attached to a specific instance).
This answer explains the methods quite well:
Method Syntax in Objective C
[MyObject myMethod]; // did not have to create an instance
MyObject* myNewObject = [[MyObject alloc] init] autorelease];
[myNewObject myMethod]; // had to create an instance
How does one detect the calling class from within a static method such that if the class is subclassed the subclass is detected? (See comment inside MakeInstance)
#interface Widget : NSObject
+ (id) MakeInstance;
#end
#implementation Widget
+ (id) MakeInstance{
Class klass = //How do I get this?
id instance = [[klass alloc] init];
return instance;
}
#end
#interface UberWidget : Widget
//stuff
#end
#implementation UberWidget
//Stuff that does not involve re-defining MakeInstance
#end
//Somewhere in the program
UberWidget* my_widget = [UberWidget MakeInstance];
I believe the appropriate solution for what you are trying to accomplish is this:
+ (id) MakeInstance{
id instance = [[self alloc] init];
return instance;
}
And as Cyrille points out, it should probably return [instance autorelease] if you want to follow convention (and aren't using ARC).
UIAdam's solution is perfectly fine for your case. Although if you want to detect, more specifically, from which class is you method called, use [self class] on objects, or simply self for classes.