iOS隨筆——編程之美oc代碼風格

tea time

原文

頭文件#import的順序


寫法模板

#import <系統庫>

#import <第三方庫>

#import “其他類”

盡量按照先系統類 第三方類 自己寫的類順序導入 中間不能有空格

  • 建議的寫法
#import <UIKit/UIKit.h>
#import <Google/Analytics.h>
#import "GBOrderEmptyView.h"
  • 不建議的寫法
#import "GBOrderEmptyView.h"
#import <UIKit/UIKit.h>
#import <Google/Analytics.h>

@Class的寫法


寫法模板

@class class1, class2;

  • 建議寫法
@class UIView, UIImage;
  • 不建議寫法
@class UIPress;
@class UIPressesEvent;

@Interface的寫法


寫法模板

@interface 類名 : 父類 <協議1, 協議2>

@interface和類名中間一個空格

類名后緊跟:之后空格加上父類協議之間用,空格分割

  • 建議寫法
@interface AppDelegate : UIResponder <UIApplicationDelegate, UITableViewDataSource>
  • 不建議寫法
@interface AppDelegate:UIResponder<UIApplicationDelegate,UITableViewDataSource>

@protocol的寫法


寫法的模板

@protocol 協議的名稱 <協議1, 協議2>

@potocol和協議的名稱有空格 協議的名稱和其他協議有空格 其他協議之間有空格

  • 建議寫法
@protocol UIResponderStandardEditActions <NSObject>
  • 不建議寫法
@protocol UIResponderStandardEditActions<NSObject>

@property的寫法


@property (關鍵詞, 關鍵詞) 類 *變量名稱;

關鍵詞用,空格分割 類前后空格

  • 建議寫法
@property (strong, nonatomic) UIWindow *window;
  • 不建議寫法
@property(strong, nonatomic) UIWindow * window;

@property關鍵詞的使用


對象 strong

基本變量assign

XIB控件 代理 weak

字符串和block使用 copy

對于一些弱引用對象使用weak

對于需要賦值內存對象 copy

h頭文件方法寫法


寫法模板

@interface

方法的參數在一排顯示

方法之間保留一行

第一個方法和@interface保留空行

最后一個方法和@end保留空行

  • 建議寫法
@interface Text : NSObject

  - (void)testFunction;

 @end
  • 不建議寫法
@interface Text : NSObject
 - (void)testFunction;
@end

聲明const的字符串


開頭用k標識

推薦k+模板名字首字母大寫+作用名稱 防止和其他的重復

比如:CartViewModel類需要聲明更新購物車列表的通知

kCVMNoticationUpdateCartList

如果是聲明Cell的重用字符

k+cell的名稱+identifier

比如: GBHomeItemTableViewCell的標識符

kGBHomeItemTableViewCellIdentifier

Const聲明字符串位置


如果是需要聲明在h里面讓其他的類用到需要在h聲明m實現

  • 聲明
UIKIT_EXTERN NSString *const kNoticationUpdateCartList;
  • 實現
NSString *const kNoticationUpdateCartList = @"kNoticationUpdateCartList";

對于如果導入是UIKit類就使用UIKIT_EXTERN 如果是Founction使用關鍵詞FOUNDATION_EXTERN

如果只在本類使用只用寫實現 不用寫聲明。

方法盡量控制最多五十行


一個方法內部最多五十行 如果超過就精簡代碼 就分開方法寫

方便之后進行熱修復 代碼重構

注釋一定要寫


自己管理的類一定注釋屬性用途 方法的用途 參數的說明

屬性如果設置默認值 一定注明默認值是什么

如果方法內部存在邏輯判斷 方法跳轉 一定注釋判斷用法 方法跳轉用法

除了初始化操作

其他聲明變量 賦值 判斷 應該注明注釋用途

注釋的寫法

  • Class類注釋
/**
 訂單的cell
 */
@interface OrdersCell : UITableViewCell
  • property屬性的注釋
@property (nonatomic,strong) UILabel *statusLabel;      /**< 顯示狀態*/
  • 方法的注釋

如果有返回值 請加上return

顯示倒計時文本
 @param timerLabel 倒計時文本
 @param countTime 剩余時間
 */
