6

我在整理关于类继承的想法时遇到了麻烦。我应该在应用程序中创建一个类似界面的仪表板,并且在该仪表板视图上我可能会有 10 个小部件/仪表板。所有这些 dashlets/widgets 的外观基本相同,顶部有标题、边框、顶部按钮行和图表。假设我创建了一个名为“Dashlet”的 UI 视图子类,其中包含属性和插座,并创建了具有正确布局和连接插座等的 XIB 文件。

现在我想创建该“Dashlet”视图的几个子类,它们只会以不同的方式处理数据,并绘制不同的图形。我当前的代码如下所示:

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

在 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;
}

现在假设我创建了一个名为 CustomDashlet.h 的子类:

@interface CustomDashlet : Dashlet
@property (nonatomic, strong) NSString* test;
-(void)testMethod;
-(void)someDummyMethod;
@end

和 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;
}

这有点工作,但我需要覆盖超类中声明的一些方法,甚至添加一些我自己的方法。每当我尝试在 CustomDashlet.m 中做这样的事情时

[self someDummyMethod]甚至[self testMethod]我收到这样的异常错误:

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

我这样做对吗?我错过了什么?我应该以其他方式完成这项工作吗?如果有人有建议,请随时分享您的想法,感谢您的所有帮助。

4

4 回答 4

5

问题是

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

不像预期的那样返回一个SalesDashlet实例,而是一个Dashlet实例。这是发生的事情:

  • [SalesDashlet alloc]分配一个SalesDashlet.
  • 使用此实例调用的子类实现initWithParams:,并调用self = [super initWithParams:parameters].
  • 超类实现initWithParams 丢弃 self并 用从 Nib 文件加载的新实例覆盖它。这是 的一个实例Dashlet
  • 这个新实例被返回。

因此SalesDashlet *sales是“唯一” a Dashlet,并且在其上调用任何子类方法都会引发“未知选择器”异常。

您无法更改 Nib 文件中加载的对象类型。您可以创建包含SalesDashlet对象的第二个 Nib 文件。如果子类的主要目的是添加其他方法,那么最简单的解决方案是将这些方法添加到类的CategoryDashlet

于 2013-11-11T21:19:23.183 回答
3

如果问题出在

- (Dashlet *)initWithParams:

方法是因为基类使用Dashlet返回值声明它,而子类使用SalesDashlet返回实例重新声明它。

始终使用instancetype作为任何 init 方法的返回类型。

于 2013-11-11T18:32:17.000 回答
3

我相信您只需要更改Dashlet.h文件中的以下行:

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

遵循:

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

或更好:

-(instancetype)initWithParams:(NSMutableDictionary *)params;
于 2013-11-11T19:24:20.770 回答
1

你需要改变你的init方法。

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

这两个的返回类型都应该是id.

您遇到的问题类似于尝试这样做:

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

尽管声明someArrayNSMutableArray,但您已将其初始化为NSArraysomeArray因此实际上将是不可变的NSArray

因此,因为您的SalesDashletinit 方法调用了它的superinit 方法并且super显式返回了一个 type 的对象Dashlet,那么 theSalesDashlet也将返回一个 type 的对象Dashlet,所以您试图testMethodSalesDashlet一个 type 的对象Dashlet(其中不知道testMethod方法)。

将返回类型更改为id将使方法返回正确类型的对象。


请注意,您已经正确地完成了init, 和initWithFrame方法。

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

以这种方式创建 aSalesDashlet将允许您调用[mySalesDashlet testMethod].

您在超类和子类中initWithFrame都有返回类型。id

于 2013-11-11T18:43:24.407 回答