关于Objective-C类别和扩展的详细信息 [英] Minutia on Objective-C Categories and Extensions

查看:67
本文介绍了关于Objective-C类别和扩展的详细信息的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在尝试弄清楚为什么在私有类别中声明的readwrite属性没有生成setter时,我学到了一些新知识.这是因为我的类别被命名为:

I learned something new while trying to figure out why my readwrite property declared in a private Category wasn't generating a setter. It was because my Category was named:

// .m
@interface MyClass (private)
@property (readwrite, copy) NSArray* myProperty;
@end

将其更改为:

// .m
@interface MyClass ()
@property (readwrite, copy) NSArray* myProperty;
@end

,我的二传手已经合成.我现在知道,类扩展不仅是匿名类别的另一个名称.保留未命名的类别会使它变成另一种野兽:现在可以实施编译时方法实施并允许您添加ivars.现在,我理解了这些基础的基本原理:类别通常用于在运行时向任何类添加方法,而类别扩展通常用于强制执行私有API实现并添加ivars.我接受这个.

and my setter is synthesized. I now know that Class Extension is not just another name for an anonymous Category. Leaving a Category unnamed causes it to morph into a different beast: one that now gives compile-time method implementation enforcement and allows you to add ivars. I now understand the general philosophies underlying each of these: Categories are generally used to add methods to any class at runtime, and Class Extensions are generally used to enforce private API implementation and add ivars. I accept this.

但是,有些琐事使我感到困惑.首先,在一个较高的层次上:为什么要这样区分?这些概念似乎类似,无法决定它们是相同还是不同.如果它们是相同的,我希望使用没有名称的类别和使用命名的类别(它们不是)可以实现完全相同的事情.如果它们不同,那么它们之间在语法上的差距会更大.似乎很奇怪地说:哦,要实现一个类扩展,只需编写一个类别,但省去名称即可.它发生了神奇的变化."

But there are trifles that confuse me. First, at a hight level: Why differentiate like this? These concepts seem like similar ideas that can't decide if they are the same, or different concepts. If they are the same, I would expect the exact same things to be possible using a Category with no name as is with a named Category (which they are not). If they are different, (which they are) I would expect a greater syntactical disparity between the two. It seems odd to say, "Oh, by the way, to implement a Class Extension, just write a Category, but leave out the name. It magically changes."

第二,关于编译时间强制执行的主题:如果您不能在命名的Category中添加属性,为什么要这样做使编译器相信您已经做到了?为了澄清,我将用我的例子来说明.我可以在头文件中声明一个只读属性:

Second, on the topic of compile time enforcement: If you can't add properties in a named Category, why does doing so convince the compiler that you did just that? To clarify, I'll illustrate with my example. I can declare a readonly property in the header file:

// .h
@interface MyClass : NSObject
@property (readonly, copy) NSString* myString;
@end

现在,我想转到实现文件,并为自己授予对该属性的私有读写访问权限.如果我做对了:

Now, I want to head over to the implementation file and give myself private readwrite access to the property. If I do it correctly:

// .m
@interface MyClass ()
@property (readwrite, copy) NSString* myString;
@end

当我不进行合成时,我会收到一条警告,当我进行合成时,我可以设置该属性,并且所有内容都是桃红色的.但是,令人沮丧的是,如果我碰巧对类别和类扩展之间的区别误入歧途,我尝试:

I get a warning when I don't synthesize, and when I do, I can set the property and everything is peachy. But, frustratingly, if I happen to be slightly misguided about the difference between Category and Class Extension and I try:

// .m
@interface MyClass (private)
@property (readwrite, copy) NSString* myString;
@end

编译器完全认为该属性是可读写的.我没有警告,甚至在设置myString我没有在Category中声明readwrite属性时,都没有很好的编译错误无法设置对象-只读属性或找不到设置器".我只是在运行时收到不响应选择器"异常.如果(命名的)类别不支持添加ivars和属性,那么要求编译器遵循相同的规则是否过多?我是否错过了一些宏伟的设计哲学?

The compiler is completely pacified into thinking that the property is readwrite. I get no warning, and not even the nice compile error "Object cannot be set - either readonly property or no setter found" upon setting myString that I would had I not declared the readwrite property in the Category. I just get the "Does not respond to selector" exception at runtime. If adding ivars and properties is not supported by (named) Categories, is it too much to ask that the compiler play by the same rules? Am I missing some grand design philosophy?

