define

A Category is a syntax unique to OC that represents a pointer to the structure of a Category. In principle, it can only add methods, not member (instance) variables. Specific reasons to see the source code composition:

Category a Category is a pointer to a classification structure defined as follows: typedef struct objc_category *Category; struct objc_category { char *category_name OBJC2_UNAVAILABLE; // Category name char *class_name OBJC2_UNAVAILABLE; Struct objc_method_list *instance_methods OBJC2_UNAVAILABLE; Struct objc_method_list *class_methods OBJC2_UNAVAILABLE; Struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; The instance_methods list is a subset of objc_class's list of methods. The instance_methods list is a subset of objc_class's list. The class_methods list is a subset of the list of metaclass methods. But there's no property list in this structure at all, no property list at all, no property list at all.Copy the code

1. Classes are used to add methods to existing classes because there is no property list in the structure pointer of a class, only a list of methods. In principle, it can only add methods, not attributes (member variables). In fact, it can add attributes in other ways. Classes can write @property, but do not generate setter/getter methods, implementation and private member variables (warning at compile time). 3. You can access the attributes in the original class. H in the classification. 4. If there is a method in the classification with the same name as the original class, the method in the classification will be called first, that is, the method in the original class will be ignored. So the precedence of method calls with the same name is category > this class > parent class. So try not to overwrite existing classes in your development; 5. If more than one class has a method with the same name as the original class, the compiler decides who executes when calling the method. The compiler executes the method in the last class to participate in the compilation.

@interface Programmer (Category) @property(nonatomic,copy) NSString *nameWithSetterGetter; @property(nonatomic,copy) NSString *nameWithoutSetterGetter; Void programCategoryMethod; void programCategoryMethod; void programCategoryMethod; void programCategoryMethod; // end // Programmer+CategoryCopy the code

So here’s the question:

Why doesn’t the runtime fail when you declare attributes in a classification? If classes don’t let you add attributes, why did I write @property and still get compiled?

Next, let’s explore the actual reason why categorization cannot add attributes:

We know that when we declare a property with @property in a class, the compiler automatically generates _ member variables and setters/getters for us, but there is no property list in the pointer structure of the classification. So declaring a property with @property in a class doesn’t generate an _ member variable or a setter/getter. So the conclusion is: we can declare a property with @property, and it will compile and run without crashing as long as we don’t use it. But if you call _ member variables and setter/getter methods, an error is inevitable.

The reasons for the error are as follows

/ / general statement, no setter/getter / / programmer nameWithoutSetterGetter = @ "no setter/getter"; [Programmer name with settergetter :] [Programmer name with settergetter :] [Programmer name with settergetter :] Unrecognized selector that sent to instance 0 x7f9de358fd70 ') / / NSLog (@ "% @" programmer. NameWithoutSetterGetter); [Programmer's nameWithsettergetter :]: [Programmer's nameWithsettergetter :] unrecognized selector sent to instance 0x7fe22be11ea0' // NSLog(@"%@",_nameWithoutSetterGetter); Use of undeclared identifier '_nameWithoutSetterGetter')Copy the code

The root cause of the error is the use of setter/getter methods that are not generated by the system. Can we manually add setter/getter methods to avoid crashes and complete the call? Well, you can. Since OC is a dynamic language, the real implementation of methods is done through the Runtime. Although the system does not generate setters/getters for us, we can manually add setter/getter methods through the Runtime. So how do you do that?

The code implementation is as follows:

With this in mind, we add this method manually through the runtime.

#import <objc/runtime.h> static NSString *nameWithSetterGetterKey = @"nameWithSetterGetterKey"; // implementation Programmer (Category) // implementation runtime setter method - (void)setNameWithSetterGetter:(NSString) *)nameWithSetterGetter { objc_setAssociatedObject(self, &nameWithSetterGetterKey, nameWithSetterGetter, OBJC_ASSOCIATION_COPY); } // Run time implement getter method - (NSString *)nameWithSetterGetter {return objc_getAssociatedObject(self, &nameWithSetterGetterKey); } @endCopy the code

Actual effect

/ / by the runtime implements the setter/getter programmer. NameWithSetterGetter = @ "setter/getter"; / / call the setter, successful NSLog (@ "% @" programmer. NameWithSetterGetter); // NSLog(@"%@",_nameWithSetterGetter); Use of undeclared identifier '_nameWithSetterGetter')Copy the code

Problem solved.

Note, however, that the above code simply implements the setter/getter method manually, but calling the _ member variable still returns an error.

Class Extension

Extension is a special case of Category. Class extension is called “anonymous classification” because it lacks only the name of the classification. In fact, during development, we use it almost every day. For some it’s like the most familiar stranger.

Class extension format

@interface XXX () // Private property // private Method (if not implemented, it will be reported when compiled,Method definition for 'XXX' not found) @endCopy the code

Function:

Add extra classes that don’t have variables, methods, and attributes that are generic class extensions and write them to the.m file and write the generic private attributes to the.m file

The difference between categories and class extensions:

(1) In principle, only methods can be added to classes (the reason for adding attributes is to solve the problem of no setter/getter through the Runtime); (2) Class extension can not only add methods, but also add instance variables (or attributes), which are of type @private by default (scope can only be used in its own class, not subclasses or elsewhere); ③ The compiler will raise an alarm if a method declared in a class extension is not implemented, but the compiler will not raise any alarm if a method declared in a class extension is not implemented. This is because class extensions are added to the class at compile time, whereas categories are added to the class at run time. (4) A class extension cannot have a separate implementation part (the @implementation part) like a class does. That is, the methods declared by a class extension must rely on the implementation part of the corresponding class. ⑤ Class extension methods defined in.m files are private, while class extension methods defined in.h files (header files) are public. Class extensions are a great way to declare private methods in.m files