-(void)timerLabel:(MZTimerLabel*)timerLabel finshedCountDownTimerWithTime:(NSTimeInterval)countTime {
  • 局部變量和全局變量注釋
BOOL isOfflinePay; // 是否是離線支付
  • Block注釋
/*!
 驗證輸入的是否正確
 @param inputText 輸入的文本
 @return 如果返回值存在就代表驗證失敗 否則就代表成功
 */
typedef NSString * (^ATFVValidateInputCorrectComplete)(NSString *inputText);
  • NSUM的注釋
typedef NS_ENUM(NSUInteger, ATFVEditState) {
    ATFVEditStateNormal,                    /**< 默認 還沒有輸入任何的文字*/
    ATFVEditStateEditing,                   /**< 正在進行輸入*/
    ATFVEditStateEdited,                    /**< 輸入完畢*/
    ATFVEditStateEditedError,               /**< 輸入完畢錯誤*/
//    ATFVEditStateNoEdit                 /**< 只允許編輯*/
};

不允許外接修改的屬性要設置readonly


大家平時設置屬性默認是可讀可寫 但是這樣容易對于別人造成誤解 以為可以賦值

對于只能獲取的屬性 一定寫readonly

  • 建議寫法
@property (nonatomic, copy, readonly) NSString *name;
  • 不建議寫法
@property (nonatomic, copy) NSString *name;

頭文件引入的其他類 要使用@class


頭文件引入的類使用@class聲明不實用#import引入

可以防止互相引入 編譯失敗 不容易查找的BUG

造成的缺點

m文件還要#import 其他類調用這個類屬性也要#import對應的類

綜合來說寧愿自己多操作 也要防止這種循環引入的BUG的出現

  • 建議寫法
@class GBHomeViewController;
  • 不建議寫法
#import "GBHomeViewController.h"

pragma mark的使用


對于屬性的不同作用 比如設置顏色的 設置字體的 設置其他樣式 的可以進行分組

對于方法的作用分類 比如添加功能 刪除功能的

對于其他的代理方法 Get Set方法 Init初始化方法

  • 建議的寫法
#pragma mark - Life Cycle
#pragma mark - DataSource
#pragma mark - Delegate
#pragma mark - Request
#pragma mark - Setter
#pragma mark - Getter
#pragma mark - Private

BOOL類型屬性的聲明


屬性set不要帶is get要帶is

  • 建議寫法
@property(nonatomic, assign, getter=isUserLogin) BOOL userLogin;
  • 不建議寫法
@property(nonatomic, assign) BOOL userLogin;

方法命名的規范


不能用init set 開頭進行命名

如果不是寫初始化方法不要用init進行開頭

如果不是屬性的set方法不要用set作為方法的前綴

{}的寫法


  • 建議寫法
if(YES) {
  doing something
}
  • 不建議寫法
if(YES)
{
  doing something
}

計算符號兩邊要有空格


比如 + - * / =等運算符左右有空格

  • 建議寫法
x = 1 + 2;
  • 不建議寫法
x=1+2;

控件命名的規范


對于命名一定不要簡寫 那篇很長的單詞 但是一些單詞就是簡寫的除外 比如WTO RMB

UILabel結尾加上Label;

UIImageView結尾記上ImageView

等等讓其他的編程人員看名字就知道變量的用法 和屬于什么控件

  • 建議寫法
@property (nonatomic, strong) UILabel *userNameLabel;
  • 不建議寫法
@property (nonatomic, strong) UILabel *name;

if判斷里面的條件要提取出來


對于if里面很多的判斷條件 要提取出來 方便之后進行斷點測試

  • 建議寫法
BOOL isTrue = a > b;
if(isTrue) {
}
  • 不建議寫法
if(a > b){
}

enum的定義


對于歸屬所在的enum 要寫在對應的類

我們現在就全部enum放在一個文件 覺得和蘋果的編碼規范違背 并且分離代碼有點麻煩

使用NS_ENUM進行定義

  • 建議的寫法
typedef NS_ENUM(NSUInteger, GBAppRunDeveloperMode) {
    GBAppRunDeveloperModeDebug,
    GBAppRunDeveloperModePreRelease,
    GBAppRunDeveloperModeRelease
};
  • 不建議寫法
typedef enum {
    GBAppRunDeveloperModeDebug,
    GBAppRunDeveloperModePreRelease,
    GBAppRunDeveloperModeRelease
}GBAppRunDeveloperMode;

對于初始化一定要使用類對應的初始化方法


比如UIView的對應初始化方法為

- (instancetype)initWithFrame:(CGRect)frame

UIViewController(如果用IB)對應的為

- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil

防止初始化用init new等沒經過系統進行設置一些默認的屬性 造成bug

對于聲明NSString const要對適應對應的模塊


比如系統的 NSNotificationName

typedef NSString *NSNotificationName NS_EXTENSIBLE_STRING_ENUM;
  • 建議寫法
typedef NSString *NSStringConfigProjectName;
FOUNDATION_EXPORT NSStringConfigProjectName const kConfigProjectPluginDebugBaseUrlString;
  • 不建議的寫法
FOUNDATION_EXPORT NSString *const kConfigProjectPluginDebugBaseUrlString;

對于#define宏命名


單詞全部的大寫 單詞之間用_分割

  • 建議的寫法
#define NS_AVAILABLE_IOS(_ios) CF_AVAILABLE_IOS(_ios)
  • 不建議的寫法
#define NSAvailableIos(_ios) CF_AVAILABLE_IOS(_ios)

對象調用方法要留空格


  • 建議寫法
[[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]
  • 不建議寫法
[[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds]

對于只在m內部聲明的const 需要添加static


這個我覺得可以不加 但是無法看到蘋果的實現 所以不知道蘋果的規范怎么寫的

  • 建議寫法
static NSStringInitCheckManger const KGoogleServerTestKey = @""
  • 不建議寫法
NSStringInitCheckManger const KGoogleServerTestKey = @""

對于局部的變量盡量的初始化


局部的變量要初始化 屬性有默認的值 所以我們不必須對于屬性進行初始化

我之前遇到的一個BUG就是int類型沒有初始化給我默認Nan造成崩潰

  • 建議寫法
int index = 0;
  • 不建議寫法
int index;

對于一些對象判斷是否賦值可以不進行初始化 但是對于一定不會為nil要進行初始化

變量名的規范


一定要使用駝峰的命名

  • 建議寫法
UNUserNotificationCenter *unCenter = [UNUserNotificationCenter currentNotificationCenter];
  • 不建議寫法
UNUserNotificationCenter *uncenter = [UNUserNotificationCenter currentNotificationCenter];

對于屬性的賦值


  • 建議的寫法
unCenter.delegate = self;
  • 不建議的寫法
[unCenter setDelegate:self];

對于NS_OPTIONS類型多個值用|連接不能用+

  • 建議寫法
UNAuthorizationOptionAlert | UNAuthorizationOptionBadge | UNAuthorizationOptionSound
  • 不建議寫法
UNAuthorizationOptionAlert+UNAuthorizationOptionBadge+UNAuthorizationOptionSound

block的命名規范


之前研究過很多的第三方的命名 對于蘋果官方的沒找到

有CallBack結尾 Complete結尾 Block結尾 還有CompletionHandle結尾的

我看到蘋果很多的結尾都是用CompletionHandle結尾

大部分命名是Block我們按照Block命名

  • 建議寫法
typedef void(DidUpdateViewCompletionHandle)(void)
  • 不建議寫法
typedef void(DidUpdateViewCallBack)

使用NSUserDefaults要先創建

因為我們用到NSUserDefaults無非是保存和讀取 事先的創建一個對象 可以精簡代碼

當執行方法很多 用變量替換

  • 建議寫法
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
[userDefault setDouble:CFAbsoluteTimeGetCurrent() forKey:@"AppStartTime"];
  • 不建議寫法
[[NSUserDefaults standardUserDefaults] setDouble:CFAbsoluteTimeGetCurrent() forKey:@"AppStartTime"]

盡量少在initialize load方法做一些初始化的事情


影響程序的啟動

  • 建議寫法
- (void)viewDidLoad {
    [super viewDidLoad];
    [[UITabBarItem appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName : GBCOLOR(153, 153, 153, 1.0)} forState:UIControlStateNormal];
    [[UITabBarItem appearance] setTitleTextAttributes:                                                         @{NSForegroundColorAttributeName : GBCOLOR(255, 129, 55, 1.0)} forState:UIControlStateSelected];
 }
  • 不建議寫法
+ (void)initialize {
    [[UITabBarItem appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName : GBCOLOR(153, 153, 153, 1.0)} forState:UIControlStateNormal];
    [[UITabBarItem appearance] setTitleTextAttributes:                                                         @{NSForegroundColorAttributeName : GBCOLOR(255, 129, 55, 1.0)} forState:UIControlStateSelected];
}

判斷不要放在一行


判斷放在一行也是可以的 但是我們還是要求正規一些 畢竟注明Apple的goto BUG

  • 建議寫法
if (!self.startPagesListInfo) {
  return ;
}
  • 不建議寫法
if (!self.startPagesListInfo) return ;

對于我們取值和存值的key要定義一下


  • 建議的寫法
NSString startLoadString = @"startLoad";
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
[userDefault objectForKey:startLoadString]
[userDefault setObject:@() forKey:startLoadString]
  • 不建議的寫法
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
[userDefault objectForKey:@"startLoad"]
[userDefault setObject:@() forKey:@"startLoad"]

方法的參數連接不能有空格


  • 建議寫法
- (BOOL)judgeStartLoadPageInTimeCurrentWithModel:(StartPageModel *)obj
  • 不建議寫法
- (BOOL)judgeStartLoadPageInTimeCurrentWithModel : (StartPageModel *)obj;

對于block的循環引用使用weakify 和strongify


  • 建議寫法
@weakify(self);
[@[] enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  @strongify(self);
}];
  • 不建議寫法
__weak typeof(self) weakSelf = self;
[@[] enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    __strong typeof(weakSelf) strongSelf = weakSelf;
}];

推薦的界面框架


所有界面的控件元素獨立到另外的UIView

UIViewController只負責跳轉界面

新建UIView負責界面的顯示

VIewModel負責數據的請求和解析

APi負責請求

model負責后臺數據解析

other 負責樣式和其他處理

多使用字面量


  • 字符串 @””
NSString *string = @"string";
  • NSNumber @()
NSNumber *number = @(1);
  • 字典 @{}
NSDictionary *dictionary = @{
                             @"name":@"123"
                             };
  • 數組 @[]
NSArray *array = @[
                   @"321231",
                   @"123"
                   ];

字典和數組的取值和存值

多用類型常量 少用#define


  • 建議的寫法
static const NSTimeInterval kAnimationDuration = 0.3;
  • 不建議寫法
#define ANIMATION_DURATION 0.3

對于一些狀態 選項的使用枚舉


盡量少用根據數字判斷狀態少用字符串 數字判斷狀態

  • 建議寫法
typedef NS_ENUM(NSUInteger, HomeViewState) {
    HomeViewStateNoData,
    HomeViewStateFailure,
    HomeViewStateItemList,
    HomeViewStateBannerList
};
if(state == HomeViewStateNoData){
  // 顯示沒數據
}else if(state == HomeViewStateFailure) {
  // 顯示請求錯誤
}else if(state == HomeViewStateItemList) {
  // 顯示商品的列表
}else if(state == HomeViewStateBannerList) {
  // 顯示banner列表
}
  • 不建議寫法
if(state == 0){
  // 顯示沒數據
}else if(state == 1) {
  // 顯示請求錯誤
}else if(state == 2) {
  // 顯示商品的列表
}else if(state == 3) {
  // 顯示banner列表
}

多使用類族


比如我們需要創建一個類 有多個樣式

typedef NS_ENUM(NSUInteger, ZHCustomViewStyle) {
    ZHCustomViewStyleRed,
    ZHCustomViewStyleWhite
};
@interface ZHCustomView : UIView
+ (instancetype)customWithStyle:(ZHCustomViewStyle)style;
@end
  
#import "ZHCustomView.h"
#import "ZHCustomRedView.h"
#import "ZHCustomWhiteView.h"
@implementation ZHCustomView
+ (instancetype)customWithStyle:(ZHCustomViewStyle)style {
    switch (style) {
        case ZHCustomViewStyleRed: {
            return [[ZHCustomRedView alloc] initWithFrame:CGRectZero];
        }
            break;
        case ZHCustomViewStyleWhite:{
            return [[ZHCustomWhiteView alloc] initWithFrame:CGRectZero];
        }
            break;
        default:
            break;
    }
}
@end

ZHCustomRedView

#import "ZHCustomView.h"
@interface ZHCustomRedView : ZHCustomView
@end
#import "ZHCustomRedView.h"
@implementation ZHCustomRedView
- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        self.backgroundColor = [UIColor redColor];
    }
    return self;
}
@end

