“Writing High Quality OC Code” has been successfully completed chapter one, two, three, four, five, six, eight! Attach a link: Objective-c code for iOS (1) — Objective C code for iOS (2) — Objective C code for iOS (3) — Interface and API design Objective-c code (5) — Memory management mechanism (6) — Block column iOS Writing high quality Objective-C code for iOS (GCD


The topic of this paper is protocol & Category.

First, a brief introduction to today’s main character: protocol and classification

  • Protocol (protocol) : Protocol in OC and Java interface (interfaceSimilarly, OC does not support multiple inheritance. But it can be done by protocolDelegate pattern.
  • Classification (category) : Categories can add new functionality to existing classes. Classification is the classification of”A double-edged sword“, well used can play OCHigh dynamic; If you don’t use it properly, it will leave a lot of potholes. Therefore, let’s study a language feature of OC through this article:category.

1. Communication between objects through delegate and data source protocol

Delegate pattern (also known as proxy) : an object assigns a class of methods (tasks) to another object to help complete. ~ similar to: a boss assigns a task to a leader. (Of course, multi-class tasks will be completed by multiple leaders). ~

For example, the delegate pattern can be used when an object wants to fetch data from another object. The implementation of a Data Source Protocol to retrieve Data is called the Data Source Protocol. Similar to UITableView’s UITableView datasource.

For another example, the delegate pattern can be used when an object wants to have some event response. Having a proxy object help the object handle the event response by implementing a protocol (commonly called a delegate). It’s like a UITableView delegate for a UITableView.

Look at the illustration:

  • Benefits: Reduces code coupling through protocols. (Decoupling) Protocols can also replace inheritance if necessary. Because there can be many classes that follow the same protocol, inheritance is not necessary.

This is an example of Button animation

  • QiCircleAnimationView. H:
@class QiAnimationButton; @protocol QiAnimationButtonDelegate <NSObject> @optional - (void)animationButton:(QiAnimationButton *)button willStartAnimationWithCircleView:(QiCircleAnimationView *)circleView; - (void)animationButton:(QiAnimationButton *)button didStartAnimationWithCircleView:(QiCircleAnimationView *)circleView;  - (void)animationButton:(QiAnimationButton *)button willStopAnimationWithCircleView:(QiCircleAnimationView *)circleView;  - (void)animationButton:(QiAnimationButton *)button didStopAnimationWithCircleView:(QiCircleAnimationView *)circleView; - (void)animationButton:(QiAnimationButton *)button didRevisedAnimationWithCircleView:(QiCircleAnimationView *)circleView; @end @interface QiAnimationButton : UIButton @property (nonatomic, weak) id<QiAnimationButtonDelegate> delegate; - (void)startAnimation; / /! < start animation - (void)stopAnimation; / /! < animation - (void)reverseAnimation; / /! < Last modified animation @endCopy the code
  • Qianimationbutton. m: Can be called in this way
if ([self.delegate respondsToSelector:@selector(animationButton:willStartAnimationWithCircleView:)]) { [self.delegate animationButton:self willStartAnimationWithCircleView:_circleView]; } / *... */ if ([self.delegate respondsToSelector:@selector(animationButton:didStartAnimationWithCircleView:)]) { [self.delegate animationButton:self didStartAnimationWithCircleView:_circleView]; }Copy the code

Etc etc…

So, a lot of code is written that looks like this:

if ([self.delegate respondsToSelector:@selector(xxxFunction)]) {
    [self.delegate xxxFunction];
}
Copy the code

Explanation: Because the methods in the protocol are @optional, classes that comply with the protocol can optionally implement the methods in the protocol. Therefore, when a proxy object invokes a callback method, it should first check to see if the Class implements any of the protocol’s methods. If it does, call back; If that doesn’t happen, move on.

Consider performance optimization:

Imagine a scenario in which callback methods are called back frequently. That is, a callback method is called very often. Every time a callback method is called, check to see if the Class implements it. So the performance is going to get worse.

Solution: Implement a structure containing bits to cache information about whether the delegate object can respond to a protocol method to optimize program execution efficiency.

A hundred say is better than a Demo, the following see xiaobian finishing Demo~

  1. Declare a structureDelegateFlags:
@interface QiAnimationButton () {
    
    struct DelegateFlags {
        int doWillStartAnimation : 1;
        int doDidStartAnimation : 1;
        int doWillStopAnimation : 1;
        int doDidStopAnimation : 1;
        int doDidRevisedAnimation : 1;
    };
}
Copy the code
  1. Declare an attribute:
@property (nonatomic, assign) struct DelegateFlags delegateFlags;
Copy the code
  1. rewritedelegatethesetMethod: Information about whether the protocol method will be implementedThe cacheup
- (void)setDelegate:(id<QiAnimationButtonDelegate>)delegate {
    
    _delegate = delegate;
    _delegateFlags.doWillStartAnimation = [delegate respondsToSelector:@selector(animationButton:willStartAnimationWithCircleView:)];
    _delegateFlags.doDidStartAnimation = [delegate respondsToSelector:@selector(animationButton:didStartAnimationWithCircleView:)];
    _delegateFlags.doWillStopAnimation = [delegate respondsToSelector:@selector(animationButton:willStopAnimationWithCircleView:)];
    _delegateFlags.doDidStopAnimation = [delegate respondsToSelector:@selector(animationButton:didStopAnimationWithCircleView:)];
    _delegateFlags.doDidRevisedAnimation = [delegate respondsToSelector:@selector(animationButton:didRevisedAnimationWithCircleView:)];
}
Copy the code
  1. Directly through_delegateFlagsThe cached value determines whether it can be called back
if (_delegateFlags.doWillStartAnimation) {
   [self.delegate animationButton:self willStartAnimationWithCircleView:_circleView];
}

/* .... */

if(_delegateFlags.doDidStartAnimation) { [self.delegate animationButton:self didStartAnimationWithCircleView:_circleView];  }Copy the code

Second, divide the implementation code of complex classes into manageable categories

  • Use a classification mechanism to “slim down” some of the most complex classes into manageable categories.
  • Making private methods a separate classification hides implementation details.

Benefits: 1. Decouple complex classes into smaller pieces. Easy to maintain and manage. 2. Easy to debug: problems can be quickly located is that classification.

Depending on the specific situation, split at the same time, there will be many more files. If a class is bloated (thousands of lines of code, for example), consider slimming it down and splitting it into multiple categories.

Always prefix the names of third party categories

  • The most powerful feature of the classification mechanism is to add new functionality to existing classes that cannot modify the source code.

At this point we should:

  • The classification class name is preceded by a special prefix.
  • The classification method name is preceded by a special prefix.

Avoid as much as possible the same name can cause bugs, and this kind of bug is difficult to troubleshoot.

The reason: classified methods are added directly to the class, whereas classification adds methods to the main class at run time. In this case, if a method has a duplicate name, the classification method written after it overwrites the previous one. The result of multiple coverage is always the last classification. So we want to add a prefix, try to prevent the occurrence of the same name of the bug.

Do not declare attributes in categories

Do not declare attributes in a classification, but you can declare attributes in a class extension so that they are not exposed.

For example :(class extension)

// @interface QiShare () /* attributes can be declared here */ @end@implementation QiShare /*... */ @endCopy the code
  1. You cannot declare attributes directly in a classification. If it is declared, the following warning will be generated at compile time: Property ‘name’ requires method ‘setName:’ to be defined – use @dynamic or provide a method implementation in this Category explanation: Classification cannot synthesize associated instance variables and requires the developer to implement access methods (GET and set) for this property. Because no instance variables are generated, the set method does not work. The get method can return a fixed value. Or use @dynamic declarations (that is, instance variables and access methods are not declared).

  2. Add attributes to categories by associating objects. (See Title II – Clause 5 for details)

Therefore, 1. It is recommended to put all attributes in the main class. 2. Try not to add attributes through associated objects in categories unless you have to. Because the memory management of associated objects is very error-prone, you need to be careful when using them.

Use the class-continuation class to hide implementation details

By class-Continuation, I mean extension.

We can declare some private attributes in the class extension, so that when we import the.h file, we can’t see the attributes declared by the class extension. Purpose: To minimize exposure to the public interface and hide some attributes and implementation details.

Here’s a quick tidbit: Everyone knows objective-C, but have you heard of Objective-C++?

Objective-c ++ is a blend of Objective-C and C++ that generates.mm files when compiled. There is a problem: whenever you import.h files containing C++, they will be compiled into.mm files. Because only.mm files can compile both OC and C++. So, how to solve OC? Extend with classes.

Here’s an example:

#import "OCClass.h"
#import "CppClass.cpp"

@interface OCClass () {
    SomeCppClass *_cppClass;
}

@end

@implementation OCClass

/* ... */

@end
Copy the code

In this way, there is no C++ code in the.h file, and you wouldn’t even know there was C++ code at the bottom if you just looked at the header file. In fact, our system does the same thing. For example, WebKit, CoreAnimation, etc., a lot of low-level code is written through C++.

Summary: Application scenarios of class extension 1. Add instance variables or attributes to a class 2. A property declared as “read-only” in a.h file can be redeclared as “read/write” in a class extension if the class wants to change the property. 3. Private method prototypes can be declared in class extensions. 4. If you do not want the outside world to know which protocols are observed in a class, you can comply with the protocols in a class extension.

Provide anonymous objects through protocol

  1. Anonymous objects can be provided by protocol, for example:id<someProtocol> delegate. There is no limit to the type of the delegate object, as long as the object complies with this protocol. The protocol specifies the methods that the object needs to implement.
  2. Use anonymous objects to hide type and class names.
  3. The object simply implements the methods in the protocol (@optionalThe rest of the implementation details are hidden.

Finally, special thanks: Effective Objective-C 2.0 chapter 4