2022-05-04 iOS分类

一、官方文档
先让我们来段自己的渣渣翻译,如有翻译不妥当的地方,咱们以后再慢慢迭代。

Category

You use categories to define additional methods of an existing class—even one whose source code is unavailable to you—without subclassing. You typically use a category to add methods to an existing class, such as one defined in the Cocoa frameworks. The added methods are inherited by subclasses and are indistinguishable at runtime from the original methods of the class. You can also use categories of your own classes to:
(可以用分类去给已经存在的,甚至是你拿不到源码没有子类化的类,定义额外的方法。通常去用分类去给已经存在的类添加额外的方法,例如coco framework的类。添加的方法被子类继承,并且在运行时难以和原有类的方法进行区分。你也可以用你自定义的类去)
例如这样式儿的:

#import "SystemClass+CategoryName.h"

@implemetation SystemClass (CategoryName)
// method definitions

Prerequisite Articles

Class definition 类定义

A class definition is the specification of a class of objects through the use of certain files and syntax. A class definition minimally consists of two parts: a public interface, and a private implementation. You typically split the interface and implementation into two separate files—the header file and the implementation file. By separating the public and private parts of your code, you retain the class interface as an independent entity.
一个类定义是用具体的文件和语法把系列事物归类的说明书。一个类定义至少有两部分组成:一个公有的接口,一个私有的实现。你通常把声明和实现分开在两个分离的文件,头文件和实现文件。通过把你代码分成私有和共有部分,你把泪定义当作一个独立的实体。

You usually name the interface and implementation files after the class. Because it’s included in other source files, the name of the interface file usually has the .h extension typical of header files. The name of the implementation file has a .m extension, indicating that it contains Objective-C source code. For example, the MyClass class would be declared in MyClass.h and defined in MyClass.m
你通常在类后命名定义和实现文件。因为它可能包含其他源文件。头文件通常用.h做后缀。实现文件通常用.m做后缀,表明它包含oc源码。例MyClass这个类通常用MyClass.h来声明,用MyClass.m来定义(实现)。

Interface

接口
In the interface, you do several things:
接口文件内,做这几件事

  • You name the class and its superclass.
    命名类和父类
    You may also specify any protocols that your class conforms to
    也可把要实现的协议方法列出来
    (see Protocol).

  • You specify the class’s instance variables.
    列出类的实例变量

  • You specify the methods and declared properties (see Declared property) that are available for the class.
    列出这个类可用的方法和声明变量
    In the interface file, you first import any required frameworks. (This will often be just Cocoa/Cocoa.h.) You start the declaration of the class interface itself with the compiler directive @interface and finish it with the directive @end.
    在接口文件,你先要import任何你需要的framework。你开始声明类接口本身,在编译指令@interface和,结束编译指令的标志@end之间。

#import <Cocoa/Cocoa.h>
@interface MyClass : SuperClass {
        int integerInstanceVariable;
}
+ (void)aClassMethod;
- (void)anInstanceMethod;

@end

Implementation
实现
Whereas you declare a class’s methods in the interface, you define those methods (that is, write the code for implementing them) in the implementation.
鉴于你在接口文件中声明方法,得在实现文件中定义方法。
In the interface file, you first import any required header files. (Minimally this will be your class’s header file.) You start the implementation of the class with the compiler directive @implementation and finish it with the directive @end.
在接口文件中,在接口文件中,你首先得import需要的头文件(至少包含你类的头文件)在编译标志@implementagtion和编译标志@end内实现文件。

@implementation MyClass

+ (void)aClassMethod {
    printf("This is a class method\n");
}

- (void)anInstanceMethod {
        printf("This is an instance method\n");
        printf("The value of integerInstanceVariable is %d\n", integerInstanceVariable);
}
  • Distribute the implementation of your own classes into separate source files—for example, you could group the methods of a large class into several categories and put each category in a different file.
    把你自己类源文件的代码分开执行,例如你可以把一个大文件的方法,分组成一个类目,然后把每个类目放到不同的文件。

  • Declare private methods.
    声明私有方法

