Summarize some of the specifications of the interface declaration, the introduction of the relevant macro, define the method useful modifiers, write the specification of the annotations, and finally write a qualified header file.
- 1. Read and Write permissions
- 1.1 Instance variable @public, @protected, @private keyword
- The Readonly,readwrite keyword for the 1.2 property
- 2. Forward statement
- 3. Exposing only the necessary interfaces and implementations
- 3.1 Do not expose any private methods that are only used inside the class
- 3.2 Do not declare protocol in the header file that the class is following
- 4.nullability description
- 5. Defining enumerations
- 5.1 Ns_enum
- 5.2 Ns_options
- 5.3 String Enumeration
- 6. Use extern to provide read-only constants externally
- 7. Provide private properties of the parent class to subclasses and categories
- 8. Mark designated initializer
- 9.API version Control
- 9.1 Available
- 9.2 Unavailable
- 9.3 Deprecated
- 10. Additional Modifiers
- 10.1 generic type
- 10.2 Ns_requires_super
- 10.3 Ns_noescape
- 11. Write a comment
- 11.1 Single-line comment
- 11.2 Multi-line comments
- 11.3 Enumeration Comments
- 11.4 Several annotation conventions
1. Read and Write permissions
The declarations in the. h file are for exposing to external interfaces, while private methods, private properties, and instance variables inside the class should be placed in the interface extension of the. m file.
1.1 Instance variable @public, @protected, @private keyword
These 3 keywords are used to decorate an instance variable and cannot be used to decorate a property. When an instance variable is used incorrectly, Xcode prompts you for an error.
Key Words |
Description |
@private |
The scope can only be in its own class |
@protected |
The scope is in its own class and inherits its own subclass, nothing is written, the default is this property. |
@public |
The scope of the action is the largest, anywhere. |
Example code:
//SearchManager.h@interface SearchManager : NSObject { @public NSInteger *state; @public NSInteger *timeout; @protected id *searchAPI; @private id _privateIvar;}@end
Because private variables are exposed and there are no high-level keywords for @property, instance variables are rarely declared in the header file. Prefer to use @property.
The Readonly,readwrite keyword for the 1.2 property
The attributes in the header file are a collection of attributes that describe the object. When declaring @property, use ReadOnly in. h to allow external read-only permissions and use ReadWrite in. m to have read and write access to the internal.
Example code:
readonly) NSInteger * state;@end
//SearchManager.m@interface SearchManager : NSObject@property (nonatomic, readwrite) NSInteger * state;@end
2. Forward statement
When other classes are used in the @interface interface, do not import the header files of the class directly in. h, which will allow the use of this header file to also import these unnecessary other header files. The correct approach is @class
to use the keyword to make a forward declaration. Of course, if you inherit the parent class, you still need to import the parent class's header file. Example code:
//SearchManager.h#import "SearchManagerBase.h"//导入父类的头文件@class LocationModel;//前向声明LocationModel类typedef void(^LocationSearchCompletionHandler)(LocationModel *location, NSError *error);@interface LocationSearchManager : SearchManagerBase- (void)searchLocationWithKeyword:(NSString *)keyword completionHandler:(LocationSearchCompletionHandler)completionHandler;@end
Using @class tells the compiler that a class exists, but it does not care about the specific implementation of the class, until the caller uses it in. m to import the class. Declare a class and a protocol, using @class and @protocol, respectively. There are two reasons to use a forward reference:
- Improve compilation efficiency. If import is in place
LocationModel.h
, LocationModel.h
all import will LocationModel.h
need to be recompiled when the content changes. If. M is referenced SearchManager.h
, but not used LocationModel
, it increases unnecessary compilation and reduces development efficiency.
- Resolve cross-reference issues. If the header file of Class A imports B, the header file of Class B imports a, which causes an error at compile time: "Can not find interface declaration", because OBJECTIVE-C does not allow cross-references.
3. Expose only the necessary interfaces and implementations 3.1 do not expose any private methods that are used only within the class
The header file only declares those public methods that are used externally, and is designed with testability in mind and a single duty. Private methods are defined only within the class, and for the sake of differentiation, it is recommended to prefix the private method, for example - (void)p_myPrivateMethod
. Because Apple declares in its code specification that Apple has an underlined method prefix, as it NS
UI
does with these class name prefixes, it is not recommended that our private methods use underscores directly as prefixes. Otherwise, when you inherit the class of cocoa touch, it is possible to overwrite the private method of the parent class, causing errors that are difficult to debug.
3.2 Do not declare protocol in the header file that the class is following
Example code for the error :
readonly) NSInteger * state;@end
UITableViewDelegate
It is the protocol that is used within the class and is not necessarily exposed to the outside, so it should be placed in the. m file. The NSCoding
characteristics of the class are described to tell the external class that the archive can be used, so it should be placed in the header file.
4.nullability description
When declaring, you can use the following keywords to describe whether an object can be nil.
Key Words |
Description |
Nullable |
Nullable, used to describe the OBJC object |
Nonnull |
Not nullable, used to describe the OBJC object |
Null_unspecified |
Indeterminate, used to describe the OBJC object |
Null_resettable |
Set is nullable, get is not empty. For property only |
_nullable |
Nullable, used to describe the C pointer and block |
_nonnull |
Not NULL, used to describe the C pointer and block |
_null_unspecified |
Indeterminate, used to describe the C pointer and block |
Example 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
If you empty a value with a nonnull adornment, the compiler gives a warning. At development time, most of the time, nonnull is used, so Apple provides a pair of macros NS_ASSUME_NONNULL_BEGIN
and a NS_ASSUME_NONNULL_END
quick retouching, a property written in two macros, and a method that uses nonnull
adornments. Example code:
//LocationSearchManager.h#import "SearchManagerBase.h"@class LocationModel;NS_ASSUME_NONNULL_BEGINtypedef void(^LocationSearchCompletionHandler)(LocationModel *_Nullable location, NSError *_Nullable error);@interface LocationSearchManager : SearchManagerBase- (void)searchLocationWithKeyword:(NSString *)keyword completionHandler:(LocationSearchCompletionHandler)completionHandler;@endNS_ASSUME_NONNULL_END
5. Defining enumerations
For the difference between Ns_enum and Ns_options, refer to here. In simple terms, Ns_options provides a bitmask-based feature.
5.1 Ns_enum
Example code:
typedef NS_ENUM(NSInteger,SearchState) { SearchStateNotSearch, SearchStateSearching, SearchStateSearchFinished, SearchStateSearchFailed};
5.2 Ns_options
Sample code, reference NSKeyValueObserving.h
:
typedef NS_OPTIONS(NSUInteger, NSKeyValueObservingOptions) { NSKeyValueObservingOptionNew, NSKeyValueObservingOptionOld, NSKeyValueObservingOptionInitial, NSKeyValueObservingOptionPrior};
When used, you can |
combine multiple option:
forKeyPath:@"title" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:NULL];
5.3 String Enumeration
When a dictionary is passed as a parameter, or as a return value, it is often difficult to provide a dictionary key directly, and now a string enumeration is used to solve the problem. Sample code, reference NSKeyValueObserving.h
:
//使用NS_STRING_ENUM宏,定义了一个枚举类型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;//使用泛型,声明了change参数用到的key,是在NSKeyValueChangeKey的枚举范围中- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context;
6. Use extern to provide read-only constants externally
It's not about @interface, but it's all about the head file.
//SearchManager.hextern NSString *const SearchErrorDomain;extern NSInteger SearchDefaultTimeout;@interface SearchManager : NSObject@end
//SearchManager.mNSString *const SearchErrorDomain = @"SearchErrorDomain";const NSInteger SearchDefaultTimeout = 20;@interface SearchManager()@end
7. Provide private properties of the parent class to subclasses and categories
Because the header files of the class only store those properties and methods that are exposed to the external, you encounter obstacles when you encounter these situations:
- In subclasses or categories, you want to use the parent class to define a private property in. M.
- In the header file of the class, the attribute is readonly, but in the subclass or category, the ReadWrite permission is required. Since these properties are not exposed in the header file, it is necessary to create a separate private header file to hold the attributes that need to be exposed to subclasses and categories. You can refer to Apple's official
UIGestureRecognizerSubclass.h
. Example code:
readonly) SearchState state;@end
//SearchManager.m@interface SearchManager()///内部使用,有读写权限@property (nonatomic, assign) SearchState state;///只在内部使用的私有属性@property (nonatomic, strong) id searchAPI;@end
///private properties and private methods that are exposed to subclasses and categories//searchmanagerinternal.h///restrict the use of this header file, Prevent misuse by other classes #ifdef searchmanager_protected_access #import " SearchManager.h "@interface Searchmanager ()///In internal.h, re-declared as ReadWrite permission @property (nonatomic, ReadWrite, assign) Searchstate state;///exposes private properties @property (Nonatomic, strong) ID searchapi;///exposes private methods-(void) P_privatemethod; @end #else #error only being included by Searchmanager ' s subclass or category!< Span class= "hljs-comment" > #endif
///category的实现文件//SearchManager+Category.m///声明私有头文件的使用权限#define SEARCHMANAGER_PROTECTED_ACCESS///导入私有头文件#import "SearchManagerInternal.h"@implementation SearchManager(Category)- (void)categoryMethod { //拥有了读写权限 self.state = SearchStateSearching; //可以访问私有属性 [self.searchAPI startSearch]; //可以使用私有方法 [self p_privateMethod];}@end
SearchManagerInternal.h
In fact, it is also public, other classes can be imported and used, only in the development of the contract. If you want to restrict the import of other classes and prompt for errors, Internal.h
you can use the following methods:
#ifdef MYCLASS_PROTECTED_ACCESS//声明部分#else#error Only be included by MYCLASS‘s subclass or category!#endif
This generates a compilation warning if it is accidentally imported in another class Internal.h
and cannot be used directly. The disadvantage is that it needs to be used everywhere Internal.h
#define MYCLASS_PROTECTED_ACCESS
.
8. Mark designated initializer
Specifies the initialization method, which is the initialization method that receives the most parameters, which is called by other initialization methods, and is designed to ensure that the instance variables are initialized correctly by all initialization methods. Add a macro to the back of the method NS_DESIGNATED_INITIALIZER
. Thus, when you subclass this class, the compiler gives a warning if the designated initializer of the parent class is not correctly called in the initialization method of the subclass. Instance code:
@interface WKWebView : UIView- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration NS_DESIGNATED_INITIALIZER;- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;@end
For more detailed instructions on designated initializer, refer to:
- OBJECTIVE-C Supplements: Designated initializer
- Several principles of correctly writing designated initializer
9.API version Control
When updating an interface, or developing the framework, it is necessary to indicate the version information that informs the user of the platform limitations of this interface, the operating system version, whether it is available, whether it has been deprecated, and so on. Apple gives a few of its own macros to mark the version, and Xcode warns you when it detects an error. Just add the corresponding macro to the method name.
9.1 Available
Declares the minimum supported operating system version of this interface. When your interface uses the APIs of the new system, such as the uialertcontroller above iOS8, but the deployment target of the project is iOS7, the version information of this interface needs to be marked to allow the user to be compatible. Example:
//SearchManager.htypedef NS_ENUM(NSInteger,SearchState) { SearchStateNotSearch, SearchStateSearching, SearchStateSearchFinished, SearchStateSearchFailed} NS_ENUM_AVAILABLE_IOS(2_0);//此枚举在iOS2.0以上才能使用NS_CLASS_AVAILABLE_IOS(2_0) //此类在iOS2.0以上才能使用@interface SearchManager : NSObject- (void)reSearch NS_AVAILABLE_IOS(5_0);//此方法在iOS5.0以上才能使用@end
These macros have a corresponding version of the platform, such as Ns_available_mac, Ns_available_ios, Ns_available_iphone. IOS10 has started to provide a new available macro API_AVAILABLE
that unifies MacOS, IOS, WatchOS, tvOS, and several platforms.
API_AVAILABLE(macos(10.10))API_AVAILABLE(macos(10.9), ios(10.0))API_AVAILABLE(macos(10.4), ios(8.0), watchos(2.0), tvos(10.0))
9.2 Unavailable
Declares that this interface is not available, and most of the time it is used to declare the platform limit. Example:
@interface SearchManager : NSObject- (void)searchInWatch NS_UNAVAILABLE;//不能用此接口- (void)searchInHostApp NS_EXTENSION_UNAVAILABLE_IOS;//extension里不能用此接口- (void)search __TVOS_PROHIBITED;//tvOS里不能用此接口,可修饰枚举,类,方法,参数@end
IOS10 begins with the new unavailable macro API_UNAVAILABLE
:
API_UNAVAILABLE(macos)API_UNAVAILABLE(watchos, tvos)
9.3 Deprecated
Declares that this interface has been deprecated and can be annotated with an alternate interface. Xcode gives a warning when the deployment target version number is set to a version number that is greater than or equal to the deprecated method. Example:
"UIAlertView is deprecated. Use UIAlertController with a preferredStyle of UIAlertControllerStyleAlert instead")@interface UIAlertView : UIView@end
//注明废弃API@interface UIViewController : UIResponder- (void)viewDidUnload NS_DEPRECATED_IOS(3_0,6_0);@end
//注明废弃枚举typedef NS_ENUM(NSInteger, UIStatusBarStyle) { UIStatusBarStyleDefault 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,}
IOS10 begins with the new deprecated macros API_DEPRECATED
and API_DEPRECATED_WITH_REPLACEMENT
. The former can indicate the reason for abandonment, the latter can indicate an alternative interface.
API_DEPRECATED("No longer supported", macos(10.4, 10.8))API_DEPRECATED("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))
10. Additional Modifier 10.1 generics
When declaring, adding a generic adornment to an object of the collection type, you can declare the data type stored in the collection. For example:
@property (nonatomic, strong) NSMutableArray<NSString *> *myArray;
When you myArray
put a non- NSString *
type object in, the compiler gives a warning.
@property(nonatomic, strong) NSMutableArray<__kindof UIView *> * viewArray;
_kindof
Subclasses that are only qualified for storage types UIView
, and therefore can also be stored, UIView
for example UIButton
. More detailed introduction, reference: New charm of objective-c language--nullability, generic set and type extension
10.2 Ns_requires_super
NS_REQUIRES_SUPER
Macros are used to declare subclasses when this method of the parent class is overloaded, the method of the parent class needs to be called. For example:
- (void)viewWillAppear:(BOOL)animated NS_REQUIRES_SUPER;
10.3 Ns_noescape
NS_NOESCAPE
Used to modify the block type parameters in the method, for example:
@interface NSArray: NSObject- (NSArray *)sortedArrayUsingComparator:(NSComparator NS_NOESCAPE)cmptr@end
The function is to tell the compiler that cmptr
the block sortedArrayUsingComparator:
will be executed before the method returns, rather than being saved and executed at some point later. Similar to this implementation:
- (void)performWithLock:(NS_NOESCAPE void (^)())block { // exposed as @noescape to Swift [myLock lock]; block(); [myLock unlock];}
Once the compiler knows, it will do some optimizations, such as removing some extra pairs self
of captures, retain, and release operations. Because the block's survival is limited to this method, there is no need to retain it within the block self
. For a more detailed introduction, refer here.
11. Write a comment
A header file is a document that requires users to quickly know what the class does. A good method name can be quickly understood by the user, but most of the time it requires a corresponding comment. Once you've written a formatted comment, when the cursor rests on the method name and property, the comment appears in the quick Help bar on the right side of Xcode, and the option
comment box pops up when you press and hold and click.
11.1 Single-line comment
Used directly on the previous line of a method or property declaration ///
, followed by annotations, and compatible with Xcode and Appledoc. Xcode is also supported //!
, but Appledoc is not supported.
readonly) NSInteger * state;@end
11.2 Multi-line comments
Multi-line annotations are used:
/** 注释内容*/
XCODE8 provides shortcut keys for quickly generating formatted comments: option
+ command
+ /
. If the method has parameters, the @param keyword is added automatically to describe the corresponding parameter. Apple provides the official Headdoc syntax, but many of them have been invalidated in Xcode, and some keywords are incompatible with appledoc. Here are a few of the keywords that are still valid in Xcode:
/** 演示苹果headDoc的语法。这里可以写方法简介 @brief 方法的简介(appleDoc不支持此关键字) @discussion 方法的详细说明 @code //示例代码(这个在Xcode里常用,但是appleDoc不支持此关键字) UIView *view; @endcode @bug 存在的bug的说明 @note 需要注意的提示 @warning 警告 @since iOS7.0 @exception 方法会抛出的异常的说明 @attention 注意,从这里开始往下的关键字,appleDoc都不支持 @author 编写者 @copyright 版权 @date 日期 @invariant 不变量 @post 后置条件 @pre 前置条件 @remarks 备注 @todo todo text @version 版本 */- (void)sampleMethod;
In Xcode, you'll see this:
11.3 Enumeration Comments
If you want to give an enumeration comment, you need to comment before each enumeration value, in the following format:
///搜索状态typedef NS_ENUM(NSInteger,SearchState) { ///没有开始搜索 SearchStateNotSearch, ///搜索中 SearchStateSearching, ///搜索结束 SearchStateSearchFinished, ///搜索失败 SearchStateSearchFailed};
11.4 Several annotation conventions
What you need to comment on:
- Try to add a description to the class, even if there is a sentence.
- Indicate the default values for some parameters and properties, such as time-outs.
- If the property is KVO compatible, that is, externally you can use KVO to listen to this property, which is declared in the property comment.
- The callback block parameter needs to describe the thread in which the callback is located, avoiding the need for the user to make redundant thread judgments in the block.
- If necessary, describe the preconditions that are required to use this API to prevent them from being called incorrectly.
- The use of method Swizzling API for uniform form of labeling, easy to encounter runtime bug when troubleshooting.
Reference
- blog.sunnyxx.com/2014/04/13/...
- blog.sunnyxx.com/2015/06/12/...
[email protected] 11 standard notation