ObjC 协议可能无用

Posted

技术标签:

【中文标题】ObjC 协议可能无用【英文标题】:ObjC protocols potentially useless 【发布时间】:2012-11-16 09:05:43 【问题描述】:

在 ObjC 中,我们可以使用协议来限制 id 行为,因此我们可以声明类似 -(void)aMethod:(id<aProtocol>)aVar 在我们提供一个值或非id 变量作为aVar 之前工作得很好,但这完全被破坏了,因为我们可以传递一个没有协议说明符的通用id 变量...这是正常的吗?有什么解决方法吗?我错过了什么吗?

【问题讨论】:

您是否尝试为没有协议的方法提供id 变量? 是的,我能做到,这就是问题所在 【参考方案1】:

只需少用id,并尽可能使用正确的类型声明变量和参数。也就是说:不要到处传ids。如果您正在实现一个集合类(例如),那么id 通常很有用。

我的方法是指定类型,并在源代码中尽可能地引入该类型。所以我省略了id 并添加了类型,当(例如)我从集合中获取引用时,我创建了一个变量:

MONType<MONProtocol>* thing = [array objectAtIndex:idx];
// now thing is correctly typed. use thing.

同样,如果我有一个id 参数,我声明一个新变量:

- (IBAction)someAction:(id)sender

  NSButton * button = sender;
  // now use button, not sender

协议非常很有用。很多时候,比子类化更好/更干净。

【讨论】:

我想:当我只能从值只是 id 的 NSArray(例如)传递值时,就会出现问题 @user732274 查看我的第一个代码示例。我声明了一个新变量。 很好的例子,但这只是一个技巧:如果我为其他开发人员开发了一个框架并且他们传递了一个不采用该协议的对象数组怎么办?我知道他们必须先阅读文档,我只是想知道是否有更可靠的机制 @user732274 不必费心容纳这些客户。颠倒角色:如果您编写了[[NSString string] subviews],您应该欣赏运行时(和/或编译器)错误/警告,而不是试图掩盖该错误的实现:) 失败。早点失败。【参考方案2】:

您没有理解 Objective-C 中的类型是在运行时确定的,而不是在编译时确定的。仅仅因为你说一个对象的类型是id&lt;aProtocol&gt; 并不意味着在运行时它就一定是这样。

将某些内容指定为id&lt;aProtocol&gt; 的想法是帮助开发人员和使用您的代码的人。它可以帮助您作为开发人员,因为如果您尝试对编译器可以确定它认为在其假定类型的实例上不存在的东西调用方法(不包括转发,这可能意味着实例响应编译器无法确定的内容)。它可以帮助人们使用您的代码,因为它告诉他们在与您的代码交互时应该遵守的合同。

所以,在你的问题中,你是这么说的:

但是如果我们传递一个没有协议说明符的通用 id 变量,这将被完全破坏

好吧,编译器会警告并告诉您您正在尝试传递不符合该协议的内容,但传递id 的情况除外。这就是为什么您通常应该尝试更精确地输入内容,而不仅仅是 id

如果你有这样定义的方法:

- (void)aMethod:(id<aProtocol>)aVar

那么aVar 可以是SomeSubclass 类型,其定义如下:

@interface SomeSubclass : NSObject <aProtocol>

然后你可以像这样使用aMethod

SomeSubclass *obj = [SomeSubclass new];
[other aMethod:obj];

【讨论】:

我知道,但我总是尽量在编译时获得最大收益 当然。然后使用协议。我真的不明白你想说什么? 我不应该将通用 id 变量作为 id 传递...但是如果我这样做编译器不会抱怨 "好吧,编译器会警告" 实际上,将 id 类型的东西分配给任何其他对象指针类型(包括 id&lt;aProtocol&gt;)永远不会产生警告,因为 id 的点是它会关闭静态类型警告。【参考方案3】:

我(终于)发现使用 Objective-C++ 是可行的方法。假设我希望能够传递NSStringNSNumber(而不是使用太多通用的id,而不是使用传递id 值变得无用的协议):好吧,我可以创建一个具有两个不同的构造函数,一个用于每个 ObjC 类,因此不能再(几乎直接)传递 id 值。例如,我们来看看

class NSStringOrNSNumber
    public:
        NSStringOrNSNumber(NSString *);
        NSStringOrNSNumber(NSNumber *);
;

最大的优势是采用NSStringOrNSNumber 参数的方法/函数可以直接获取NSString/NSNumber 值,因为构造函数充当隐式强制转换。换句话说,如果我们有

void aFunction(NSStringOrNSNumber param);

以下调用完全有效:

aFunction(@"Hello!");
aFunction(@25);

唯一的(小)缺点是,如果我们想取回传递给构造函数的值,我们需要类实现一个函数。

使用 C++ 类构造函数来获取类似 id&lt;NSCoding&gt; 的内容仍然比直接使用 id&lt;NSCoding&gt; 更好:事实上,如果我们执行以下操作

@class classOne, classTwo;

class NSCodingClass
    private:
        NSCodingClass(classOne *);
        NSCodingClass(classTwo *);
    public:
        NSCodingClass(id<NSCoding>);

我们将无法将通用 id 作为参数传递(因为它会模棱两可:编译器无法知道在两个私有构造函数中调用哪个构造函数)

【讨论】:

以上是关于ObjC 协议可能无用的主要内容,如果未能解决你的问题,请参考以下文章

“无用发明家”告诉你,5G推开了三重门

Android Studio去除项目无用的资源

Android Studio去除项目无用的资源

利用 Android Gradle 瘦身 apk

被一个无用的网站反爬到自闭--糗事百科

没有目标的项目是在做无用功