推荐答案

在Objective-C 2.0中添加了类扩展以解决两个特定问题:

Class extensions were added in Objective-C 2.0 to solve two specific problems:

  1. 允许对象具有由编译器检查的专用"接口.
  2. 允许公开可读,私有可写的属性.

私人界面

在Objective-C 2.0之前,如果开发人员希望在Objective-C中拥有一组方法,则他们通常在类的实现文件中声明"Private"类别:

Private Interface

Before Objective-C 2.0, if a developer wanted to have a set of methods in Objective-C, they often declared a "Private" category in the class's implementation file:

@interface MyClass (Private)
- (id)awesomePrivateMethod;
@end

但是,这些私有方法经常混入类的@implementation块(对于Private类别,不是单独的@implementation块).那么为何不?这些并不是该类的真正扩展.它们只是弥补了Objective-C类别中缺乏公共/私人限制的原因.

However, these private methods were often mixed into the class's @implementation block (not a separate @implementation block for the Private category). And why not? These aren't really extensions to the class; they just make up for the lack of public/private restrictions in Objective-C categories.

问题在于,Objective-C编译器假定在类别中声明的方法将在其他地方实现,因此他们不检查以确保实现了这些方法.因此,开发人员可以声明 awesomePrivateMethod,但无法实现它,并且编译器也不会警告他们该问题.那就是您注意到的问题:在类别中,您可以声明属性(或方法),但是如果您从未真正实现它,则不会收到警告-这是因为编译器希望它在某处"实现(很可能是,位于与此独立的另一个编译单元中.)

The problem is that Objective-C compilers assume that methods declared in a category will be implemented elsewhere, so they don't check to make sure the methods are implemented. Thus, a developer could declare awesomePrivateMethod but fail to implement it, and the compiler wouldn't warn them of the problem. That is the problem you noticed: in a category, you can declare a property (or a method) but fail to get a warning if you never actually implement it -- that's because the compiler expects it to be implemented "somewhere" (most likely, in another compilation unit independent of this one).

输入课程扩展名.假定在类扩展中声明的方法是在主@implementation块中实现的;如果不是,则编译器将发出警告.

Enter class extensions. Methods declared in a class extension are assumed to be implemented in the main @implementation block; if they're not, the compiler will issue a warning.

实现不可变数据结构通常是有益的-也就是说,外部代码不能使用setter来修改对象的状态.但是,为内部使用 使用一个可写属性仍然会很不错.类扩展允许:在公共接口中,开发人员可以将属性声明为只读,然后在类扩展中将其声明为可写.对于外部代码,该属性将是只读的,但可以在内部使用setter.

It is often beneficial to implement an immutable data structure -- that is, one in which outside code can't use a setter to modify the object's state. However, it can still be nice to have a writable property for internal use. Class extensions allow that: in the public interface, a developer can declare a property to be read-only, but then declare it to be writable in the class extension. To outside code, the property will be read-only, but a setter can be used internally.

类别无法添加实例变量.设置员通常需要某种形式的后备存储.决定允许类别声明可能需要后备商店的财产是A Bad Thing™.因此,类别不能声明可写属性.

Categories cannot add instance variables. A setter often requires some sort of backing storage. It was decided that allowing a category to declare a property that likely required a backing store was A Bad Thing™. Hence, a category cannot declare a writable property.

混淆之处在于类扩展只是一个未命名的类别".语法相似,并且暗含了这种想法.我想象它之所以被选中是因为它是Objective-C程序员熟悉的,并且在某些方面,类扩展就像类别.它们 相似之处在于,这两个功能都允许您将方法(和属性)添加到现有的类中,但是它们具有不同的用途,因此允许不同的行为.

The confusion lies in the idea that a class extension is just an "unnamed category". The syntax is similar and implies this idea; I imagine it was just chosen because it was familiar to Objective-C programmers and, in some ways, class extensions are like categories. They are alike in that both features allow you to add methods (and properties) to an existing class, but they serve different purposes and thus allow different behaviors.

这篇关于关于Objective-C类别和扩展的详细信息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