This is the fourth in the Objective-C series.

  • Objective-c (1) Familiar with Objective-C
  • Objective-c (2) Objects, messages, and runtime
  • Objective-c language (3) Interface and API design
  • Objective-c (4) Protocols and classification
  • Objective-c language (5) System framework
  • Objective-c (6) Block and GCD

1. Best practices

  • Use bits wisely in development
  • Divide the implementation code of a class into manageable categories

    • A classification mechanism is used to divide the implementation code of a class into small manageable modules.
    • Name methods that should be considered “private.PrivateTo hide implementation details.
  • Always prefix the class names of third-party classes

    • When adding categories to third-party classes, always give their names your own prefix.
    • When adding classes to third-party classes, always prefix the methods with your own special prefix.
  • Do not declare attributes in a classification

    • Define all attributes used to encapsulate data in the master interface;

    • In categories other than class-Continuations, you can define access methods, but try not to define properties.

      Editor’s note: In many third-party open source libraries, the use of “associative objects” to define attributes in categories is a common tool.

  • Use the class-CONTINUATION classification to hide implementation details

    • Add instance variables to a class using the class-CONTINUATION category;
    • If a property is always declared “readonly” on the main interface and a class needs to change it internally with a setup method, extend it to “readwrite” in a class-CONTINUATION;
    • Declare a private method’s prototype declaration in a class-continuation;
    • If you want to keep the protocol that your class follows unknown, you can declare it with a “class-continuation”.
  • Provide anonymous objects by protocol

    • The protocol may provide anonymous types to some extent. The specific object type can be diluted to an ID type that complies with some one, and the protocol specifies the methods that should be used in advance.
    • Use anonymous objects to hide type names (or class names).
    • If the specific type is not important but the object’s ability to respond to a particular method (defined in the protocol), then anonymous objects can be used.

Second, practical explanation

2.1 a period of

In a delegate proxy, if the proxy is frequently checked to see if it responds to a method, the proxy capability is cached for optimization. The best way to optimize is to use “bits”. A “bit segment” is a C language data type.

About the bit segment, a brief explanation:

2.1.1 definition

struct bs{
    int a:1;
    int  :2;		// There is no bit segment name, it is used only for filling or adjusting position
    int b:3;		
    int  :0;		/ / the airspace
    int c:5;		// Start with the next unit
};

struct bs data;
Copy the code

Or:

struct bs{
    int a:1;
    int  :2;
    int b:3;
    int  :0;
    int c:5;
} data;
Copy the code

2.1.2 sample

#include <stdio.h>
int main(a){
    struct{
        unsigned a:1;
        unsigned b:3;
        unsigned c:4;
    } bit, *pbit;
    bit.a=1;
    bit.b=7;
    bit.c=15;
    printf("%d, %d, %d\n", bit.a, bit.b, bit.c);
    pbit=&bit;
    pbit->a=0;
    pbit->b&=3;
    pbit->c|=1;
    printf("%d, %d, %d\n", pbit->a, pbit->b, pbit->c);
    return 0;
}
Copy the code
  1. The bit type can only be int, unsigned int, or signed int. It cannot be char or floating-point.
  2. The number of bits can not exceed the maximum number of bits that the basic type can represent. For example, in VC, int is 4 bytes, so it can only be 32 bits at most;
  3. A bit-field can have no bit-domain name, which is used only for padding or repositioning. Nameless bit fields are unusable;
  4. If the bit segment occupies 0 bits, the bit segment must be an unknown bit segment, and the next bit segment is stored from the next bit storage unit.

2.1.3 Application of bit segment in principal-agent mode

@class HONetworkFetcher;
@protocol NetworkFetcherDelegate <NSObject>

@optional
- (void)networkFetcher:(HONetworkFetcher*)fetcher didReceiveData:(NSData*)data;
- (void)networkFetcher:(HONetworkFetcher*)fetcher didFailerWithError:(NSError *)error;
- (void)networkFetcher:(HONetworkFetcher*)fetcher didUpdateProgerssTo:(float)progress;

@end

@interface HONetworkFetcher : NSObject
@property (nonatomic ,weak) id<HONetworkFetcherDelegate> delegate;
@end
Copy the code
#import "HONetworkFetcher.h"
@interface HONetworkFetcher(a)
{
    struct {
        unsigned int didReceiveData                 :1;
        unsigned int didFailedWithError             :1;
        unsigned int didUpdateProgressTo            :1;
    } _delegateFlags;
    
}

@end

@implementation HONetworkFetcher

/** * Check method reachability while setting up the proxy and cache it */
- (void)setDelegate:(id<HONetworkFetcherDelegate>)delegate
{
    _delegate = delegate;
    _delegateFlags.didReceiveData = [delegate respondsToSelector:@selector(networkFetcher:didReceiveData:)];
    _delegateFlags.didFailedWithError = [delegate respondsToSelector:@selector(networkFetcher:didFailerWithError:)];
    _delegateFlags.didUpdateProgressTo = [delegate respondsToSelector:@selector(networkFetcher:didUpdateProgerssTo:)];
}