You add methods to a class by declaring them in an interface file under a category name and defining them in an implementation file under the same name. The category name indicates that the methods are an extension to a class declared elsewhere, not a new class.
你可以在一个类目名称下,给一个类通过一个interfacea文件声明添加方法。并在同名类目文件实现。类目名称暗含着,这些方法可以扩展一个类,任何地方声明,并不需要一个新的类。

Declaration

声明
The declaration of a category interface looks very much like a class interface declaration—except that the category name is listed within parentheses after the class name and the superclass isn’t mentioned. A category must import the interface file for the class it extends:
一个类目接口的声明看上去挺像一个接口的声明。除了类目的名字,跟在类名后,列在原括号内。并且不提及父类。一个类目必须import它扩展类的接口

#import "SystemClass.h"

@interface SystemClass (CategoryName)
// method declarations
@end

A common naming convention is that the base file name of the category is the name of the class the category extends followed by “+” followed by the name of the category. This category might be declared in a file named SystemClass+CategoryName.h.
一个常见的命名惯例是,类目的基础文件名称紧跟在,类目所扩展的类名+号之后。例如系统类+类目名.h

If you use a category to declare private methods of one of your own classes, you can put the declaration in the implementation file before the @implementation block:
如果你用类目声明你自己类的私有方法,可以把声明放在@implementation块之前

#import "MyClass.h"

@interface MyClass (PrivateMethods)
// method declarations
@end

@implamentations MyClass
// method definitions
@end

Implementation
If you use a category to declare private methods of one of your own classes, you can put the implementation in your class’s @implementation block. If you use a category to extend a class to which you don’t have source code, or to distribute the implementation of your own class, you put the implementation in a file named <ClassName>+CategoryName.m The implementation, as usual, imports its own interface. A category implementation might therefore look like this:
你可以用类目来声明自定义类的私有方法。你可以把实现放在类的@implementation块内。你可以用类目去扩展你不知道源码的类。或者去分开实现自定义类,你可以把实现放在一个文件名为<ClassName>+CategoryName.m的文件。实现通常import自己的接口。一个类目的实现可以像这样

#import "SystemClass+CategoryName.h"

@implemetation SystemClass (CatregoryName)
// method definitions
@end

Definitive Discussion

最权威的讨论

Categories Add Methods to Existing Classes

用类目给一个已经存在的类添加方法
Objects should have clearly-defined tasks, such as modeling specific information, displaying visual content or controlling the flow of information. As you’ve already seen, a class interface defines the ways in which others are expected to interact with an object to help it accomplish those tasks.
对象需要有清晰定义的任务,例如作为一个具体信息的模型, 展示可见内容或者控制休息流。正如你已经见过的,一个类接口定义了一些可以和其他类期望交互的方法,来帮助完成任务。

Sometimes, you may find that you wish to extend an existing class by adding behavior that is useful only in certain situations. As an example, you might find that your application often needs to display a string of characters in a visual interface. Rather than creating some string-drawing object to use every time you need to display a string, it would make more sense if it was possible to give the NSString class itself the ability to draw its own characters on screen.
有时候,你可能找到你希望去扩展的一个已经存在的,只在特定场景下有用的一个类。例如你可能发现你的应用经常需要在一个可见的界面,展示一个字符串。而不是时时刻刻要创建一些可以绘制的字符串对象。更多的使用场景是需要能给NSString 类本身,去绘制字符在屏幕上的能力。

In situations like this, it doesn’t always make sense to add the utility behavior to the original, primary class interface. Drawing abilities are unlikely to be needed the majority of times any string object is used in an application, for example, and in the case of NSString, you can’t modify the original interface or implementation because it’s a framework class.
在类似的场景下,不用把这种能力加在原有的首要的类接口中。绘制能力在应用里,看上去不是一个大多场景任何字符串对象需要的能力。并且拿NSString举例,你不能编辑原有类定义和实现,因为是 framework的类。

