Summarize some interface declaration specifications, macros, useful modifiers for defining methods, specification for writing annotations, and finally write a qualified header file.

  • 1. Read and write permissions
    • 1.1 @public,@protected,@private keywords for instance variables
    • 1.2 Readonly,readwrite keywords for attributes
  • 2. Forward statements
  • 3. Expose only necessary interfaces and implementations
    • 3.1 Do not expose any private methods that are used only inside a class
    • 3.2 Do not declare internal protocols for classes in header files
  • 4. The nullability
  • 5. Define enumerations
    • 5.1 NS_ENUM
    • 5.2 NS_OPTIONS
    • 5.3 Enumeration of Strings
  • 6. Use extern to provide read-only constants externally
  • 7. Provide private attributes of the parent to subclasses and categories
  • 8. Designated Initializer
  • 9.API version control
    • 9.1 the available
    • 9.2 unavailable
    • 9.3 deprecated
  • 10. Extra modifiers
    • 10.1 generics
    • 10.2 NS_REQUIRES_SUPER
    • 10.3 NS_NOESCAPE
  • 11. Write a comment
    • 11.1 One-line comments
    • 11.2 Multi-line comments
    • 11.3 Enumeration Comments
    • 11.4 Several annotation conventions

1. Read and write permissions

Declarations in.h files are intended to be exposed to external interfaces, while private methods, private attributes, and instance variables within a class should be placed in interface Extension in.m files.

1.1 @public,@protected,@private keywords for instance variables

These three keywords are used to modify instance variables, not properties. Xcode displays an error message when an instance variable is used incorrectly.

The keyword instructions
@private Scope can only be on its own class
@protected Scope to its own class and subclasses that inherit from it, write nothing, default to this property.
@public Maximum range, anywhere.

Sample code:

//SearchManager.h
@interface SearchManager : NSObject {
    @public    NSInteger *state;
    @public    NSInteger *timeout;
    @protected id *searchAPI;
    @private   id _privateIvar;
}
@end
Copy the code

Because private variables are exposed and there are no advanced @property keywords, instance variables are rarely declared in header files. @property is preferred.

1.2 Readonly,readwrite keywords for attributes

The properties in the header file are a collection of properties that describe the object. When declaring @property, use readonly in.h to give external read permission, and use readwrite in.m to give internal read/write permission.

Sample code:

//SearchManager.h
@interface SearchManager : NSObject
@property (nonatomic, readonly) NSInteger * state;
@end
Copy the code
//SearchManager.m
@interface SearchManager : NSObject
@property (nonatomic, readwrite) NSInteger * state;
@end
Copy the code

2. Forward statements

When using another class in the @interface interface, do not import the class header directly in the.h directory. This will cause unnecessary import of other header files in the same place that uses the header. The correct way to do this is to use the keyword @class for forward declarations. Of course, if you inherit from the parent class, you still need to import the parent class header file. Sample code:

//SearchManager.h
#import "searchManagerBase. h"// Import the parent header file@class LocationModel; / / before class to declare LocationModel typedef void (^ LocationSearchCompletionHandler) (LocationModel * the location, NSError * error); @interface LocationSearchManager : SearchManagerBase - (void)searchLocationWithKeyword:(NSString *)keyword completionHandler:(LocationSearchCompletionHandler)completionHandler; @endCopy the code

Using @class tells the compiler that such a class exists, but it doesn’t care about the implementation until the caller uses it in.m. Declare a class and a protocol using @class and @protocol, respectively. There are two reasons for using a forward reference:

  • Improve compilation efficiency. If the importLocationModel.h, then whenLocationModel.hAll imports are added when the contents of theLocationModel.hAll need to be recompiled. If dot m references itSearchManager.hBut it is not usedLocationModel, will increase unnecessary compilation, reduce development efficiency.
  • Resolve cross references. If the header from class A imports B and the header from class B imports A, it will compile with an error: “Can not find interface declaration” because Objective-C does not allow cross-references.

3. Expose only necessary interfaces and implementations

