美文网首页OC进阶
Objective-C 代码规范

Objective-C 代码规范

作者: kim4apple | 来源:发表于2019-07-18 11:14 被阅读1次
    版本 内容 修订人 时间
    0.1.0 草稿 黄鑫 2018/06/11
    0.2.0 修改文档组织 黄鑫 2018/06/16

    0. 前言


    "代码是写给人看的"

    例子🌰

    //
    //  M2User.h
    //  M2API
    //
    //  Created by Kim on 2018/06/11.
    //
    
    // 头文件引入
    #import <Foundation.h>
    
    #import "M2Defines.h"
    
    
    // 常量定义
    FOUNDATION_EXPORT NSString * const M2UserErrorDomain;
    
    /**
      性别枚举 
    */
    typedef NS_ENUM(NSUInteger, M2Gender) {
        M2GenderUnknow = 0,     //!< 未知
        M2GenderMale,           //!< 男性
        M2GenderFemale          //!< 女性
    };
    
    
    /** 用户  */
    @interface M2User : NSObject
    
    @property (nonatomic, readonly, copy) NSString *name;       //!< 名字
    @property (nonatomic, readonly, assign) NSUInteger age;     //!< 年龄
    @property (nonatomic, readonly, assign) M2Gender gender;    //!< 性别
    
    /**
      初始化
      
      @param name 用户名
      @param age 年龄
      @param gender 性别  
    */
    + (instancetype)userWithName:(NSString * __Nonnull)name 
                             age:(NSUInteger)age 
                          gender:(M2Gender)gender;
    
    - (instancetype)initWithName:(NSString * __Nonnull)name
                             age:(NSUInteger)age
                          gender:(M2Gender)gender;
    
    @end
    
    
    // 实现 
    
    @implementation M2User
    
    + (instancetype)userWithName:(NSString * __Nonnull)name 
                             age:(NSUInteger)age 
                          gender:(M2Gender)gender {
        return [[self alloc] initWithName:name age:age gender:gender];                               
    }
    
    - (instancetype)initWithName:(NSString * __Nonnull)name 
                             age:(NSUInteger)age 
                          gender:(M2Gender)gender {
        if (self = [super init]) {
            _name = name;
            _age = age;
            _gender = gender;
        }
        
        return self;
    }
    
    @end
    
    

    1. 布局与风格


    良好布局的目的

    • 准确表现代码的逻辑结构
    • 始终如一地表现代码的逻辑结构
    • 改善可读性
    • 经得起修改

    布局技术

    • 分组 从另一个角度看,空白也是分组,也是确保相关到语句组成放在一起。
    • 空行 是指示一个程序如何组织的手段。可以用空行将相关语句各自划分成段落,分开各个子程序,突出注释部分。
    • 缩进 使用缩进形式显示程序的逻辑结构

    “当程序有两到四个空格的缩进时,受试者对程序的理解分数会比毫无缩进的程序高出20%到30%。”

    — 《程序缩进和可理解性》

    2. 代码组织


    Objective-C的类通常分成头文件和实现文件。

    头文件

    头文件通常包含:

    • 文件说明与版权
    • 头文件引入
    • 宏定义
    • 常量定义
    • 类型前置声明
    • 块类型定义
    • 枚举定义
    • 函数定义
    • 协议定义
    • 类定义 - 类定义通常包含
      • 类方法。
      • 属性。
      • 公开方法。
    • 分类定义
      • 分类方法。

    🚧 注意:内容排列顺序与上面一致。

    如下面的头文件模板所示。按照下面的顺序定义。

    // 文件说明与版权
    //
    //  M2API2Client.h
    //  M2API
    //
    //  Created by Kim on 2017/11/11.
    //  Copyright (c) 2017 Kim Studio. All rights reserved.
    //
    
    // 头文件引入 (见下文说明)
    #import <Foundation.h>
    
    #import "M2APIClient.h"
    
    
    // 宏定义 (见下文说明。必须是才使用宏)
    #define M2_DEBUG  0
    #define M2_TEST   1
    
    // 常量定义
    FOUNDATION_EXPORT NSString * const M2UserErrorDomain;
    
    // 类型前置声明
    @class User;
    
    // 类型定义
    typedef NSString * const M2APIHTTPMethod;
    
    // Block类型定义
    typedef void (^M2APISuccessBlock)(id response);
    typedef void (^M2APIFailureBlock)(NSError *error);
    
    // 枚举定义
    typedef NS_ENUM(NSUInteger, M2DirectionType) {
        M2DirectionTypeUnknown = 0,
        M2DirectionTypeTop,
        M2DirectionTypeLeft,
        M2DirectionTypeButtom,
        M2DirectionTypeRight
    };
    
    // 协议定义
    @protocol M2LoginViewDelegate : NSObject
    
    @end
    
    // 类定义
    @interface M2User : NSObject
    
    @property (nonatomic, readonly, copy) NSString *name;
    
    @end
        
    // 分类定义
    @interface M2User <M2Debug>
    
    - (NSString *)debugInfo;
        
    @end
    

    📝 通常使用文件模板

    版权

    文件头中增加版权信息。

    //
    //  M2API2Client.h
    //  M2API
    //
    //  Created by Kim on 2017/11/11.
    //  Copyright (c) 2017 Kim Studio. All rights reserved.
    //
    

    头文件引入

    头文件引入规则顺序:

    • 系统库
    • 第三方库
    • 工程内类引入

    注意:

    1. 系统库/第三方库与工程内类引入直接的分组空行
    2. 工程内类如果有多个头文件引入,也可以增加空行按功能进行分组。
    //
    //  M2API2Client.h
    //  M2API
    //
    //  Created by Kim on 2017/11/11.
    //  Copyright (c) 2017 Kim Studio. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    #import <AFNetworking/AFNetworking.h>
    #import <CocoaLumberjack/CocoaLumberjack.h>
    #import <AFHTTPSessionManagerLogger.h>
    
    #import "M2APIConfiguration.h"
    #import "M2APIHTTPSessionManager.h"
    
    

    实现文件

    实现文件通常包含:

    // 文件描述
    
    // 头文件引入
    
    // 常量定义
    
    // 文件内私有类定义
    
    // 类私有方法定义
    
    // 类实现
    

    类定义

    一般类定义组成如下:

    • 协议
      • 成员变量
      • 属性
      • 类方法
      • 构造函数
      • 公开方法
      • 控件响应函数
      • 通知响应函数
      • 委托方法
      • 私有方法

    空行与注释:

    1. 协议之间留2行空行。
    2. @protocol/@interface第一个属性方法后,空1行
    3. 最后一个方法@end之间空1行
    4. 类最后空1行
    5. 属性第一个方法之间空1行
    6. 属性如果有长注释,则空1行。
    7. 属性如果使用短注释,则在属性后使用//!<进行注释。
    8. 如果方法定义有注释,则空1行
    9. 如果方法定义没有注释,则可以不留空行进行分组。
    10. 方法分组之间,空1行
    11. 函数定义的注意点,见后面函数一节
    ////// 头文件说明
    //
    //  M2APIUserClient.h
    //  M2UserAPI
    //  Created by Kim on 2018/06/10
    //  Copyright (c) 2017 Kim Studio. All rights reserved.
    
    ////// 头文件引入
    #import <M2HTTPClient.h>
    #import "M2APIUser.h"
    
    ////// 协议定义
    @protocol M2APIUserEndPoint <NSObject>
    
    /**
      登陆
      
      @param user 用户名
      @param password 密码
      
      @return 信号 M2APIUser
     */
    - (RACSignal *)loginWithUser:(NSString *)user password:(NSString *)password;
    
    /**
      获取用户信息
      
      @return 信号 用户信息
     */
    - (RACSignal *)userInfo;
    
    @end
    
    /////// 类定义
    /**
      用户模块客户端
     */
    @interface M2APIUserClient : M2HTTPClient <M2APIUserEndPoint>
    
    @property (nonatomic, strong) M2APIConfiguration *configuration; //!< 配置消息
    @property (nonatomic, strong) M2APISigner *signer; //!< 签名类
    
    + (instancetype)sharedClient;
    + (instancetype)clientWithConfiguration:(M2APIConfiguration *)configuration;
    - (instancetype)initWithConfiguration:(M2APIConfiguration *)configuration;
    
    @end
    

    类实现

    类实现内部组织 使用#pargma mark -来分割功能组。一个典型的ViewController的实现功能分组有:

    • 类方法。

      • 单件函数。
      • 其他类方法。
    • Lifecycle。 对象生命周期函数

      • 对象生命周期函数。init, dealloc, descrition
    • 自定义属性。

    • UI对象的事件响应函数。

    • 公开方法。

    • 私有方法和辅助函数。

    • 通知处理函数。

    • 委托方法。

    #pargma mark - Class methods
    + (instancetype)sharedInstance {}
    + (CGFloat)viewHeightForObject:(id)object {}
    
    #pargma mark - Lifecycle
    - (instancetype)init {}
    - (void)dealloc {}
    - (void)viewDidLoad {}
    - (void)viewWillAppear:(BOOL)animated {}
    - (void)didReceiveMemoryWarning {}
    
    #pargma mark - Custom Accessors
    - (void)setCustomProperty:(id)value {}
    - (id)customProperty {}
    
    #pargma mark - IBActions
    - (IBAction)onSubmitDataAction:(id)sender {}
    
    #pargma mark - Public 
    - (void)publicMethod {}
    
    #pargma mark - Private helpers or utils
    - (void)m2_privateMethod {}
    
    #pargma mark - Notification Handlers
    - (void)onEnterBackgroundHandler:(NSNotification *)notification {}
    
    #pargma mark - Delegate methods
    // 多个delegate 进行分组
    
    

    📝 使用文件模板

    3. 命名


    Apple命名规则尽可能坚持,特别是与这些相关的memory management rules(NARC)。

    长的,描述性的方法和变量命名是好的。

    命名涉及到比较多:

    • 库名
    • 文件名
    • 类名
    • 函数名
    • 变量名

    库名

    设计一个库通常使用前缀+名称的方式。eg.

    • UIKit
    • AVFoundation
    • SDWebImage
    • AFNetworking

    文件名

    文件名命名规则与类命名规则一致:

    • 命名空间。本项目/或项目模块缩略前缀。eg. M2API, M2BL, M2PL
    • 功能名词。User,Device,File,VideoPlayer。
    • 功能分类名字。例如,
      • Client 代表DAL的网络访问客户端。
      • Service代表BL的业务逻辑类。
      • Item代表PL中View的DTO(Data Transfer Object)。
      • View代表PL的视图类。
      • Controller代表PL的中MVC模式的C控制器
      • ViewModel/Store代表PLMVVMVM或者MVCS的S

    eg.

    • UIViewController.h
    • M2PLLoginView.h

    命名空间

    由于Objective-C 没有命名空间,所以通常使用项目名的头字母用于:

    • 常量

    • 枚举

    • C函数名

    • 全局变量名

    • 类名

    • 块类型名

    前缀应由不少于3个字母组成(苹果保留所有2个字母的前缀)。可以是APP名、公司名缩写等。

    例子🌰

    // 宏
    #debug M2_DEBUG
    
    // 常量
    FOUNDATION_EXPORT NSString * const M2UserErrorDomain;
    
    // 别名
    typedef NSString * const M2HTTPMethod;
    
    // 块类型名
    typedef void (^M2APISuccessBlock)(id response);
    typedef void (^M2APIFailureBlock)(NSError *error);
    
    // 类名
    @class M2User;
    

    尽量少使用宏来定义常量。宏通常用于编译条件。

    • 增加命名空间。
    • 单词使用大写。
    • 使用下划线连接单词。

    使用

    #define M2_DEBUG
    

    不使用

    #define Production
    #define M2_Production
    

    常量

    常量通常使用与字符串类型常量与值类型常量。注意点:

    • 命名空间。命名空间前缀全部大写
    • 使用驼峰命名
    // 头文件 .h
    FOUNDATION_EXPORT NSString * const M2UserErrorDomain;
    FOUNDATION const CGFloat M2UserMaxAge;
    
    // 实现文件 .m
    static NSString * const M2UserError = @"net.kim.M2UserErrorDomain"; // 跨文件使用
    static const CGFloat M2UserMaxAge = 200; // 跨文件使用
    
    static const CGFloat M2UserMinAge = 0; // 文件内部使用
    

    块类型

    块类型定义。注意点:

    • 命名。命名空间 + 功能名词 + Block
      • 命名空间。见《命名空间一节》
      • 功能名词。
      • 后缀Block。以区别其他类型。
    • 注意返回类型后需要增加一个空格

    typedef void (^M2APISuccessBlock)(id response);
    typedef void (^M2APIFailureBlock)(NSError *error);
    

    4. 类


    • 类方法
    • 单例模式
    • 属性
    • 实例方法

    类方法

    单例模式

    单例对象应该使用线程安全模式来创建共享实例。

    + (instancetype)sharedInstance {
      static id sharedInstance = nil;
    
      static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
      });
    
      return sharedInstance;
    }
    

    这会防止possible and sometimes prolific crashes.

    属性

    公开属性

    属性特性排列顺序如下:

    • 是否原子atomic/nonatomic。虽然默认为atomic, 原子访问时还是需要显式说明。
    • 读写readonly/readwrite。默认为readwrite。只读是需要显式说明。
    • 访问器getter/setter。如果是布尔类型getter,需要加is前缀。
    • 存储特性weak/strong/copy/assign。放在最后。

    💡Tip 不可变性 Immutable

    为了避免数据遭到不必要的修改:

    1. 不应该被外部直接修改的属性,应该声明(readonly)(可以在Extension中重新声明为(readwrite),使它对外只读,对内可读写)。
    2. 不要把NSMutable(Array/Dictionary/Set...)暴露出来,应该只留一个setter给外部使用,以免它们被其他类修改时,类自身难以察觉。
    3. 如果数据不是特别多,copy的代价不是特别大,留给其他类的getter应尽量用copy方法,返回一个不可变的对象。

    例子

    @property (nonatomic, readonly, copy) NSString *name;
    @property (nonatomic, readonly, assign) NSUInteger age;
    @property (nonatomic, readonly, assign) M2Gender gender;
    @property (nonatomic, readonly, getter=isLogin, assign) BOOL login;
    

    💡Tip 控件属性命名

    • 控件功能名词 + 后缀不带命名空间的控件类型名

    例子

    @interface M2PLoginView : UIView
    
    @property (nonatomic, weak) IBOutlet UILabel *userLabel; //!< 用户名标签
    @property (nonatomic, weak) IBOutlet UITextField *userTextField; //!< 用户名输入
    
    @end
    

    私有属性

    如果私有属性在模块内部可以访问,则使用私有头文件。

    eg. M2User_Private.h 在有需要使用到的实现文件引入即可。

    🚧 注意:私有头文件,在模块外部不可访问。生成库时需要注意忽略改头文件。

    例子

    /// 实现文件
    
    @interface M2User ()
    
    @property (nonatomic, copy)NSString *name;
    @property (nonatomic, assign)NSUInteger age;
    @property (nonatomic, assign)M2Gender gender;
    
    @end
        
    @implementation M2User
        
    // ...
        
    @end
    

    自定义属性

    下面是一个懒加载的自定义属性:

    - (NSMultableDictionary *)extraInfo {
        if (!_extraInfo) {
            _extraInfo = [NSMutableDictionary dictionary];
        }
        
        return _extraInfo
    }
    

    自定义属性设置

    - (void)setExtraInfo:(NSDictionary *)extraInfo {
        _extraInfo = extraInfo;
        // Do something else.
        // 其他副作用。
    }
    

    🚧 注意:在自定义属性内增加副作用需要特别注意。需要在属性增加注释说明。

    成员变量

    私有属性,一般定义在类的实现文件。

    /// 实现文件
    
    @interface M2User () {
        BOOL _status;
    }
    
    @end
        
    @implementation M2User
        
    // ...
        
    @end
    

    标识位

    @interface Fool () {
        struct {
            BOOL step1Done;
            BOOL step2Done;
        } _flags;
    }
    
    
    // 使用
    _flag.step1Done = YES;
    _flag.step1Done = NO;
    

    类初始化方法

    @interface Airplan
    
    + (instancetype)airplanWithType:(AirplanType)type;
    
    @end
        
    @implementation Airplan
    

    关于更多instancetype信息,请查看NSHipster.com

    Init方法

    Init方法应该遵循Apple生成代码模板的命名规则。返回类型应该使用instancetype而不是id

    - (instancetype)init {
        if (self = [super init]) {
            // ...
        }
        
        return self;
    }
    
    // 或者
    - (instancetype)initWithName:(NSString *)name {
        self = [super init];
        if (self) {
            // ...
        }
        
        return self;
    }
    

    查看关于instancetype的文章Class Constructor Methods

    方法定义

    响应函数

    规则:

    • 控件响应函数:前缀on + 功能动作 + 后缀Action
    • 通知响应函数:前缀on + 通知 + 后缀Handler

    使用

    // 控件响应函数
    - (IBAction)onLoginAction:(id)sender {
        // ...
    }
    
    // 通知响应函数
    - (void)onEnterBackgroundHandler:(NSNotification *)notification {
        // ...
    }
    

    不使用

    - (IBAction)login:(id)sender {
        // ...
    }
    
    - (void)enterBackground:(NSNotification *)noti {
        // ...
    }
    

    分类

    @interface M2APIUserClient (User)
    
    // 登陆
    - (RACSignal *)loginWithUser:(NSString *)user password:(NSString *)password;
    
    // 登出
    - (RACSignal *)logout;
    
    @end
    

    5. 子程序


    变量[TODO]

    布尔值

    Objective-C使用YESNO。因为truefalse应该只在CoreFoundation,C或C++代码使用。既然nil解析成NO,所以没有必要在条件语句比较。不要拿某样东西直接与YES比较,因为YES被定义为1和一个BOOL能被设置为8位。

    这是为了在不同文件保持一致性和在视觉上更加简洁而考虑。

    使用

    if (someObject) {}
    if (![anotherObject boolValue]) {}
    

    不使用

    if (someObject == nil) {}
    if ([anotherObject boolValue] == NO) {}
    if (isAwesome == YES) {} // Never do this.
    if (isAwesome == true) {} // Never do this.
    

    如果BOOL属性的名字是一个形容词,属性就能忽略"is"前缀,但要指定get访问器的惯用名称。例如:

    @property (assign, getter=isEditable) BOOL editable;
    

    文字和例子从这里引用Cocoa Naming Guidelines

    条件语句if/else

    条件语句主体为了防止出错应该使用大括号包围,即使条件语句主体能够不用大括号编写(如,只用一行代码)。这些错误包括添加第二行代码和期望它成为if语句;还有,even more dangerous defect可能发生在if语句里面一行代码被注释了,然后下一行代码不知不觉地成为if语句的一部分。除此之外,这种风格与其他条件语句的风格保持一致,所以更加容易阅读。

    使用

    // Good!
    if (!error) {
        return success;
    }
    

    不使用

    // Bad
    // 没有花括号,容易多些空行,造成逻辑提前返回。
    if (!error)
        return success;
    
    if (!error) return success;
    
    if (error != nil)
        return success
    

    多条件情而且单行过长的情况下,使用换行。条件符放行最后。

    使用

    // Good! 
    if (direction == M2Left ||
        direction == M2Right) {
        // ...
    } 
    

    不使用

    // Bad! 
    if (direction == M2Left 
        || direction == M2Right) {
        // ...
    } 
    

    使用

    if (user.isHappy) {
        // Do something
    } else {
        // Do something else
    }
    

    不使用

    if (user.isHappy)
    {
        // ...
    }
    else {
        // ...
    }
    

    Switch-Case

    - (void)handleMessage:(M2Message *)message {
        
        // 注意花括号与break。
        M2MessageType type = message.type;
        switch(type) {
            case M2MessageChat: {
                // ...
            } break;
            
            case M2MessageNotify: {
                // ...
            } break;
            case M2MessageSystem: {
                // ...
            } break;
                
            default:
               break;
        }
    }
    

    三元操作符 ?:

    当需要提高代码的清晰性和简洁性时,三元操作符?:才会使用。单个条件求值常常需要它。多个条件求值时,如果使用if语句或重构成实例变量时,代码会更加易读。一般来说,最好使用三元操作符是在根据条件来赋值的情况下。

    Non-boolean的变量与某东西比较,加上括号()会提高可读性。如果被比较的变量是boolean类型,那么就不需要括号。

    使用

    NSInteger value = 5;
    result = (value != 0) ? x : y;
    
    Bool isHorizontal = YES;
    result = isHorizontal ? x : y;
    
    NSString *name = nil;
    result = name ?: @"";
    
    

    不使用

    BOOL result = value!=0 ?x:y;
    

    block

    使用

    typedef void (^M2SuccessBlock)(id response);
    
    M2SuccessBlock successBlock = ^(id response) {
        // Do something.
    };
    

    不使用

    // 可读性不够强,另外可能导致过长行。
    void (^successBlock)(id response) = ^(id response) {
        // Do something.
    };
    

    使用

    // 风格1
    [userClient loginWithUser:user password:password success:^(id user) {
        // do something. 
    } failure:^(NSError *error) {
        // ...
    }];
    
    
    // *******************************************
    // 如果success/failure块过长,则可以前缀定义块。
    // 风格2
    M2APISuccessBlock successBlock = ^(id response) {
      // do something.  
    };
    
    M2APIFailureBlock failureBlok = ^(NSError *error) {
      // ...  
    };
    
    // 单行过长,则换行。
    [userClient loginWithUser:user 
                     password:password 
                      success:successBlock 
                      failure:failureBlock];
    
    

    字面值

    使用

    // 数组
    NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
    
    // 字典
    NSDictionary *productManagers = @{@"iPhone": @"Kate", @"iPad": @"Kamal", @"Mobile Web": @"Bill"};
    
    // Number
    NSNumber *shouldUseLiterals = @YES;
    NSNumber *buildingStreetNumber = @10018;
    

    不使用

    // 数组
    NSArray *names = [NSArray arrayWithObjects:@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul", nil];
    
    // 字典
    NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @"Kate", @"iPhone", @"Kamal", @"iPad", @"Bill", @"Mobile Web", nil];
    
    // Number
    NSNumber *shouldUseLiterals = [NSNumber numberWithBool:YES];
    NSNumber *buildingStreetNumber = [NSNumber numberWithInteger:10018];
    

    数组

    // 数组
    NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
    
    names[]
    

    字典

    // 字典创建
    NSDictionary *productManagers = @{@"iPhone": @"Kate", @"iPad": @"Kamal", @"Mobile Web": @"Bill"};
    
    // 访问
    NSString *product = productManagers[@"iPhone"];
    
    // 
    NSMutableDictionary *user = [NSMutableDictionary dictionary];
    
    // 设置
    user[@"name"] = @"Bob";
    user[@"title"] = @"IT Manager";
    user[@"age"] = @25;
    
    // 迭代
    [user enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
       NSLog(@"key : %@, value : %@", key, obj);     
    }];
    

    CGRect 函数

    使用

    // Good!
    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);
    

    不使用

    // Bad
    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 };
    

    注释

    当需要注释时,注释应该用来解释这段特殊代码为什么要这样做。任何被使用的注释都必须保持最新或被删除。

    一般都避免使用块注释,因为代码尽可能做到自解释,只有当断断续续或几行代码时才需要注释。例外:这不应用在生成文档的注释

    空格

    空行

    6. 资源


    iOS应用包含多种资源文件

    • storyboard/xib
    • 图片
    • 字符串
    • 字体
    • 多媒体

    storyboard/xib

    图片

    字符串

    字体

    多媒体

    7. 模块


    8. Xcode工程


    物理文件应该与Xcode工程文件保持同步来避免文件扩张。任何Xcode分组的创建应该在文件系统的文件体现。代码不仅是根据类型来分组,而且还可以根据功能来分组,这样代码更加清晰。

    尽可能在target的Build Settings打开"Treat Warnings as Errors,和启用以下additional warnings。如果你需要忽略特殊的警告,使用 Clang's pragma feature

    9. 辅助工具


    • Spacecommander

    参考:

    相关文章

      网友评论

        本文标题:Objective-C 代码规范

        本文链接:https://www.haomeiwen.com/subject/iudwdftx.html