Furthermore, it might not make sense to subclass the existing class, because you may want your drawing behavior available not only to the original NSString class but also any subclasses of that class, like NSMutableString. And, although NSString is available on both OS X and iOS, the drawing code would need to be different for each platform, so you’d need to use a different subclass on each platform.
此外,继承原来的类没啥用。因为你可能想绘制的能力不在原有的类中,也不用在像NSMutableString的子类中。并且NSString在OS X和iOS系统中,绘制代码需要区分平台。你还需要区分不同的子类在不同的实现平台。

Instead, Objective-C allows you to add your own methods to existing classes through categories and class extensions.
代替这个的是,oc允许你通过类目和延展给已经存在的类添加方法。

Categories Add Methods to Existing Classes

If you need to add a method to an existing class, perhaps to add functionality to make it easier to do something in your own application, the easiest way is to use a category.
如果你需要给已经存在的类,可能加些方法使你的应用更容易做一些事。最简单的方式就是用类目。

The syntax to declare a category uses the @interface keyword, just like a standard Objective-C class description, but does not indicate any inheritance from a subclass. Instead, it specifies the name of the category in parentheses, like this:
声明category用@interface关键字,就像标准的oc类描述,但是不要指明任何子类继承关系。相反,它在圆括号中规定类目的名字。

@interface ClassName (CategoryName)
 
@end

A category can be declared for any class, even if you don’t have the original implementation source code (such as for standard Cocoa or Cocoa Touch classes). Any methods that you declare in a category will be available to all instances of the original class, as well as any subclasses of the original class. At runtime, there’s no difference between a method added by a category and one that is implemented by the original class.
一个类目可以为任何类声明,即使你拿不到实现的源码(例如系统的cocoa 或者coco touch框架的类)任何你声明的类目方法,将在所有的原类的实例变量中可用。就和任何原类的子类的方法一模一样。在运行时,实现的原类的方法和类目中的方法没有任何区别。

Consider the XYZPerson class from the previous chapters, which has properties for a person’s first and last name. If you’re writing a record-keeping application, you may find that you frequently need to display a list of people by last name, like this:
思考前几章节提到的XYZPerson类,它有person first and last name的属性。如果你一个记录保持者的应用,你可能经常需要展示一个像这样一个last name的人的列表。

Appleseed, John
Doe, Jane
Smith, Bob
Warwick, Kate

Rather than having to write code to generate a suitable lastName, firstName string each time you wanted to display it, you could add a category to the XYZPerson class, like this:
出了当你想展示它的时候,每次都写代码生成一个合适的lastName firstName 字符串,你可以添加一个XYZPerson类目,像这样

#import "XYZPerson.h"

@interface XYZPerson (XYZPersonNameDisplayAdditions)
- (NSString *)lastNameFirstNameString;
@end

In this example, the XYZPersonNameDisplayAdditions category declares one additional method to return the necessary string.
在这个例子中, XYZPersonNameDisplayAdditions 类目声明了一个额外的返回需要字符串的方法

A category is usually declared in a separate header file and implemented in a separate source code file. In the case of XYZPerson, you might declare the category in a header file called XYZPerson+XYZPersonNameDisplayAdditions.h.
一个类目通常被声明在一个分开的头文件和实现文件中。例如XYZPerson,你可以声明在叫XYZPerson+XYZPersonNameDisplayAdditions.h的头文件中。

Even though any methods added by a category are available to all instances of the class and its subclasses, you’ll need to import the category header file in any source code file where you wish to use the additional methods, otherwise you’ll run into compiler warnings and errors.
尽管任何被类目添加的方法都可以在任何类和类的子类的实例变量中使用。你也需要在任何你需要使用额外方法的源文件 import类目头文件,否则你运行程序会在编译时期有警告和错误。

The category implementation might look like this:
类目的实现像这样

#import "XYZPerson+XYZPersonNameDisplayAdditions.h"

@implementation XYZPerson (XYZPersonNameDisplayAdditions)
- (NSString *)lastNameFirstNameString {
    return [NSString stringWithFormat:@"%@, %@", self.lastName, self.firstName];
}