3.1 Do not expose any private methods that are used only inside a class

Only those methods that are exposed for external use are declared in header files and should be designed with testability in mind, following a single responsibility. Private methods are defined only inside the class, and to distinguish them, it is recommended to prefix them, for example – (void)p_myPrivateMethod. Since Apple states in its coding specification that Apple owns the underscore method prefix just as it owns the NS and UI class name prefixes, it is not recommended that our private methods use the underscore prefix directly. Otherwise, when you inherit Cocoa Touch classes, you run the risk of overwriting the parent’s private methods, causing hard-to-debug errors.

3.2 Do not declare internal protocols for classes in header files

Example code for this rule:

//SearchManager.h
@interface SearchManager : NSObject<NSCoding, UITableViewDelegate>
@property (nonatomic, readonly) NSInteger * state;
@end
Copy the code

UITableViewDelegate is the protocol that the class follows for internal use. It doesn’t need to be exposed externally, so it should be in a.m file. NSCoding, on the other hand, describes the class’s properties to tell outsiders that the class can be archived and therefore should be placed in a header file.

4. The nullability

When declaring, you can use the following keywords to describe whether an object can be nil.

The keyword instructions
nullable Nullable to describe objC objects
nonnull Non-null, used to describe objC objects
null_unspecified Indeterminate, used to describe objC objects
null_resettable Set can be null, get is not null. Only for the property
_Nullable Nullable to describe C Pointers and blocks
_Nonnull Non-null, used to describe C Pointers and blocks
_Null_unspecified Indeterminate, used to describe C Pointers and blocks

Sample code:

//SearchManager.h
#import "SearchManagerBase.h"
@class LocationModel;

typedef void(^LocationSearchCompletionHandler)(LocationModel *_Nullable location, NSError *_Nullable error);
@interface LocationSearchManager : SearchManagerBase
- (void)searchLocationWithKeyword:(nonnull NSString *)keyword completionHandler:(LocationSearchCompletionHandler _Nonnull)completionHandler;
@end
Copy the code

The compiler warns if null is assigned to a value that uses the nonNULL modifier. In development, nonNULL is used most of the time. Therefore, Apple provides a pair of macros NS_ASSUME_NONNULL_BEGIN and NS_ASSUME_NONNULL_END for quick modification. Properties and methods written between the two macros will use nonNULL modifier. Sample code:

//LocationSearchManager.h

#import "SearchManagerBase.h"
@class LocationModel;

NS_ASSUME_NONNULL_BEGIN
typedef void(^LocationSearchCompletionHandler)(LocationModel *_Nullable location, NSError *_Nullable error);
@interface LocationSearchManager : SearchManagerBase
- (void)searchLocationWithKeyword:(NSString *)keyword completionHandler:(LocationSearchCompletionHandler)completionHandler;
@end
NS_ASSUME_NONNULL_END
Copy the code

5. Define enumerations

For the difference between NS_ENUM and NS_OPTIONS, see here. In short, NS_OPTIONS provides bitwise masks.

5.1 NS_ENUM

Sample code:

typedef NS_ENUM(NSInteger,SearchState) {
    SearchStateNotSearch,
    SearchStateSearching,
    SearchStateSearchFinished,
    SearchStateSearchFailed
};
Copy the code

5.2 NS_OPTIONS

For example code, see nsKeyValueobserving.h:

typedef NS_OPTIONS(NSUInteger, NSKeyValueObservingOptions) {
    NSKeyValueObservingOptionNew,
    NSKeyValueObservingOptionOld,
    NSKeyValueObservingOptionInitial,
    NSKeyValueObservingOptionPrior
};
Copy the code

When use can use | combining multiple option:

[_webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:NULL];
Copy the code

5.3 Enumeration of Strings

When a dictionary is passed as an argument or as a return value, it is often difficult to provide the dictionary key directly. For example code, see nsKeyValueobserving.h:

