Reputation: 3856
I'm having trouble wrapping my thoughts about class inheritance. I'm suppsed to create a dashboard like interface in a app, and I'll have maybe 10 widgets/dashlets on that dashboard view. All those dashlets/widgets will have basically same look, with a title on the top, borders, row of buttons on the top and a graph. Let's say I create a subclass of UI View called 'Dashlet' with properties and outlets, and create XIB file with proper layout and connected outlets etc.
Now I want to create several subclasses of that 'Dashlet' view that will only process data differently, and draw different graphs. My current code looks something like this:
Dashlet.h
@interface Dashlet : UIView{
@private
UILabel *title;
UIView *controls;
UIView *graph;
}
@property (weak, nonatomic) IBOutlet UILabel *title;
@property (weak, nonatomic) IBOutlet UIView *controls;
@property (weak, nonatomic) IBOutlet UIView *graph;
-(Dashlet*)initWithParams:(NSMutableDictionary *)params;
-(void)someDummyMethod;
@end
And in Dashlet.m
- (id) init {
self = [super init];
//Basic empty init...
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
-(id)initWithParams:(NSMutableDictionary *)params
{
self = [super init];
if (self) {
self = [[[NSBundle mainBundle] loadNibNamed:@"Dashlet" owner:nil options:nil] lastObject];
//some init code
}
return self;
}
Now let's say that I create a subclass called CustomDashlet.h:
@interface CustomDashlet : Dashlet
@property (nonatomic, strong) NSString* test;
-(void)testMethod;
-(void)someDummyMethod;
@end
and CustomDashlet.m
-(id)init{
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
-(id)initWithParams:(NSMutableDictionary *)parameters
{
self = [super initWithParams:parameters];
if (self) {
//do some stuff
}
return self;
}
This, kind of works, but I need to override some of the methods declared in the superclass or even add some of my own. Whenever i try to do something like this in CustomDashlet.m
[self someDummyMethod]
or even [self testMethod]
I get an exception error like this:
NSInvalidArgumentException', reason: '-[Dashlet testMethod]: unrecognized selector sent to instance
Am I even doing this right? Did I miss something? Am I supposed to make this work in some other way? If anyone has suggestions, please feel free to share your thoughts, thank you for all the help.
Upvotes: 6
Views: 14090
Reputation: 540055
The problem is that
SalesDashlet *sales = [[SalesDashlet alloc] initWithParams:nil];
does not return a SalesDashlet
instance, as expected, but a Dashlet
instance.
Here is what happens:
[SalesDashlet alloc]
allocates an instance of SalesDashlet
.initWithParams:
is called with this instance,
and calls self = [super initWithParams:parameters]
.initWithParams
discards self
and
overwrites it with a new instance loaded from the Nib file. This is an instance
of Dashlet
.Therefore SalesDashlet *sales
is "only" a Dashlet
, and calling any subclass
method on it throws an "unknown selector" exception.
You cannot change the type of objects loaded in the Nib file. You could create a second
Nib file containing a SalesDashlet
object. If the main purpose of the subclass is
to add additional methods, then the easiest solution would be to add these methods
in a Category of the Dashlet
class.
Upvotes: 5
Reputation: 10625
I believe you simply need to change following line in your Dashlet.h
file:
-(Dashlet*)initWithParams:(NSMutableDictionary *)params;
to following:
-(id)initWithParams:(NSMutableDictionary *)params;
or better:
-(instancetype)initWithParams:(NSMutableDictionary *)params;
Upvotes: 3
Reputation: 62072
You need to change your init
methods.
-(Dashlet*)initWithParams:(NSMutableDictionary *)params
-(SalesDashlet*)initWithParams:(NSMutableDictionary *)parameters
The return type on both of these should be id
.
The problem you're running into is similar to trying to do this:
NSMutableArray *someArray = [[NSArray alloc] init];
Despite declaring someArray
as an NSMutableArray
, you've initialized it as an NSArray
, and as such, someArray
will actually be an immutable NSArray
.
So because your SalesDashlet
init method calls its super
init method and the super
explicitly returns an object of type Dashlet
, then the SalesDashlet
will also return an object of type Dashlet
, so you're trying to call testMethod
(a method that only exists in SalesDashlet
) on an object of type Dashlet
(which doesn't know about the testMethod
method).
Changing your return type to id
will make the methods return an object of the right type.
As a note, you've done your init
, and initWithFrame
methods correctly.
SalesDashlet *mySalesDashlet = [[SalesDashlet alloc] initWithFrame:someFrame];
Creating a SalesDashlet
in this way will allow you to call [mySalesDashlet testMethod]
.
Your initWithFrame
has return type of id
in both super and sub classes.
Upvotes: 1
Reputation: 4735
If the problem is with the
- (Dashlet *)initWithParams:
method it is because the base class declares it with a Dashlet return value, whereas the subclass is redeclaring it with a SalesDashlet return instance.
Always use instancetype as the return type for any init method.
Upvotes: 3