Once you’ve declared a category and implemented the methods, you can use those methods from any instance of the class, as if they were part of the original class interface:
一旦你声明和实现了类目方法,你可以从任何这个类的实例变量的使用这些方法,就像是原始类接口声明的方法。

#import "XYZPerson+XYZPersonNameDisplayAdditions.h"
@implementation SomeObject
- (void)someMethod {
    XYZPerson *person = [[XYZPerson alloc] initWithFirstName:@"John"
                                                    lastName:@"Doe"];
    XYZShoutingPerson *shoutingPerson =
                        [[XYZShoutingPerson alloc] initWithFirstName:@"Monica"
                                                            lastName:@"Robinson"];
 
    NSLog(@"The two people are %@ and %@",
         [person lastNameFirstNameString], [shoutingPerson lastNameFirstNameString]);
}
@end

As well as just adding methods to existing classes, you can also use categories to split the implementation of a complex class across multiple source code files. You might, for example, put the drawing code for a custom user interface element in a separate file to the rest of the implementation if the geometrical calculations, colors, and gradients, etc, are particularly complicated. Alternatively, you could provide different implementations for the category methods, depending on whether you were writing an app for OS X or iOS.
正如给已经存在的类添加方法,你也可以分成多个源代码文件,来用类目来分离复杂类的实现。例如,你可以把绘制用户界面元素的代码和其余实现地理位置计算、颜色、和渐变等代码分离开。或者你可以提供不用的类目实现方法,根据你写的应用的平台

Categories can be used to declare either instance methods or class methods but are not usually suitable for declaring additional properties. It’s valid syntax to include a property declaration in a category interface, but it’s not possible to declare an additional instance variable in a category. This means the compiler won’t synthesize any instance variable, nor will it synthesize any property accessor methods. You can write your own accessor methods in the category implementation, but you won’t be able to keep track of a value for that property unless it’s already stored by the original class.
类目可以用来声明类方法或者实例方法。但是通常不适合声明额外的属性。在类目接口定义中,包含变量声明的语法是有效的,但是在类名的实例变量中是不能够深入声明一个额外的变量的。这意味着,编译器不会合成任何实力变量,也不会合成任何属性方法。你可以在类目的实现中写你自己的访问方法,但是你不能记录属性值,除非你已经在原类中保存过。

The only way to add a traditional property—backed by a new instance variable—to an existing class is to use a class extension, as described in Class Extensions Extend the Internal Implementation.
唯一的,给已有类添加额外的属性的方法是class 延展

Note: Cocoa and Cocoa Touch include a variety of categories for some of the primary framework classes.

The string-drawing functionality mentioned in the introduction to this chapter is in fact already provided for NSString by the NSStringDrawing category for OS X, which includes the drawAtPoint:withAttributes: and drawInRect:withAttributes: methods. For iOS, the UIStringDrawing category includes methods such as drawAtPoint:withFont:and drawInRect:withFont:.

Avoid Category Method Name Clashes

避免类目方法命名冲突

Because the methods declared in a category are added to an existing class, you need to be very careful about method names.
因为类目声明的方法被加到已经存在的类中,你需要特别注意方法的命名。

If the name of a method declared in a category is the same as a method in the original class, or a method in another category on the same class (or even a superclass), the behavior is undefined as to which method implementation is used at runtime. This is less likely to be an issue if you’re using categories with your own classes, but can cause problems when using categories to add methods to standard Cocoa or Cocoa Touch classes.
如果类目声明的方法名字和原有的类相同,或者一个方法在同一个类的另一个类目中,运行时用哪个类目的方法是不确定的。用你自定义的类看上去没那么大的问题,但是用 Cocoa or Cocoa Touch 的类目添加方法,会有很多问题。

An application that works with a remote web service, for example, might need an easy way to encode a string of characters using Base64 encoding. It would make sense to define a category on NSString to add an instance method to return a Base64-encoded version of a string, so you might add a convenience method called base64EncodedString.
一个应用和远端的服务一起工作,例如,需要简单的把字符串通过 Base64编码方式编码。可以把NSString类添加类目,类目中加返回 Base64-编码的实例方法。你可以添加一个叫base64EncodedString的方法。