// Use the NS_STRING_ENUM macro to define an enumeration type typedef NSString * NSKeyValueChangeKey NS_STRING_ENUM; FOUNDATION_EXPORT NSKeyValueChangeKey const NSKeyValueChangeKindKey; FOUNDATION_EXPORT NSKeyValueChangeKey const NSKeyValueChangeNewKey; FOUNDATION_EXPORT NSKeyValueChangeKey const NSKeyValueChangeOldKey; FOUNDATION_EXPORT NSKeyValueChangeKey const NSKeyValueChangeIndexesKey; FOUNDATION_EXPORT NSKeyValueChangeKey const NSKeyValueChangeNotificationIsPriorKey; // Use generics to declare the key used by the change parameter, Is in the scope of the NSKeyValueChangeKey enumeration - (void) observeforkeypath :(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context;Copy the code

6. Use extern to provide read-only constants externally

This isn’t about at sign interface, but it’s about the header file, so I’ll put it all together.

//SearchManager.h
extern NSString *const SearchErrorDomain;
extern NSInteger SearchDefaultTimeout;

@interface SearchManager : NSObject
@end
Copy the code
//SearchManager.m
NSString *const SearchErrorDomain = @"SearchErrorDomain";
const NSInteger SearchDefaultTimeout = 20;

@interface SearchManager()
@end
Copy the code

7. Provide private attributes of the parent to subclasses and categories

Since the header file of a class only holds properties and methods that are exposed to the outside world, there are obstacles when encountering these cases:

  • In a subclass or category, you want to use the private property that the parent class defines in.m.
  • In the class header, the property is readonly, but in a subclass or category, the readwrite permission is required. Since these attributes are not exposed in the header file, we need to create a separate private header file to hold the attributes that need to be exposed to subclasses and categories. You can refer to the official AppleUIGestureRecognizerSubclass.h. Sample code:
// SearchManager.h@interface SearchManager: NSObjectreadonly) SearchState state;
@end
Copy the code
@property (nonatomic, assign) SearchState state; @property (nonatomic, assign) SearchState state; @property (nonatomic, strong) id searchAPI; @endCopy the code
/ / / exposure to subclasses and the category of private property and private methods / / SearchManagerInternal. H / / / limit the use of the header files, to prevent misuse by other class#ifdef SEARCHMANAGER_PROTECTED_ACCESS

#import "SearchManager.h"@interface SearchManager() @property (nonatomic, readWrite, assign) SearchState state; @property (nonatomic, strong) id searchAPI; // expose private methods - (void)p_privateMethod; @end#else
#error Only be included by SearchManager's subclass or category!
#endif
Copy the code
//SearchManager+ category. M // declare permission to use private header files#define SEARCHMANAGER_PROTECTED_ACCESS/// Import private header files#import "SearchManagerInternal.h"@implementation SearchManager(Category) - (void)categoryMethod {self.state = SearchStateSearching; // Access private attributes [self.searchapi startSearch]; // Use the private method [self p_privateMethod]; } @endCopy the code

Searchmanagerinternal.h is also public, and other classes can be imported and used, only by contract at development time. If you want to restrict other class imports and get an error, Internal. H can use the following method:

#ifdef MYCLASS_PROTECTED_ACCESS// Declaration section#else
#error Only be included by MYCLASS's subclass or category!
#endif
Copy the code

This generates a compile warning when an Internal. H is accidentally imported from another class and cannot be used directly. The disadvantage is that you need to #define MYCLASS_PROTECTED_ACCESS everywhere you use internal.h.

8. Designated Initializer

Specify the initializer method that receives the most parameters and can be called by other initializers. This is designed to ensure that all initializers correctly initialize instance variables. Add the NS_DESIGNATED_INITIALIZER macro to the end of the method. This way, when you subclass the designated Initializer for your subclass, the compiler will warn you if the designated Initializer for your subclass is not called correctly. Example code:

@interface WKWebView : UIView
- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
@end
Copy the code

For more information about Designated Initializer, please refer to:

9.API version control

When updating an interface or developing the framework, the version information needs to be displayed to inform users of the platform restrictions, operating system version, availability, and deprecations of the interface. Apple provides several built-in macros to indicate the version, and Xcode warns when it detects incorrect use. Just add the corresponding macro after the method name.

