IOS Custom Class Subclassification

I'm having trouble transferring my thoughts on class inheritance. I support creating an interface in the form of a panel, for example, in an application, and I will have 10 widgets / dashlets in this panel view. All of these dashlets / widgets will have basically the same look, with a title at the top, borders, a row of buttons at the top and a graph. Let's say I create a subclass of the UI View called "Dashlet" with properties and outputs and create a XIB file with the proper location and connected outputs, etc.

Now I want to create several subclasses of this "Dashlet" view, which will process the 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 of 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 seems to work, 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 as follows:

 NSInvalidArgumentException', reason: '-[Dashlet testMethod]: unrecognized selector sent to instance 

Am I even doing this right? Did I miss something? Should I do this work in some other way? If anyone has any suggestions, please feel free to share your thoughts, thank you for your help.

+6
source share
4 answers

The problem is

 SalesDashlet *sales = [[SalesDashlet alloc] initWithParams:nil]; 

does not return an instance of SalesDashlet as expected, but an instance of Dashlet . Here's what happens:

  • [SalesDashlet alloc] selects an instance of SalesDashlet .
  • The implementation of the subclass initWithParams: is called with this instance, and calls self = [super initWithParams:parameters] .
  • The implementation of the superclass initWithParams overrides self and overwrites it with a new instance loaded from the Nib file. This is an example of a Dashlet .
  • This new instance is returned.

Therefore, SalesDashlet *sales is "only" a Dashlet and calls any subclass of the method on it that throws an "unknown selector" exception.

You cannot change the type of objects loaded in the Nib file. You can create a second Nib File containing the SalesDashlet object. If the main purpose of the subclass is to add additional methods, the easiest solution would be to add these methods to the categories of the Dashlet class.

+5
source

If the problem is related to

 - (Dashlet *)initWithParams: 

this is because the base class declares it with a Dashlet return value, while the subclass updates it with an instance of SalesDashlet .

Always use instancetype as the return type for any init method.

+3
source

It seems to me that you just need to change the following line in the Dashlet.h file:

 -(Dashlet*)initWithParams:(NSMutableDictionary *)params; 

:

 -(id)initWithParams:(NSMutableDictionary *)params; 

or better:

 -(instancetype)initWithParams:(NSMutableDictionary *)params; 
+3
source

You need to change the init methods.

 -(Dashlet*)initWithParams:(NSMutableDictionary *)params -(SalesDashlet*)initWithParams:(NSMutableDictionary *)parameters 

The return type for both of them must be id .

The problem you're working with is similar to trying to do this:

 NSMutableArray *someArray = [[NSArray alloc] init]; 

Despite declaring someArray as an NSMutableArray , you initialized it as an NSArray , and so someArray will actually be an immutable NSArray .

Since your SalesDashlet init method calls its super init method, and super explicitly returns an object of type Dashlet , SalesDashlet also returns an object of type Dashlet , so you are trying to call testMethod (a method that exists only in SalesDashlet ) for an object of type Dashlet (which does not know about the testMethod method).

Changing the type of the return value to id will cause the methods to return an object of the desired type.


As a note, you have correctly executed your init and initWithFrame methods.

 SalesDashlet *mySalesDashlet = [[SalesDashlet alloc] initWithFrame:someFrame]; 

Creating a SalesDashlet this way allows you to call [mySalesDashlet testMethod] .

Your initWithFrame has an id return type in super and subclasses.

+1
source

Source: https://habr.com/ru/post/957855/


All Articles