Objective c init方法返回不同类型的对象是可以接受的模式吗?

Objective c init方法返回不同类型的对象是可以接受的模式吗?,objective-c,xcode,Objective C,Xcode,我正在修复一些现有objective-c代码的错误,遇到了一些我觉得奇怪的事情: @interface ClassA : UIView ... static ClassA* oldSelf = nil; @implementation - (id)initWithFrame:(CGRect)frame { oldSelf = self; self = [[ClassB alloc] initWithFrame:(CGRect)frame]; // xcode warns: I

我正在修复一些现有objective-c代码的错误,遇到了一些我觉得奇怪的事情:

@interface ClassA : UIView
...

static ClassA* oldSelf = nil;

@implementation
- (id)initWithFrame:(CGRect)frame {
    oldSelf = self;
    self = [[ClassB alloc] initWithFrame:(CGRect)frame]; // xcode warns: Incompatible pointer types assigning to "ClassA *" from "ClassB *"
    //       ^^^^^^ Is this ok?
    [oldSelf release];
    return self;
}


@interface ClassB : UIView
...

@implementation
- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    return self;
}
这整件事都被包装到一个静态库中。公众获得lib.a文件和ClassA.h 在使用库的代码中,会发生以下情况:

#import "ClassA.h"

...

// useage
ClassA *myA = [[ClassA alloc] initiWithFrame:CGRectMake(0,0,100,100)];
...
我们得到了一个ClassA的初始值设定项,它实际上返回了一个不相关的类。ClassA和ClassB响应相同的消息,因此它编译并运行。似乎ClassA被用来掩盖ClassB中暴露的一些特性

我很好奇这是否是可以接受的行为,如果这是一种已知的模式,它叫什么?此设置是否有任何副作用

=========================================================

谢谢大家的回答!我想我明白了。。。简言之,这不是一个正常的模式,也不是一个好主意

  • 有点像“类集群”(抽象工厂),但不完全一样,因为应该返回一个公共抽象类。而且,由于代码似乎从来没有打算返回除ClassB对象以外的任何对象,可能不是原始作者所想的
  • 更像是代理,但实现错误。ClassA应该持有ClassB的私有实例,并在两者之间传递消息
=========================================================

编辑:添加了“oldSelf”部分

已编辑:已添加静态库详细信息


编辑:添加了关于已接受答案的简介…

这不是我所知道的模式


如果我理解正确的话,通常的做法是让两个类都从同一个抽象基类继承。

类集群就是这样实现的。一种相关的技术,
isa
-swizzling可以用来实现一种状态机。它确实需要相同的ivar布局才能工作。就副作用而言,我相信它可能会破坏KVO;但是有人可能会在这一点上纠正我。

在用户代码中返回不相关的类肯定不常见,但是在苹果的一些框架中,返回具有相同公共接口的类的更具体版本是很常见的


Apple的文章详细讨论了一个事实,即像NSArray和NSNumber这样的对象返回的对象可能与您所请求的类不同

我在这里看到的主要缺点是:
ClassA
的用户希望他刚刚通过
[[ClassA alloc]initWithFrame:…]创建的对象返回
[object iskindof class:[ClassA class]
的YES 在使用诸如
NSInvocation
之类的东西时,这也可能导致错误,因为将使用错误的类来确定方法签名,尽管我不确定这一点

由于客观的Cs动态特性,正如您所描述的,这将起作用,但使用起来可能会混淆,我强烈建议任何人不要使用这种模式


正如pilavdzice所说,“正确”的替代方案是让
ClassA
ClassB
从另一个类继承(一个Abstract超类)然后在其初始值设定项中决定要使用的具体子类。这种模式的好例子,称为类集群,有
NSString
NSArray
NSDictionary
,它们都会根据初始化方式返回不同子类的对象,这也是您不能将这些子类子类化的原因不费吹灰之力就直接使用se。

正如@alan Dunchun所指出的,这种技术被称为类集群,有点常见。但是您的实现有点不正确。您永远不应该返回不兼容的类型。在您的示例中,ClassB应该从ClassA继承。

在所有情况下这样做都不是不合理的,但很难说在你描述的情况下,这是否是一个好主意。两个可能是好主意的例子:

  • 初始值设定项返回更专业化的子类的实例。例如,您可以根据存储的项目数选择不同的数据结构实现

  • 初始值设定项返回某种代理对象

您的代码看起来确实有点奇怪。至少,我希望将强制转换视为一种信号(对编译器和未来的程序员都是如此)作者知道他在做什么。解释返回不同类型对象的原因的注释也不会有什么坏处。理想情况下,
ClassB
应该是
ClassA
的子类,因为它应该提供相同的接口。

这就是实现的方式

这样内部类就不会公开,也不会被误用。ClassB不能在ClassA的实现文件之外的其他地方初始化

如果您有多个内部类,并且您的初始值设定项以某种方式决定实际需要哪个类,那么这是有意义的


如果只使用一个内部类,我看不到任何好处。

ClassA
initWithFrame:
是否有
[自发布]
在分配给
self
之前?不可接受,因为它具有误导性。@josh是的。作为一个ObjC n00b,我不确定细节是否相关,所以我把它漏掉了。担心的是泄漏问题?@Nick:是的,没错。在这一点上,
self
指向一个分配的(尽管没有设置)实例,如果要重新分配指针,则需要释放。不过,静态指针
oldSelf
对我来说似乎很奇怪——局部变量也可以工作。您是否可以访问编写此代码的人员,以要求澄清此处的设置?@josh我同意,静态oldSelf变量非常复杂e、 代码是由一位海外顾问编写的,我必须做一些挖掘,看看他是否仍然存在:)“某种程度上”说得很委婉。一个真正的类集群