9.1 the available

Specifies the lowest operating system version supported by the interface. When your interface uses the API of the new system, such as UIAlertController (iOS8), but the deployment target of the project is iOS7, you need to indicate the version of this interface to make the user compatible. Example:

//SearchManager.h typedef NS_ENUM(NSInteger,SearchState) { SearchStateNotSearch, SearchStateSearching, SearchStateSearchFinished, SearchStateSearchFailed } NS_ENUM_AVAILABLE_IOS(2_0); NS_CLASS_AVAILABLE_IOS(2_0); // This class can only be used in iOS2.0. @interface SearchManager: NSObject - (void)reSearch NS_AVAILABLE_IOS(5_0); // this method can only be used with @end in iOS5.0 or higherCopy the code

There are platform versions of these macros, such as NS_AVAILABLE_MAC, NS_AVAILABLE_IOS, and NS_AVAILABLE_IPHONE. The available macro API_AVAILABLE is available for iOS10 to unify macOS, iOS, watchOS, and tvOS.

API_AVAILABLE(MacOS (10.9), ios(10.0)) API_AVAILABLE(MacOS (10.4), ios(8.0), watchos(2.0), Tvos (10.0))Copy the code

9.2 unavailable

Declaring this interface unavailable is most often used to declare platform restrictions. Example:

@interface SearchManager : NSObject - (void)searchInWatch NS_UNAVAILABLE; - (void)searchInHostApp NS_EXTENSION_UNAVAILABLE_IOS; // Extension cannot use this interface - (void)search __TVOS_PROHIBITED; // This interface cannot be used in tvOS. Enumerations, classes, methods, @end can be modifiedCopy the code

IOS10 starts with the new unavailable macro API_UNAVAILABLE:

API_UNAVAILABLE(macos)
API_UNAVAILABLE(watchos, tvos)
Copy the code

9.3 deprecated

Declare that this interface is deprecated, and you can comment the alternative interface. Xcode warns when the Deployment target version is set to be greater than or equal to the version of the deprecated method. Example:

NS_CLASS_DEPRECATED_IOS(2_0, 9_0,"UIAlertView is deprecated. Use UIAlertController with a preferredStyle of UIAlertControllerStyleAlert instead")
@interface UIAlertView : UIView
@end
Copy the code
@interface UIViewController: UIResponder - (void)viewDidUnload NS_DEPRECATED_IOS(3_0,6_0); @endCopy the code
NS_ENUM(NSInteger, UIStatusBarStyle) {UIStatusBarStyleDefault = 0, // Dark content,for use on light backgrounds
    UIStatusBarStyleLightContent     NS_ENUM_AVAILABLE_IOS(7_0) = 1, // Light content, for use on dark backgrounds
    
    UIStatusBarStyleBlackTranslucent NS_ENUM_DEPRECATED_IOS(2_0, 7_0, "Use UIStatusBarStyleLightContent") = 1,
    UIStatusBarStyleBlackOpaque      NS_ENUM_DEPRECATED_IOS(2_0, 7_0, "Use UIStatusBarStyleLightContent") = 2,}Copy the code

IOS10 started with the new deprecated macros API_DEPRECATED and API_DEPRECATED_WITH_REPLACEMENT. The former can indicate the reason for discarding, and the latter can indicate the alternative interface.

