Ios 符合协议的id vs使用协议限定id

Ios 符合协议的id vs使用协议限定id,ios,objective-c,cocoa,methods,protocols,Ios,Objective C,Cocoa,Methods,Protocols,我正在浏览苹果公司提供的文件 我试图理解下面的一段,但到目前为止,我还不能理解 @protocol XYZPieChartViewDataSource - (NSUInteger)numberOfSegments; - (CGFloat)sizeOfSegmentAtIndex:(NSUInteger)segmentIndex; @optional - (NSString *)titleForSegmentAtIndex:(NSUInteger)segmentIndex; @required -

我正在浏览苹果公司提供的文件

我试图理解下面的一段,但到目前为止,我还不能理解

@protocol XYZPieChartViewDataSource
- (NSUInteger)numberOfSegments;
- (CGFloat)sizeOfSegmentAtIndex:(NSUInteger)segmentIndex;
@optional
- (NSString *)titleForSegmentAtIndex:(NSUInteger)segmentIndex;
@required
- (UIColor *)colorForSegmentAtIndex:(NSUInteger)segmentIndex;
@end

@interface XYZPieCharView : UIView
@property (weak) id <XYZPieChartViewDataSource> dataSource;
// some additional stuff
@end
@协议XYZPieChartViewDataSource
-(整数)段数;
-(CGFloat)sizeOfSegmentAtIndex:(NSUInteger)segmentIndex;
@可选的
-(NSString*)标题为分段索引:(NSInteger)分段索引;
@必需的
-(UIColor*)colorForSegmentAtIndex:(NSUPINTEGER)段索引;
@结束
@接口XYZPieCharView:UIView
@属性(弱)id数据源;
//一些额外的东西
@结束
如果尝试调用id上的respondsToSelector:方法 按照上面定义的协议,您将得到 编译器错误,没有已知的实例方法。一旦你 使用协议限定id,所有静态类型检查返回; 如果您试图调用任何未定义的方法,将出现错误 在指定的协议中。避免编译器错误的一种方法是 设置自定义协议以采用NSObject协议

我对“遵守协议”和“用协议限定某个对象”之间的区别感到困惑。如果我们发送一个符合协议的id,
respondsToSelector
消息,为什么编译器会产生错误

符合议定书

这是当类的定义指定类实现(或符合)协议时。这是编译器用来验证类是否确实实现了所需方法的信息

@interface MyClass < MyProtocol >
如果我们发送一个符合协议的id——respondsToSelector消息,为什么编译器会产生错误呢

是的,这是一件很奇怪的事情。现在,在ARC下,如果声明为
id
的对象被发送到
响应选择器:
方法、
方法或任何其他熟悉的基本NSObject实例方法,编译器将抛出错误!试试看

这似乎很奇怪,因为
id
应该允许向其发送任何(已知)消息。然而,
id
被认为是一种完全特定的类型;编译器将只允许属于XYZPieChartViewDataSource协议一部分的消息。一个现代的解决方案是:不要使用

id <XYZPieChartViewDataSource>
id
相反,使用

NSObject<XYZPieChartViewDataSource>*
NSObject*
这将使NSObject的所有优点(就编译器而言)都包含在这个对象中,包括响应
respondsToSelector:
class
之类的功能

这是我在书中插入的一段,讨论了这个问题:

奇怪的是,编译器对待类型化为
id
的对象与对待类型化为
id
的对象非常不同,只允许将SomeProtocol中定义的方法发送到该对象。例如,假设MyClass是用类型为
id
delegate
属性定义的。如果
obj
是一个MyClass实例,那么就不能说
[obj.delegate class]
;编译器抱怨
class
不是已知的实例方法!这是因为
obj.delegate
被键入为
id
,因此它唯一已知的实例方法是
doSomething:
,这是MyProtocol定义的方法。我们可以通过将
obj.delegate
转换为
id
来解决这个问题;当MyClass的定义由我们决定时,一个更优雅的解决方案是将
delegate
属性声明为
NSObject*
,而不是
id


由于到目前为止我已经实现了许多协议数据源,我无法确切地说出这句话想要解释什么。然而,我使用的协议没有问题。以我个人的拙见,我认为在实际情况下实现它,看看它在需要协议时是如何工作和感觉的是最快的学习方法。让协议扩展
意味着在运行时得到的对象可能是
NSProxy
,指定协议比指定类类型(通常被认为是良好实践)更具限制性。那么,使用协议将类指定为
NSObject
有什么好处呢?个人偏好或我遗漏了什么?@Wain让协议指定它符合
协议也可以。我从未使用过Nproxy,因此在实践中,我从未出现过这种区别。另外,我直接从苹果公司的格雷格·帕克那里得到了
NSObject*
的想法,我倾向于按照他说的去做:)我对我的答案做了一些修改。当然这一点也不“奇怪”,尽管乍一看语法可能有些混乱。将变量声明为类类型就是断言引用对象提供了类的方法等,而不断言其他任何内容。将变量声明为协议类型应该是与此直接并行的(Java中也是如此)。这里没什么奇怪的。唯一的问题是语法。由于协议出现在语法中一个类后的括号中,因此使用什么来表示“仅协议”
id
是有意义的,因为“任何内容和协议”是一个冗余规范。@CRD嗯,不同的人对不同的事情感到惊讶。我所能告诉你的是,当我第一次对键入为
id
的引用说
respondsToSelector:
时,编译器向我吐了一口唾沫,我非常惊讶@matt-我们确实是:-)但是我建议您面临的问题更多地与默认情况下不包括NSObject协议的协议有关,而不是与“协议类型变量”的语法有关。很少有Obj-C程序员会错过一个接口上的NSObject基础(并且当他们这样做时会对错误感到惊讶),但是有多少人会错过一个协议上的NSObject基础呢?Java没有相同的问题,因为只有一个根对象,所以无论什么都必须实现它的方法。O
NSObject<XYZPieChartViewDataSource>*