ZHCustomWhiteView

#import "ZHCustomView.h"
@interface ZHCustomWhiteView : ZHCustomView
@end
#import "ZHCustomWhiteView.h"
@implementation ZHCustomWhiteView
- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        self.backgroundColor = [UIColor whiteColor];
    }
    return self;
}
@end

類名加上前綴避免沖突


因為團隊的合作 可能會出現大家想到一樣的名字或者添加第三方庫引入和第三方庫名字一樣

盡量加上前綴。

如果只針對工程就使用工程的縮寫

比如自己個人的第三方庫就加上自己名字或者昵稱的縮寫

  • 建議寫法
@interface GBHomeViewController : NSObject
  • 不建議寫法
@interface HomeViewController : NSObject

提供全能的初始化方法


對于初始化參數有很多 但是不是一定全部使用的可以提供多個初始化方法

  • 建議寫法
- (instancetype)initWithFrame:(CGRect)frame;
- (instancetype)initWithPerson:(GBPersonModel *)person;
- (instancetype)initWithFrame:(CGRect)frame person:(GBPersonModel *)person;
  • 不建議寫法
- (instancetype)initWithFrame:(CGRect)frame person:(GBPersonModel *)person;

實現Description方便調試


這個不推薦自己手寫 可以使用Xcode插件自動生成 屬性越多會加重手寫代碼的長度