API_DEPRECATED("No longer supported"API_DEPRECATED, macos (10.4, 10.8)),"No longer supported"Macos (10.4, 10.8), ios(2.0, 3.0), watchos(2.0, 3.0), tvos(9.0, 10.0)) API_DEPRECATED_WITH_REPLACEMENT("-setName:"Tvos (10.0, 10.4) ios(9.0, 10.0) API_DEPRECATED_WITH_REPLACEMENT("SomeClassName", MacOS (10.4, 10.6), Watchos (2.0, 3.0))Copy the code

10. Extra modifiers

10.1 generics

The data types stored in the collection can be declared by adding the generic modifier to objects of the collection type. Such as:

@property (nonatomic, strong) NSMutableArray<NSString *> *myArray;
Copy the code

The compiler will warn you when you add an object of type other than NSString * to myArray.

@property(nonatomic, strong) NSMutableArray<__kindof UIView *> * viewArray;
Copy the code

_kindof is only limited to UIView, so you can store subclasses of UIView, such as UIButton. For more information, see the new power of Objective-C — Nullability, generic collections, and type continuation

10.2 NS_REQUIRES_SUPER

The NS_REQUIRES_SUPER macro is used to declare that a subclass needs to call the parent class’s method when it overloads the parent class’s method. Such as:

- (void)viewWillAppear:(BOOL)animated NS_REQUIRES_SUPER;
Copy the code

10.3 NS_NOESCAPE

NS_NOESCAPE is used to modify block type parameters in methods, such as:

@interface NSArray: NSObject
- (NSArray *)sortedArrayUsingComparator:(NSComparator NS_NOESCAPE)cmptr
@end
Copy the code

Is tells the compiler, CMPTR this block in sortedArrayUsingComparator: returns will be executed before the end, rather than being stored in some time and then executed. An implementation like this:

- (void)performWithLock:(NS_NOESCAPE void (^)())block {  // exposed as @noescape to Swift
    [myLock lock];
    block();
    [myLock unlock];
}
Copy the code

Once the compiler knows this, it makes some optimizations accordingly, such as removing some redundant capture, retain, and release operations on self. Because the block’s lifetime is limited to this method, there is no need to keep self inside the block. For more details, see here.

11. Write a comment

A header file is a document, and you need to quickly let the user know what this class does. A good method name can be quickly understood by the user, but most of the time it requires a comment. Once the formatting comment is written, the comment will appear in the Quick Help bar on the right side of Xcode when the cursor is over the method name and properties. Hold option down and click to pop up the comment box.

11.1 One-line comments

Use // directly on the top line of a method or property declaration, followed by comments, compatible with Xcode and appleDoc. Xcode also supports //! , but appleDoc doesn’t support it.

//SearchManagerBase @interface SearchManagerBase: NSObjectreadonly) NSInteger * state;
@end
Copy the code

11.2 Multi-line comments

Multi-line comments use:

/** Annotate the content */Copy the code

Xcode8 provides shortcuts for quickly generating formatted comments: Option + Command +/. If the method has parameters, the @param keyword is automatically added to describe the corresponding parameters. Apple provides the official headDoc syntax, but much of it is dead in Xcode, and some keywords are incompatible with Apple Doc. Here are some examples of keywords that are still valid in Xcode:

/** demo apple headDoc syntax. @discussion @code // sample code (this is commonly used in Xcode, but appleDoc does not support this keyword) UIView *view; @endCode @bug @note @warning @since iOS7.0 @exception method will throw an exception @attention AppleDoc does not support @author writer @copyright @date date @invariant @POST postcondition @pre condition @Remarks @todo todo text @version Version */ - (void)sampleMethod;Copy the code

In Xcode, it would look like this:

11.3 Enumeration Comments

If you want to comment enumerations, you need to comment them before each enumeration value in the following format:

/// SearchState typedef NS_ENUM(NSInteger,SearchState) {/// no search is started for SearchStateNotSearch, / / / SearchStateSearchFinished search is complete, / / / search failure SearchStateSearchFailed};Copy the code

11.4 Several annotation conventions

What needs to be commented:

  • Try to add a description to your class, even if it’s just a sentence.
  • Specifies default values for certain parameters and attributes, such as timeout time.
  • If the property is KVO compatible, that is, it can be listened on externally using KVO, it is declared in the property comment.
  • The callback block argument needs to specify the thread in which the callback is being called to avoid unnecessary thread judgment in the block.
  • If necessary, specify the preconditions required to use the API to prevent it from being called incorrectly.
  • The API using Method Swizzling is annotated in a unified form to facilitate troubleshooting of runtime bugs.

reference