@end
Copy the code

2.2 Divide the implementation code of a class into manageable categories

  • A classification mechanism is used to divide the implementation code of a class into small manageable modules.
  • Name methods that should be considered “private.PrivateTo hide implementation details.

2.3 Always prefix the class names of third-party classes

A class adds new functionality to an existing class. If multiple classes add a method with the same method name to the class, the method name will be overwritten several times at run time, with the last overwritten mainly. If you run into a bug like this, it’s hard to trace back because, unbeknownfully, someone else has overwritten the method. So to avoid this, we need to prefix the classification as a “namespace”, such as:

@interface NSString	 (HOG_HTTP)
- (NSString*)hog_urlEncodedString;
@end
Copy the code

Even if you add a prefix, there’s no guarantee that other categories won’t override the method you’ve written. But it reduces the probability.

  • When adding categories to third-party classes, always give their names your own prefix.
  • When adding classes to third-party classes, always prefix the methods with your own special prefix.

2.4 Do not declare attributes in a classification

Attributes are a way to encapsulate data. Technically, classifications can also declare attributes, but avoid this practice.

Declaration Document:

@interface HOPerson (Friends)
@property (nonatomic ,strong)NSSet *friends;
@end
Copy the code

Implementation file:

@implementation HOPerson(Friends)
@end
Copy the code

A warning is issued:

HOPerson+Friends.m:11:17: Property 'friends' requires method 'friends' to be defined - use @dynamic or provide a method implementation in this category HOPerson+Friends.m:11:17: Property 'friends' requires method 'setFriends:' to be defined - use @dynamic or provide a method implementation in this  categoryCopy the code

To eliminate warnings, either add @dynamic or the corresponding setter/getter methods.

Add setter/getter methods to the implementation file:

#import "HOPerson+Friends.h"
#import <objc/runtime.h>

static const char *kFriendPropertyKey  = "kFriendPropertyKey";

@implementation HOPerson(Friends)
//@dynamic friends;

- (NSSet *)friends
{
    return objc_getAssociatedObject(self, kFriendPropertyKey);
}

- (void)setFriends:(NSSet *)friends
{
    objc_setAssociatedObject(self,
                             kFriendPropertyKey,
                             friends,
                             OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end
Copy the code

In this case, the right thing to do is to define all the attributes in the main interface. The main interface is the only place where member variables (data) can be defined. Properties are just “syntactic sugar” used to define instance variables and their associated access methods, so they should follow the same rules as instance variables. The classification mechanism should be understood as a means to extend the functionality of a class, not to encapsulate data.

  • Define all attributes used to encapsulate data in the master interface;
  • In categories other than class-Continuations, you can define access methods, but try not to define properties.

2.5 Hides implementation details using the class-Continuation category

Unlike other classes, class-continuations must be defined in the implementation file of the class they continue from. The important thing is that this is the only class that can declare instance variables, and there is no specific implementation file for this class, whose methods should be defined in the class’s main implementation file.

Refer to the bit segments defined in “Application of bit segments in the principal-agent pattern” in the previous section of this article, i.e. instance variables.

Instance variables are defined in class-continuations primarily to hide the details.

It is also desirable to declare in a class-continuation that only private methods are in the class’s implementation code. Before you write the implementation code for a class, you first declare the method prototypes you need to implement in a class-continuation and then implement them one by one. Such as:

@interface HOPerson(a)
{
    NSMutableSet *_internalFriends;
}

- (void)hog_findFriends;

@end

@implementation HOPerson
@end
Copy the code

Finally, there are cases where an object follows a protocol that should only be considered private, and it is best to declare it in a class-continuation as well. Such as:

@interface HOPerson()"NSCopying.NSCoding>
{
    NSMutableSet *_internalFriends;
}

- (void)hog_findFriends;
@end

@implementation HOPerson
@end
Copy the code
  • Add instance variables to a class using the class-CONTINUATION category;
  • If a property is always declared “readonly” on the main interface and a class needs to change it internally with a setup method, extend it to “readwrite” in a class-CONTINUATION;
  • Declare a private method’s prototype declaration in a class-continuation;
  • If you want to keep the protocol that your class follows unknown, you can declare it with a “class-continuation”.

2.5 Providing Anonymous Objects through protocols

  • The protocol may provide anonymous types to some extent. The specific object type can be diluted to an ID type that complies with some one, and the protocol specifies the methods that should be used in advance.
  • Use anonymous objects to hide type names (or class names).
  • If the specific type is not important but the object’s ability to respond to a particular method (defined in the protocol), then anonymous objects can be used.