“The best programmers are the ones who follow the code code.” – Google Code Code

Reference documentation

The specification was developed with the following good Objective-C coding style guidelines in mind:

  • Coding Guidelines for Cocoa
  • raywenderlich.com Objective-C style guide

The code structure

The code structure in the implementation file advocates the following conventions:

  • Use #pragma mark – to group functions or methods by function.

  • The dealloc method is placed at the top of the implementation file.

    This is to remind you to always release relevant resources.

  • Delgate or protocol-specific methods come after the general content.

    #pragma mark - Lifecycle
    
    - (void)dealloc {}
    - (instancetype)init {}
    - (void)viewDidLoad {}
    - (void)viewWillAppear:(BOOL)animated {}
    - (void)didReceiveMemoryWarning {}
    
    #pragma mark - Custom Accessors
    
    - (void)setCustomProperty:(id)value {}
    - (id)customProperty {}
    
    #pragma mark - Protocol conformance
    #pragma mark - UITextFieldDelegate
    #pragma mark - UITableViewDataSource
    #pragma mark - UITableViewDelegate
    
    #pragma mark - NSCopying
    
    - (id)copyWithZone:(NSZone *)zone {}
    
    #pragma mark - NSObject
    
    - (NSString *)description {}
    Copy the code

Code formatting

  • Only space indent, 1 TAB = 4 space characters

    In Xcode->Preferences->Text Editing->Indentation set the following:

    1. Prefer indent using: Select Spaces
    2. Tab Key: Select Intents in Leading Whitespace
    3. Set the number of Spaces to 4 for all fields

    Ps. Set to 4 because Xcode’s default indentation is 4 Spaces. A lot of legacy code is also indented by four Spaces.

  • Suggestion: The length of each line of code cannot exceed 100 characters

    To prevent code from getting too long, and to accommodate typography on the Macbook, each line is limited to 100 characters.

    Check Xcode->Preferences->Text Editing->Editing and set the length to 100 characters to turn on the line width indicator.

    Ps. Google’s 80 characters per line is a bit low and leads to more frequent line breaks, so up to 100 characters.

  • Suggestion: Try to limit the implementation code for a single function or method to 30 lines

    If the implementation code for a function or method is too long, consider splitting the code into smaller methods with a single function.

    Ps.30 lines is exactly how many lines of code for a function can be displayed on a full screen when Xcode uses a 14-point font on a 13-inch macbook.

  • Suggestion: Limit the number of lines of code in a single implementation file to 500-600

    For brevity and ease of reading, it is recommended to limit the number of lines of code in a single implementation file to 500-600.

    When you approach or exceed 800 lines, you should start thinking about splitting the implementation file.

    It is best not to have an implementation file with more than 1000 lines of code.

    We tend to think that the longer the lines of code in a single file, the worse the structure. Besides, code is a soft touch.

    You can use objective-C’s Category feature to divide implementation files into several relatively lightweight implementation files.

    To enable Line numbers prompt, go to Xcode->Preferences->Text Editing->Editing.

  • There must be at least one blank line between function or method implementations in the implementation file

    There are no blank lines, and when the code is too long, it all sticks together, making reading difficult.

    / / prohibited
    - (void)loadView {
    	//load view...
    }
    - (void)viewDidLoad {
    	[super viewDidLoad];
    	
    	//Do Something...
    }
    
    
    / / correct
    - (void)loadView {
    	//load view...
    }
    
    - (void)viewDidLoad {
    	[super viewDidLoad];
    	
    	//Do Something...
    }
    Copy the code
  • When overloading a superclass method, it is necessary to call the superclass method. Leave a blank line between the code calling super and the overloaded code.

    This is done to make it easier to distinguish between calls to super. Typically in the iOS SDK, there are a number of methods that require super to be called when they are reloaded. Sometimes forgetting to call super causes a bug that behaves strangely. Therefore, it is required to separate the code that calls super so that it is easy to read and find if the call to super has been forgotten.

    - (void)viewWillDisappear:(BOOL)animated {
        [super viewWillDisappear:animated];
        
        // An empty line separates the super call from the overloaded code.
        [NSObject cancelPreviousPerformRequestsWithTarget:self];
    }
    Copy the code
  • In the implementation file, the opening curly brace of the function body is not a separate line, and the function name, and keep a space between the function name

    This is intended to be consistent with the code style of the files generated by the Xcode6.1 template.

    / / in favor of
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    // Do not approve of
    - (void)didReceiveMemoryWarning
    {
    
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    Copy the code
  • Elsewhere (if/else/while/switch, etc.), the open curly brace does not start on a separate line. After the code block immediately following the opening curly brace exceeds 5 lines, there must be a blank line between the code block and the parentheses. Code blocks of less than 5 lines may not be blank

    This is intended to be consistent with the code style generated by Xcode automatic code completion.

    / / in favor of
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
        
        if (somethCondtion) {
        
            //DO Something. }}// Do not approve of
    - (void)didReceiveMemoryWarning 
    {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
        
        if (somethCondtion) 
        {
            //DO Something}}Copy the code
  • Suggestion: In if/else, a line break is required between the else and the closing parenthesis of the previous branch statement

    This rule prevents the else from bumping up against the previous branch block and affecting reading, so line breaks are recommended.

    It also makes it easy to quickly locate the ELSE branch after a line wrap.

    / / in favor of
    if (a > 0) {
    
    	//Do Something
    } 
    else {
    
    	//Do Something
    }
    
    // Do not approve of
    if (a > 0) {
    
    	//Do Something
    } else {
    
    	//Do Something
    }
    Copy the code
  • If you need to manually use @synthesize or @dynamic, you can define only one property per line

  • In a method call, if a block argument needs a line break, the curly bracket at the end of the block should be aligned with the first character on the line declaring the block

    [operation setCompletionBlock:^{
        [self.delegate newDataAvailable];
    }];
    Copy the code
  • If part of the code in a method call is too long, resulting in too long indenting of the embedded block code, you can appropriately add manual line breaks to reduce the code indenting

    / / such as the following code before loadWindowWithCompletionBlock manual line breaks, be advocated:
    [[SessionService sharedService]
        loadWindowWithCompletionBlock:^(SessionWindow *window) {
        
            if (window) {
                [self windowDidLoad:window];
            } 
            else{[selferrorLoadingWindow]; }}];Copy the code

annotation

  • Comments should be kept as concise as possible, and code should be self-explanatory as possible

    Except, of course, for the comments used to generate documentation, be as detailed as possible, especially if your interface may have side effects.

  • Comments must be synchronized with the code. Don’t let the code change and the comments don’t update

  • Comments to functions or API interfaces are in the Javadoc style specification

    Because Xcode5 supports generating javadoc-style comments directly into documentation.

    There is also an Xcode plug-in for adding Javadoc style comments: VVDocumenter-Xcode

    Xcode8 has VVDocumenter embedded, shortcut keys: Option + Command + /

Naming conventions

In any case, try to stick to Apple’s naming conventions, especially when it comes to memory management rules

And the memory management rules here emphasize that in the underlying Core Foundation framework, you’re responsible for releasing objects that are returned by functions that have names like Create or Copy.

Class name/class name/protocol

  • Class names, class names, and protocol names are all humped

  • The file name should reflect the name of the class it contains

    For example, nsString. h and nsString. m contain the definition and implementation of the NSString class

  • The filename of a Category should contain the name of the class it extends, and the Category name should try to describe its functionality

    UIImage + Resize. H or UIImage + TintColor. H

  • In application-specific code, avoid using prefixes for class names; using the same prefix for each class can compromise readability

    Application-specific code refers to code that will only be used in one project and will not be used in other projects.

  • In multi-application oriented code, class names are prefixed to prevent naming conflicts

    Multi-application oriented code refers to code that will be used by multiple projects.

    The CRKit library, for example, uses the CR prefix.

  • Suggestion: Use at least three letters for the prefix

    This rule is used to reduce naming conflicts. However, since most popular prefixes are two letters, this article is not mandatory

  • No Spaces are left between the type identifier, protocol name, and Angle brackets in the protocol declaration or definition

    @interface MyProtocoledClass : NSObject<NSWindowDelegate> 
    {
        @private
        id<MyFancyDelegate> _delegate;
    }
    
    - (void)setDelegate:(id<MyFancyDelegate>)aDelegate;
    @end
    Copy the code

methods

  • Both method and parameter names use small camel – shaped naming.

    – (BOOL)isFileExistedAtPath:(NSString *)filePath;

  • In method declarations, there must be one space between -/+ and the return value type, and no space between the method name and parameter type or parameter type and parameter name

    - (void)invokeWithTarget:(id)target; / / right
    - (void)invokeWithTarget: (id)target; / / error
    - (void)invokeWithTarget:(id) target; / / error
    - (void)invokeWithTarget: (id) target; / / error
    Copy the code
  • If there are more than one line of parameters in a method declaration, you can add manual line breaks to make each parameter occupy one line and align them with colons

    - (void)doSomethingWith:(GTMFoo *)theFoo
                       rect:(NSRect)theRect
                   interval:(float)theInterval;
    Copy the code
  • The first paragraph of the method name is shorter than the rest of the method name. Each argument should take one line, and each line should be indented by at least four Spaces. Try to keep the arguments aligned with colons

    Select more than one line of code at a time and use the shortcut keys “command+[” or “Command +]” to reduce or increase indentation.

    - (void)short:(GTMFoo *)theFoo
                  longKeyword:(NSRect)theRect
            evenLongerKeyword:(float)theInterval
                        error:(NSError **)theError; 
    Copy the code
  • Method and parameter names should try to read like one sentence. See Apple’s method naming convention for details

    Such as: convertPoint: fromRect: or replaceCharactersInRange: withString:

  • Do not concatenate method names with “and” when arguments are attributes of the receiver

    / / in favor of
    - (int)runModalForDirectory:(NSString *)path file:(NSString *) name types:(NSArray *)fileTypes;
    
    // Do not approve of
    - (int)runModalForDirectory:(NSString *)path andFile:(NSString *)name andTypes:(NSArray *)fileTypes;
    Copy the code
  • If the method name describes two different actions, use “and” to connect

    - (BOOL)openFile:(NSString *)fullPath withApplication:(NSString *)appName andDeactivate:(BOOL)flag;
    Copy the code
  • The method name of a getter method should be the same as the variable name. The prefix “get” is not allowed

    This rule applies only to Objective-C. C++ uses the relevant specification of C++

    - (id)delegate;		/ / right
    - (id)getDelegate;	/ / is prohibited
    Copy the code
  • Class private methods start with an underscore

    Such as: – (void) _startDownloadFiles;

    There are no really strictly private methods in Objective-C. By “private methods” I mean methods that do not need to be made public and are used only in implementation files.

    The advantage of this is that it is straightforward and quick to distinguish between private and public methods in the implementation file.

    This makes it easy to refactor. If a method is deprecated and needs to be removed and it starts with an underscore, you can be sure that the method is private and will only be used in the implementation file. Simply search the implementation file for the name of the method, and then clean up the search results. You don’t have to look through the entire project to see if it’s not cleaned up.

    According to Apple’s advice, this might override the private methods of the parent class. However, this has not been the case so far, and we believe that the benefits of this agreement far outweigh its potential dangers, so we are still going ahead with this agreement.

function

Functions refer to pure C functions, and an Apple-style convention is advocated here.

  • Function names are named in large camel shape, and parameter names are named in small camel shape

  • If a function is associated with a particular type, the function name prefix is the same as the type prefix

    Such as CGRectMake() and CGContextCreate()

constant

  • Use the Literals syntax when creating constants such as NSString, NSDictionary, NSArray, and NSNumber

    / / such as:
    NSArray *names = @[@"Brian".@"Matt".@"Chris".@"Alex".@"Steve".@"Paul"];
    NSDictionary *productManagers = @{@"iPhone" : @"Kate".@"iPad" : @"Kamal".@"Mobile Web" : @"Bill"}; 
    NSNumber *shouldUseLiterals = @YES; 
    NSNumber *buildingZIPCode = @10018; 
    
    // Instead of:
    NSArray *names = [NSArray arrayWithObjects:@"Brian".@"Matt".@"Chris".@"Alex".@"Steve".@"Paul".nil]; 
    NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @"Kate".@"iPhone".@"Kamal".@"iPad".@"Bill".@"Mobile Web".nil]; 
    NSNumber *shouldUseLiterals = [NSNumber numberWithBool:YES]; 
    NSNumber *ZIPCode = [NSNumber numberWithInteger:10018];
    Copy the code
  • When defining enumeration constants, use NS_ENUM or NS_OPTIONS

    Both NS_ENUM and NS_OPTIONS provide type checking

    / / such as:
    typedef NS_ENUM(NSUInteger, PPNavBarButtonColor) {
        PPNavBarButtonColorBlack,
        PPNavBarButtonColorGreen,
        PPNavBarButtonColorDefault = PPNavBarButtonColorBlack
    };
    
    typedef NS_OPTIONS(NSUInteger, PSTCollectionViewScrollPosition) {
        PSTCollectionViewScrollPositionNone                 = 0,
    	
        PSTCollectionViewScrollPositionTop                  = 1 << 0,
        PSTCollectionViewScrollPositionCenteredVertically   = 1 << 1,
        PSTCollectionViewScrollPositionBottom               = 1 << 2,
    	
        PSTCollectionViewScrollPositionLeft                 = 1 << 3,
        PSTCollectionViewScrollPositionCenteredHorizontally = 1 << 4,
        PSTCollectionViewScrollPositionRight                = 1 << 5
    };
    Copy the code
  • When defining constants, use const over #define unless you explicitly need to use constants as macros

  • Static is used for constants that are only used in a particular file

    The static keyword ensures that variables have only file scope and avoids link errors caused by variable names.

    Such as:

    Static CGFloat const RWImageThumbnailHeight = 50.0;

  • Constant names begin with a lowercase K and are capitalized to separate words

    / / such as:
    const int kNumberOfFiles = 12;
    NSString *const kUserKey = @"kUserKey";
    enum DisplayTinge {
    	kDisplayTingeGreen = 1,
    	kDisplayTingeBlue = 2
    };
    Copy the code
  • Enumeration constants associated with a particular type are prefixed with the class name instead of starting with a lowercase K.

    typedef NS_OPTIONS(NSUInteger.UICollectionViewScrollPosition) {
        UICollectionViewScrollPositionNone                 = 0.UICollectionViewScrollPositionTop                  = 1 << 0.UICollectionViewScrollPositionCenteredVertically   = 1 << 1.UICollectionViewScrollPositionBottom               = 1 << 2.UICollectionViewScrollPositionLeft                 = 1 << 3.UICollectionViewScrollPositionCenteredHorizontally = 1 << 4.UICollectionViewScrollPositionRight                = 1 << 5
    };
    Copy the code

variable

  • Both attribute and variable names use small camel – shaped naming

  • Instance variable names cannot start with an underscore, and local variables cannot start with an underscore

  • It is forbidden to name variables using Hungarian notation or vague abbreviations

    The exceptions are I, j, and k in the for loop.

    In Objective-C, the name of a variable should be as clear as possible about its purpose. This will make the code immediately clear to others, and don’t worry about making the code too long.

    // These are all wrong naming conventions
    int w;
    int nerr;
    int nCompConns;
    tix = [[NSMutableArray alloc] init];
    obj = [someObject object];
    p = [network port];
    
    // These are the preferred naming conventions
    int numErrors;
    int numCompletedConnections;
    tickets = [[NSMutableArray alloc] init];
    userInfo = [someObject object];
    port = [network port];
    Copy the code
  • The pointer symbol “*” is near the variable name. (Except for constant definitions)

    NSString *varName; / / in favor of
    
    NSString* varName; // Do not approve of
    Copy the code
  • When using property, the dot syntax is preferred

    Using dot syntax makes code concise. But for all other cases, square brackets syntax should be used.

    / / in favor of
    NSInteger arrayCount = [self.array count];
    view.backgroundColor = [UIColor orangeColor];
    [UIApplication sharedApplication].delegate;
    
    // Do not approve of
    NSInteger arrayCount = self.array.count;
    [view setBackgroundColor:[UIColor orangeColor]];
    UIApplication.sharedApplication.delegate;
    Copy the code

Notifications and exceptions

  • Notify name naming rules: [associated class name] + [Did | Will] + [unique a name] + Notification

    Such as: UIApplicationDidBecomeActiveNotification

  • Exception names are named as follows: [prefix] + [unique segment name] + Exception

    Such as: NSColorListIOException

Boolean value

  • Objective-c booleans use only YES and NO

  • True and false can only be used in CoreFoundation, C, or C++ code

  • Disallow comparing the result of a value or expression with YES

    Because BOOL is defined as signed char. This means that it could be other values besides YES(1) and NO(0).

    So non-zero being true in C or C++ does not necessarily mean YES

    // The following are prohibited
    - (BOOL)isBold {
        return [self fontTraits] & NSFontBoldTrait;
    }
    
    - (BOOL)isValid {
        return [self stringValue];
    }
    
    if ([self isBold] == YES) {
    	/ /...
    }
    
    
    // The following is the way to approve
    - (BOOL)isBold {
        return ([self fontTraits] & NSFontBoldTrait)?YES : NO;
    }
    
    - (BOOL)isValid {
        return [selfstringValue] ! =nil;
    }
    
    - (BOOL)isEnabled {
        return [self isValid] && [self isBold];
    }
    
    if ([self isBold]) {
    	/ /...
    }
    Copy the code
  • Although nil will be interpreted as NO directly, it is advisable to keep the comparison to nil for conditional judgments because it makes the code more intuitive.

    // For example, more intuitive code
    if(someObject ! =nil) {
    	/ /...
    }
    
    // Less intuitive code
    if(! someObject) {/ /...
    }
    Copy the code
  • In C or C++ code, be aware of NULL pointer detection.

    Sending a message to a nil Objective-C object does not cause a crash. But since the Objective-C runtime does not handle NULL pointer cases, NULL pointer detection for C/C++ needs to be handled to avoid crashes.

  • If the property name of a BOOL is an adjective, it is recommended to alias the getter method with an “is”.

    @property (assign.getter = isEditable) BOOL editable;
    Copy the code
  • In method implementations, if there is a block argument, be careful to detect if the block argument is nil.

    - (void)exitWithCompletion:(void(^)(void))completion {// error. If completion is passed nil when this method is called externally, EXC_BAD_ACCESS completion() occurs here; / / correct. If Completion does not exist, it is not called. if (completion) { completion(); }}Copy the code

Conditional statements

  • The body of a conditional statement, even if it is only one line, must not omit the parenthesis

    This will reduce mistakes. For example, if you add a second line to the body of an if statement, the second line may not be included in the body of an if statement because there are no curly braces. In addition, there are other dangerous situations mentioned here.

    / / in favor of
    if (error == nil) {
      return success;
    }
    
    // Do not approve of
    if (error == nil)
      return success;
      
    / / or
    if (error == nil) return success;
    Copy the code
  • Multi-layer nested conditional statements, preferentially if the condition is not true and can be jumped immediately

    Objective-c code is generally long, and if you add multiple nested conditional statements, the indenting will increase, the code will become longer, and the readability will be affected. For example, in the following case, the indent length can be effectively reduced by changing the priority to jump out:

    // General process
    if (a) {
    
        if (b) {
        
            if (c) {
            
            	//do something}}}// Prioritize processes that can escape
    if(! a) {return;
    }
    
    if(! b) {return;
    }
    
    if(! c) {return;
    }
    
    //do something
    Copy the code
  • Ternary arithmetic is recommended only if it increases clarity and cleanliness of code

    The trinary operator (? Use caution if it does not increase code cleanliness and clarity. In particular, nesting multiple ternary operations should be avoided. Because it makes the code harder to read.

    In addition, the conditional judgment in the ternary operator is a statement, best enclosed in parentheses. No parentheses are required if the value is directly a Boolean. Such as:

    / / in favor of
    NSInteger value = 5; result = (value ! =0)? x : y;BOOL isHorizontal = YES;
    result = isHorizontal ? x : y;
    
    // Do not approve of
    result = a > b ? x = c > d ? c : d : y;
    Copy the code

Initialization method

  • The return type of the initialization method is instanceType

    See NSHipster.com for an introduction to InstanceType.

CGRect function

  • When accessing x, Y, width, or height elements in CGRect, the CGGeometry correlation function is used instead of directly accessing them

    The function inside CGGeometry will implicitly standardize the CGRect parameters and then compute the results. Therefore, you should avoid directly reading or overwriting values in CGRect data structures, and instead use these functions for related operations.

    Refer to the Overview section of CGGeometry Reference for standardization.

    / / in favor of
    CGRect frame = self.view.frame;
    
    CGFloat x = CGRectGetMinX(frame);
    CGFloat y = CGRectGetMinY(frame);
    CGFloat width = CGRectGetWidth(frame);
    CGFloat height = CGRectGetHeight(frame);
    CGRect frame = CGRectMake(0.0.0.0, width, height);
    
    // Do not approve of
    CGRect frame = self.view.frame;
    
    CGFloat x = frame.origin.x;
    CGFloat y = frame.origin.y;
    CGFloat width = frame.size.width;
    CGFloat height = frame.size.height;
    CGRect frame = (CGRect){ .origin = CGPointZero, .size = frame.size };
    Copy the code

Xcode engineering structure

  • The physical files should be synchronized with the Xcode project files to prevent file inconsistencies

    Any manually created Xcode Group should have a corresponding folder on the file system. Not only should your code be organized by type, but it should be organized with clearer characteristics.

  • Suggestion: Whenever possible, always check the Treat Warnings as Errors option in the Build Settings. Expose as many additional warnings as possible. To ignore a specific type of Warning, use Clang’s pragma feature.

    This rule is not mandatory, but “treating warnings as errors” is an attitude you should have.

Just for fun

And finally, just for fun, while long names are a virtue in Objective-C, you have to have a certain degree. Someone wrote a script that counted the longest names in Cocoa Framework and found that they underestimated the sentence making abilities of Apple programmers. Mac platform’s longest constant name 96 characters, the longest method name 150 characters, C function names can reach 68 characters! -_-# Peat, since I learned Objective-C, my mom doesn’t have to worry about my ability to make sentences anymore.