盡可能使用不可變的對象


對于OC存在很多可變的對象 比如NSMutableString NSMutableArray NSMutableDictionary等等

對于一些不允許改變的直接使用不可變對象

可以節省對象開支 還可以防止別人修改數據造成bug

  • 建議寫法
NSArray *sexList = @[@"男",@"女"];
  • 不建議寫法
NSMutableArray *sexList = [NSMutableArray arrayWithArray:@[@"男",@"女"]]

記得Dealloc記得釋放


記得在Dealloc釋放注冊的通知和KVO的監聽

不釋放容易造成內存釋放崩潰

養成習慣把按照方法功能到分類里面


對于一些有按照功能類型的方法劃分在一個分類里面 分類和之前類寫在同一個文件

  • 建議寫法
@interface GBPerson : NSObject
@end
  
@interface GBPerson (Friend)
// 朋友
- (void)addFriend:(GBPenson *)friend;
- (void)deleteFriend:(GBPenson *)friend;
@end
@interface GBPerson (Play)
  // 娛樂
- (void)playSound;
- (void)playGame;
@end
  • 不建議寫法
@interface GBPerson : NSObject
// 朋友
- (void)addFriend:(GBPenson *)friend;
- (void)deleteFriend:(GBPenson *)friend;
// 娛樂
- (void)playSound;
- (void)playGame;

