在ObjC中,我们可以使用协议来限制id
行为,因此我们可以声明类似的内容
-(void)aMethod:(id<aProtocol>)aVar
效果很好,直到我们提供一个值或非id
变量为aVar
,但由于我们可以传递一个通用的id
变量,因此它完全被破坏了协议说明符......这是正常的吗?有没有解决方法?我错过了什么吗?
答案 0 :(得分:3)
你不了解Objective-C中的类型是在运行时确定的,而不是编译时。仅仅因为你说某个对象的类型为id<aProtocol>
并不意味着它在运行时保证是这样的。
将某些内容指定为id<aProtocol>
的想法是为了帮助您作为开发人员和使用您的代码的人。它可以帮助您作为开发人员,因为如果您尝试在编译器可以确定它认为存在于其假定类型的实例上的某些内容上调用某个方法,编译器将发出警告(或ARC下的错误)(不包括转发,这可能意味着实例响应编译器无法确定的内容。它帮助人们使用您的代码,因为它告诉他们在与您的代码交互时应该遵守的合同。
所以,在你的问题中你说:
但如果我们传递一个没有协议说明符的通用id变量,那么这会完全被破坏
好吧,除了传递id
的情况之外,编译器会警告并告诉您正在尝试传递一些不符合该协议的内容。这就是为什么你通常应该尝试比id
更精确地输入内容。
如果你有一个如此定义的方法:
- (void)aMethod:(id<aProtocol>)aVar
然后aVar
可以是SomeSubclass
类型,其定义如下:
@interface SomeSubclass : NSObject <aProtocol>
然后你可以像这样使用aMethod
:
SomeSubclass *obj = [SomeSubclass new];
[other aMethod:obj];
答案 1 :(得分:3)
只需少用id
,并尽可能使用正确的类型声明变量和参数。也就是说:不要通过id
。如果您正在实现集合类(例如),那么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
协议非常非常有用。通常,比子类更好/更清洁。
答案 2 :(得分:0)
我(最后)发现使用Objective-C ++是可行的方法。假设我希望能够传递NSString
或NSNumber
(而不是使用过多的通用id
,而不是使用无效传递id
值的协议): ,我可以创建一个具有两个不同构造函数的C ++类,每个ObjC类一个,因此传递id
值不再能够完成(几乎直接)。例如,让我们来看看
class NSStringOrNSNumber{
public:
NSStringOrNSNumber(NSString *);
NSStringOrNSNumber(NSNumber *);
};
最大的优点是采用NSStringOrNSNumber
参数的方法/函数可以直接获取NSString / NSNumber值,因为构造函数充当隐式强制转换。换句话说,如果我们有
void aFunction(NSStringOrNSNumber param);
以下电话完全有效:
aFunction(@"Hello!");
aFunction(@25);
唯一的(小)缺点是,如果我们想要获取传递给构造函数的值,我们需要该类来实现一个函数。
使用C ++类构造函数获取类似id<NSCoding>
的内容更好直接使用id<NSCoding>
:事实上,如果我们执行以下操作
@class classOne, classTwo;
class NSCodingClass{
private:
NSCodingClass(classOne *);
NSCodingClass(classTwo *);
public:
NSCodingClass(id<NSCoding>);
}
我们将无法将通用id
作为参数传递(因为它不明确:编译器无法知道在两个私有构造函数中调用哪个构造函数)