Objective-c categories, also known as classifications, are a syntactic technique for adding methods to classes without inheritance. Let’s take a look at how to use it and the three scenarios in which it can be used.

Add category

Category declaration template:

H files:

@interface ClassName (XSD_CategoryName)
- (void)xsd_addedMethod;
@endCopy the code

M file:

@implementation ClassName (XSD_CategoryName)
- (void)xsd_addedMethod {
}
@endCopy the code

XSD is my custom prefix, and I strongly recommend adding a custom prefix, for reasons described below.

Added via Xcode8




Choose the Objective – C File




File Type Select Category




File specifies the class name, no need to write the class name




Select the file storage address




Adding good categories

There are three scenarios for the use of categories

1. Extend existing classes

When it comes to adding methods to an existing class, it seems like a good idea to define a subclass that inherits from an existing class and then add methods, like we often do custom UITableViewCell, XSDTableviewCell, showIconWithImage:(UIImage *)image, but the reality is that a lot of the code that you already have is already implemented using the parent class UITableViewCell, and you need to use subclasses

  1. Add the header file for the new class
  2. All useUITableViewCellIs to be changed toXSDTableviewCell
  3. Calling a new methodshowIconWithImage:

This second step, which may involve changing the class name of array storage, parameters, local variables, and so on, can take a lot of time if the modified method needs to be shared across files. When category is used, it becomes:

  1. Add a header file of a new category
  2. Call the new class method directly with UITableViewCell

In addition, if XSDTableviewCell already exists, you can simply introduce the new class header file and call the showIconWithImage: method directly, because the class method added to the parent class will be inherited by the subclass. As a result,

Classes are particularly suitable for situations where there are already a large number of subclasses that need to add common methods, but cannot modify their parent classes, such as system classes

2. Refer to an unexposed method of the parent class

For example, the parent class XSDLabel:

// XSDLabel.h
#import 

@interface XSDLabel : UILabel

@endCopy the code
// XSDLabel.m
#import "XSDLabel.h"

@implementation XSDLabel
- (void)giveTextRandomColor {
    self.textColor = [UIColor orangeColor];
}
@endCopy the code

XSDLabel1 inherits from XSDLabel:

#import 
#import "XSDLabel.h"
@interface XSDLabel1 : XSDLabel

@endCopy the code

Now we need to set the text color when setting the text, and call the parent class of giveTextRandomColor:

#import "XSDLabel1.h"

@implementation XSDLabel1

- (void)setText:(NSString *)text {
    [super setText:text];
    [self giveTextRandomColor];
}

@endCopy the code

Compiling directly will cause an error:




The compiler prompts that a method of the parent class cannot be found

After declaring a parent class in a subclass, we can compile:

#import "XSDLabel1.h"

@interface XSDLabel (private)
- (void)giveTextRandomColor;
@end

@implementation XSDLabel1

- (void)setText:(NSString *)text {
    [super setText:text];
    [self giveTextRandomColor];
}

@endCopy the code

The class name private is arbitrary, but cannot be default.

Please don’t mess around: Apple officially refuses to ship apps that use the system’s private apis, so even if you’ve learned how to call private methods, be careful when calling private methods of other classes and try to substitute them.

3. Implement simple protocols

Suppose we need to send a message when the text color changes, now modify XSDLabel as follows:

#import 

@interface XSDLabel : UILabel
@property(nonatomic) id delegate;
@end

@interface NSObject (XSDLabelDelegateMethods)
- (void)textColorChanged:(UIColor *)colorNow;
@endCopy the code

Add a delegate, declared as an ID, to accept any class. Declare the class of NSObject, and declare the methods it implements.

#import "XSDLabel.h" @implementation XSDLabel - (void)giveTextRandomColor { self.textColor = [UIColor orangeColor]; [self.delegate textColorChanged:self.textColor]; // Call the proxy's method} @endCopy the code

Where to call:

#import "XSDLabel1.h" @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, Typically from a nib.xsdlabel1 *label = [[XSDLabel1 alloc] initWithFrame:CGRectMake(10.0, 40.0, 100.0, 30.0)]; [self.view addSubview:label]; label.delegate = self; Label. text = @" iOS"; } - (void)textColorChanged:(UIColor *)colorNow { NSLog(@"text color changed to %@", colorNow); }Copy the code

Taking advantage of the fact that any class is a subclass of NSObject, a “simple” proxy protocol is implemented by adding the class of NSObject.

In contrast to the “formal protocol”, this protocol does not require the implementation class display declaration (e.g., ViewController), but there is a drawback here. It crashes when the ViewController does not implement the textColorChanged method, so you need to check whether the proxy method is implemented before calling:

#import "XSDLabel.h"

@implementation XSDLabel
- (void)giveTextRandomColor {
    self.textColor = [UIColor orangeColor];
    if ([self.delegate respondsToSelector:@selector(textColorChanged:)]) {
        [self.delegate textColorChanged:self.textColor];
    }
}
@endCopy the code

Limitations of categories

  1. You can only add methods, not properties. Properties declared in a category will not be accessible.
  2. A method in a class that overrides a method of the same name in its parent class can no longer be called (because super cannot be used in a class), and classes should always be prefixed to prevent accidental overwriting.
  3. Methods with the same name in different files do not report errors. The actual method executed is the last file to be loaded. Therefore, prefix is used to prevent the categories from overwriting each other.

summary

This article shows how to add categories, which Xcode8 adds differently from previous generations. Then three cases of category use are introduced:

  1. Extend existing classes.
  2. Reference an unexposed method of a parent class.
  3. Implement simple protocols.

Finally, we introduce the limitations of categories, mainly storage space allocation and name conflicts, which can be avoided by prefixing.

Categories are a way to take full advantage of the dynamic nature of Objective-C to achieve flexible and diverse coding.