為第三方類添加分類添加前綴


比如為系統UIView添加分類Add的添加前綴

  • 建議寫法
@interface UIView (GB_Add) 
- (void)gb_addCustomView:(CustomView *)customView;
@end
  • 不建議寫法
@interface UIView (Add)
- (void)addCustomView:(CustomView *)customView;
@end

使用dispatch_once來創建單例


  • 建議寫法
+ (instancetype)sharedInstance {
    static ZHCustomView* instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [ZHCustomView new];
    });
    return instance;
}
  • 不建議寫法
+ (instancetype)sharedInstance {
    static ZHCustomView* instance = nil;
    if(!instance) {
      instance = [[ZHCustomView alloc] initWithFrame:CGRectZero];
    }
    return instance;
}

遍歷的寫法


如果只需要便利數組和字典的寫法用for in

  • 建議寫法
for(NSString *name in names) {
  //
}
  • 不建議寫法
for(int i = 0; i < names.lenght ; i ++) {
  NSString *name = names[i];
}

需要便利字典和數組的內容 并且需要索引用enumerator


  • 建議寫法
[names enumerateObjectsUsingBlock:^(NSString * _Nonnull name, NSUInteger idx, BOOL * _Nonnull stop) {
}];
  • 不建議寫法
for(int i = 0; i < names.lenght ; i ++) {
  NSString *name = names[i];
}

如果想進行緩存使用NSCache不要使用NSDictionary進行緩存


  • 建議的寫法
NSCache *cache = [[NSCache alloc] init];
[cache setObject:object forKey:key];
  • 不建議寫法
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setObject:object forKey:key];

復雜的表達式


  • 建議寫法
BOOL nameContainsSwift  = [sessionName containsString:@"Swift"];
BOOL isCurrentYear      = [sessionDateCompontents year] == 2014;
BOOL isSwiftSession     = nameContainsSwift && isCurrentYear;
if (isSwiftSession) {
    // Do something very cool
}
  • 不建議寫法
if ([sessionName containsString:@"Swift"] && [sessionDateCompontents year] == 2014) {
    // Do something very cool
}

三元運算符


  • 建議寫法
result = a > b ? x : y;
  • 不建議寫法
result = a > b ? x = c > d ? c : d : y;
  • 建議寫法
result = object ? : [self createObject];
  • 不建議寫法
result = object ? object : [self createObject];

數組和字典最好指定元素的類型


  • 建議寫法
NSArray<NSString *> *names = [NSArray array];
  • 不建議寫法
NSArray *names = [NSArray array];

數組和字典的元素垂直寫


  • 建議的寫法
NSArray *array = @[
                    @"a",
                    @"b",
                    @"b"
                    ];
NSDictionary *dictionary = @{
                              @"a":@"",
                              @"b":@"",
                              @"c":@""
                            };
  • 不建議寫法
NSArray *array = @[@"a",@"b",@"b"];
NSDictionary *dictionary = @{@"a":@"",@"b":@"",@"c":@""};
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容