A problem arises if you link to another framework that also happens to define its own category on NSString, including its own method called base64EncodedString. At runtime, only one of the method implementations will “win” and be added to NSString, but which one is undefined.
一个问题发生在,你链接另一个库,那个库也定义了它自己的NSString类目,包含了同名叫base64EncodedString的方法。在运行时只有一个方法执行被加载进NSString`类,但是具体哪个方法不确定。

Another problem can arise if you add convenience methods to Cocoa or Cocoa Touch classes that are then added to the original classes in later releases. The NSSortDescriptorclass, for example, which describes how a collection of objects should be ordered, has always had an initWithKey:ascending: initialization method, but didn’t offer a corresponding class factory method under early OS X and iOS versions.
另一个可能发生的问题是,如果你在Cocoa or Cocoa Touch classes 的类目中添加了方法,之后版本的又在原有的类中添加了方法。比如NSSortDescriptor这个类,描述了集合对象如何被排序,永远有个 initWithKey:ascending:初始化方法,但是不提供相关的工厂方法在早期的
OS X and iOS版本。

By convention, the class factory method should be called sortDescriptorWithKey:ascending:, so you might have chosen to add a category on NSSortDescriptor to provide this method for convenience. This would have worked as you’d expect under older versions of OS X and iOS, but with the release of Mac OS X version 10.6 and iOS 4.0, a sortDescriptorWithKey:ascending: method was added to the original NSSortDescriptor class, meaning you’d now end up with a naming clash when your application was run on these or later platforms.
按照惯例,类工厂方法应该叫sortDescriptorWithKey:ascending:,所以你可能选择添加叫NSSortDescriptor的类目来提供这样的方法。这会按照你期望的在早期版本的 OS X and iOS系统中工作。但是对于Mac OS X version 10.6 and iOS 4.0版本的系统,sortDescriptorWithKey:ascending:被加到原类中,意外着你的应用运行在这个系统版本和以后的系统版本中会有命名冲突。

In order to avoid undefined behavior, it’s best practice to add a prefix to method names in categories on framework classes, just like you should add a prefix to the names of your own classes. You might choose to use the same three letters you use for your class prefixes, but lowercase to follow the usual convention for method names, then an underscore, before the rest of the method name. For the NSSortDescriptor example, your own category might look like this:
为了避免这中不可控的行为,最好把 framework类目的方法名加前缀。就像你给自定义的类添加前缀。你可能选择用和你的类前缀同样的三个字母,小写,下划线,在剩余的方法名字前。拿NSSortDescriptor 举例,你自己的类目看上去像这样

This means you can be sure that your method will be used at runtime. The ambiguity is removed because your code now looks like this:

Class Extensions Extend the Internal Implementation

A class extension bears some similarity to a category, but it can only be added to a class for which you have the source code at compile time (the class is compiled at the same time as the class extension). The methods declared by a class extension are implemented in the @implementation block for the original class so you can’t, for example, declare a class extension on a framework class, such as a Cocoa or Cocoa Touch class like NSString.

The syntax to declare a class extension is similar to the syntax for a category, and looks like this:

@interface NSSortDescriptor (XYZAdditions)
+ (id)xyz_sortDescriptorWithKey:(NSString *)key ascending:(BOOL)ascending;
@end

This means you can be sure that your method will be used at runtime. The ambiguity is removed because your code now looks like this:
这样可以保证运行时你的方法被使用。
因为你的代码像如下这样消除了歧义

NSSortDescriptor *descriptor = [NSSortDescriptor xyz_sortDescriptorWithKey:@"name" ascending: YES];

Because no name is given in the parentheses, class extensions are often referred to as anonymous categories.

Unlike regular categories, a class extension can add its own properties and instance variables to a class. If you declare a property in a class extension, like this:


二、参考链接
https://www.jianshu.com/p/cbb4aece70d7
https://www.jianshu.com/p/7900a6b39843
https://tech.meituan.com/2015/03/03/diveintocategory.html
https://www.jianshu.com/p/c9d2f635b6d5 编译

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容