我在尝试弄清楚为什么我在私有类别中声明的readwrite属性没有生成setter时学到了一些东西.这是因为我的类别被命名为:
// .m
@interface MyClass (private)
@property (readwrite, copy) NSArray* myProperty;
@end
Run Code Online (Sandbox Code Playgroud)
将其更改为:
// .m
@interface MyClass ()
@property (readwrite, copy) NSArray* myProperty;
@end
Run Code Online (Sandbox Code Playgroud)
我的二传手是合成的.我现在知道,类扩展不仅仅是匿名类别的另一个名称.保留一个未命名的类别会导致它变成一个不同的野兽:一个现在提供编译时方法实施强制并允许你添加ivars.我现在理解每个基础的一般原理:类别通常用于在运行时向任何类添加方法,而类扩展通常用于强制私有API实现并添加ivars.我接受这个.
但有些小事让我感到困惑.首先,在高层:为什么要这样区别?这些概念看起来像是类似的想法,无法决定它们是相同的还是不同的概念.如果它们是相同的,我希望使用没有名称的类别和具有命名类别(它们不是)的类别可以完全相同.如果它们不同,(它们是)我会期望两者之间存在更大的语法差异.看起来很奇怪,"哦,顺便说一句,要实现一个类扩展,只需写一个类别,但忽略名称.它神奇地改变了."
第二,关于编译时强制的主题:如果你不能在命名类别中添加属性,为什么这样做会说服编译器你就是这么做的?为了澄清,我将用我的例子来说明.我可以在头文件中声明一个readonly属性:
// .h
@interface MyClass : NSObject
@property (readonly, copy) NSString* myString;
@end
Run Code Online (Sandbox Code Playgroud)
现在,我想转到实现文件并给自己私有的readwrite访问属性.如果我做得正确:
// .m
@interface MyClass ()
@property (readwrite, copy) NSString* myString;
@end
Run Code Online (Sandbox Code Playgroud)
当我不合成时,我会收到警告,当我这样做时,我可以设置属性,一切都很好.但是,令人沮丧的是,如果我碰巧有点误导了类别和类扩展之间的区别,我尝试:
// .m
@interface MyClass (private)
@property (readwrite, copy) NSString* myString;
@end
Run Code Online (Sandbox Code Playgroud)
编译器完全安心认为属性是readwrite.我没有得到任何警告,甚至没有好的编译错误"对象无法设置 - 无论是readonly属性还是没有找到setter",在设置myString时我都没有在类别中声明readwrite属性.我只是在运行时获得"不响应选择器"异常.如果(命名)类别不支持添加ivars和属性,那么要求编译器按相同规则播放是否太过分了?我错过了一些宏伟的设计理念吗?
mip*_*adi 53
在Objective-C 2.0中添加了类扩展来解决两个特定问题:
在Objective-C 2.0之前,如果开发人员希望在Objective-C中拥有一组方法,他们通常会在类的实现文件中声明一个"私有"类别:
@interface MyClass (Private)
- (id)awesomePrivateMethod;
@end
Run Code Online (Sandbox Code Playgroud)
但是,这些私有方法通常混合到类的@implementation块中(不是@implementation该Private类别的单独块).那么为何不?这些并不是课堂的真正延伸; 它们只是弥补了Objective-C类别中缺乏公共/私人限制.
问题是Objective-C编译器假设在一个类别中声明的方法将在别处实现,因此他们不会检查以确保方法已实现.因此,开发人员可以声明 awesomePrivateMethod但无法实现它,并且编译器不会警告他们这个问题.这就是你注意到的问题:在一个类别中,你可以声明一个属性(或一个方法)但是如果你从未实际实现过它就不会得到警告 - 这是因为编译器希望它"在某个地方"实现(很有可能) ,在另一个独立于此的编译单元中).
输入课程扩展.假定在类扩展中声明的方法在主@implementation块中实现; 如果不是,编译器将发出警告.
实现不可变数据结构通常是有益的 - 也就是说,外部代码不能使用setter来修改对象的状态.但是,拥有可写属性供内部使用仍然很好.类扩展允许:在公共接口中,开发人员可以将属性声明为只读,但随后在类扩展中声明它是可写的.对于外部代码,该属性将是只读的,但可以在内部使用setter.
类别无法添加实例变量.设定者通常需要某种后备存储.决定允许类别声明可能需要后备存储的属性是A Bad Thing™.因此,类别不能声明可写属性.
混淆在于类扩展只是一个"未命名的类别".语法类似,暗示了这个想法; 我想它只是被选中,因为它对Objective-C程序员很熟悉,在某些方面,类扩展就像是类别.它们的相似之处在于,这两个功能都允许您向现有类添加方法(和属性),但它们可以用于不同的目的,从而允许不同的行为.
你对句法的相似性感到困惑.一类扩展是不是只是一个无名类.类扩展是一种将接口的一部分设为私有和部分公共的方法 - 两者都被视为类的接口声明的一部分.作为类接口的一部分,必须将扩展定义为类的一部分.
另一方面,类别是一种在运行时向现有类添加方法的方法.例如,这可能是在仅在星期四加载的单独捆绑中.
对于Objective-C的大多数历史记录,在加载类别时,无法在运行时向类添加实例变量.这在最近的新运行时已经解决了,但该语言仍然显示其脆弱的基类的伤疤.其中之一是该语言不支持添加实例变量的类别.你必须自己写出getter和setter,老派风格.
类别中的实例变量也有些棘手.因为在创建实例时它们不一定存在并且初始化器可能对它们一无所知,所以初始化它们是普通实例变量不存在的问题.
| 归档时间: |
|
| 查看次数: |
7703 次 |
| 最近记录: |