你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> [編寫高質量iOS代碼的52個有效方法](五)接口與API設計(下)

[編寫高質量iOS代碼的52個有效方法](五)接口與API設計(下)

編輯:IOS開發綜合

先睹為快

19.使用清晰而協調的命名方式

20.為私有方法名加前綴

21.理解Objective-C錯誤模型

22.理解NSCopying協議

第19條:使用清晰而協調的命名方式

類、方法及變量的命名是Objective-C編程的重要環節。其語法結構使得代碼讀起來和句子一樣。名稱中一般都帶有in、for、with等介詞,而其他編程語言則很少使用這些它們認為多余的字眼:

// C++
string text = "The quick brown fox jumped over the lazy dog";
string newText = text.replace("fox","cat");

// Objective-C
NSString *text = @"The quick brown fox jumped over the lazy dog";
NSString *newText = [text stringByReplacingOccurrencesOfString:@"fox" withString:@"cat"];

text.replace(“fox”,”cat”)從字面上無法判斷是用fox替換cat還是用cat替換fox,而Objective-C的命名方式雖然長一點,但卻非常清晰。

C++或Java更習慣簡省的函數名,在這種命名方式下,若想知道每個參數的用途,就得查看函數原型,這會令代碼難於讀懂,以一個矩形的類為例:

// C++
class Rectangle{
    public:
        Rectangle(float width, float height);
        float getWidth();
        float getHeight();
    private:
        float width;
        float height;
};

// Objective-C
@interface EOCRectangle : NSObject

@property (nonatomic, assign, readonly) float width;
@property (nonatomic, assign, readonly) float height;

- (id)initWithWidth:(float)width andHeight:(float)height;
@end

當創建該類實例時:

// C++
Rectangle aRectangle = new Rectangle(5.0f,10.0f);
// Objective-C
Rectangle *aRectangle = [[Rectangle alloc]initWithWidth:5.0f andHeight:10.0f];

C++代碼無法判斷5.0f和10.0f分別表示什麼,就算能推測出來表示矩形的尺寸,也不知道寬度在先還是高度在先,需要查函數定義才能確定。而Objective-C的代碼卻很清晰。

雖說長名字可伶代碼更為易讀,但也不能長得太過分了,應盡量言簡意赅:

// 好的方法名
- (EOCRectangle*)unionRectangle:(EOCRectangle*)rectangle
- (float)area

// 不好的方法名
- (EOCRectangle*)union:(EOCRectangle*)rectangle // 不清晰
- (float)calculateTheArea  // 太冗余

給方法命名時的注意事項可以總結為:

如果方法的返回值是新創建的,那麼方法名的首個詞應是返回值類型,除非前面還有修飾語。屬性的存取方式不遵循這種命名方式。 應該把表示參數類型的名詞放在參數前面。 如果方法要在當前對象上執行操作,那麼就應該包含動詞;若執行操作時還需要參數,則應該在動詞後面加上一個或多個名詞。 不要使用str這種簡稱,應該使用string這樣的全稱。 返回Bool值的方法應加上has或is前綴。 get這個前綴留給那些借由輸出參數來保存返回值的方法。

第20條:為私有方法名加前綴

Objective-C語言沒辦法將方法標為私有,每個對象都可以響應任意消息。需要開發者在命名慣例中體現私有方法等語義。筆者喜歡用p_作為前綴,p表示private,而下劃線可以把這個字母和真正的方法名區隔開。

#import 

@interface EOCObject : NSObject
- (void)publicMethod;
@end

@implementation EOCObject

// 公有方法
- (void)publicMethod{
    // code
}

// 私有方法
- (void)p_privateMethod{
    // code
}
@end

蘋果公司喜歡單用一個下劃線作私有方法的前綴,所以請不要照蘋果公司的辦法來做,不然有可能無意間重寫父類的同名方法。

#import 

@interface EOCViewController : UIViewController
@end

@implementation EOCViewController
- (void)_resetViewController{
    // code
}
@end

以上代碼看起來沒有問題,但UIViewController類本身已經實現了一個名叫_resetViewController的方法。這樣寫的話所有調用都將執行子類中的這個方法。由於超類中的同名方法並未對外公布,除非深入研究這個庫,否則根本不會察覺無意間重寫了這個方法。

第21條:理解Objective-C錯誤模型

很多編程語言都有異常(exception)機制,Objective-C也不例外,但需要注意的是,ARC在默認情況下不是異常安全的。這意味著:如果拋出異常,那麼本應在作用域末尾釋放的對象現在卻不會自動釋放了。想要生成異常安全的代碼,需要打開編譯器標志-fobjc-arc-exceptions。

Objecti-C現在所采用的方法是:只在極其罕見的情況下拋出異常,異常拋出之後,無需考慮恢復問題,應用程序直接退出。異常只用於處理嚴重錯誤,出現一般錯誤時,令方法返回nil/0,或使用NSError。

比如初始化無法根據傳入的參數來初始化當前實例,那麼就令其返回nil/0。

- (id)initWithValue:(id)value{
    if((self = [super init])){
        if(/* 無法用value初始化實例 */){
            self = nil;
        }else{
            // 初始化
        }
    }
    return self;
}

而NSError的用法更加靈活,可以把導致錯誤的原因回報給調用者。NSError對象裡封裝了三條信息:Error domain(錯誤范圍,類型為字符串)、Error code(錯誤碼,類型為整數)、User info(用戶信息,類型為字典)。

// EOCErrors.h
#import 

// 聲明全局變量錯誤范圍
extern NSString *const EOCErrorDomain;

// 錯誤碼
typedef NS_ENUM(NSUInteger, EOCError){
    EOCErrorUnkown = -1,
    EOCErrorInteralInconsistency = 100,
    EOCErrorGeneralFault = 105,
    EOCErrorBadInput = 500,
};

@interface EOCErrors : NSObject

- (BOOL)doSomething:(NSError**)error;

@end

// EOCErrors.m
#import "EOCErrors.h"

NSString *const EOCErrorDomain = @"EOCErrorDomain";

@implementation EOCErrors

- (BOOL)doSomething:(NSError *__autoreleasing *)error{
    if(/* error發生 */){
        if (error) {
        *error = [NSError errorWithDomain:EOCErrorDomain code:EOCErrorGeneralFault userInfo:@{@"EOCErrorUserInfo":@"General fault occurs!"}];
        }
        return NO;
    }
    return YES;
}

@end

這樣就能經由輸出參數把NSError對象回傳給調用者:

NSError *error = nil;
BOOL ret = [object doSomething:&error];
if (error){
    // code
}

第22條:理解NSCopying協議

使用對象時,經常需要拷貝它。在Objective-C中,此操作通過copy方法完成。如果想令自己的類支持拷貝操作,那就要實現NSCopying協議,該協議只有一個方法:

- (id)copyWithZone:(NSZone*)zone

現在每個程序只有一個區了(默認區),所以實現這個方法時不用擔心其中的zone參數。copy方法由NSObject實現,該方法只是以默認區為參數來調用copyWithZone:方法。所以我們想的是重寫copy方法,真正需要實現的是copyWithZone:方法。

下面是一個實現NSCopying協議的例子:

// EOCPerson.h
#import 

@interface EOCPerson : NSObject

@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;

- (id)initWithFirstName:(NSString*)firstName lastName:(NSString*)lastName;
- (void)addFriend:(EOCPerson*)person;
- (void)removeFriend:(EOCPerson*)person;
@end

// EOCPerson.m
#import "EOCPerson.h"

@interface EOCPerson()
@property (nonatomic, copy, readwrite) NSString *firstName;
@property (nonatomic, copy, readwrite) NSString *lastName;
@end

@implementation EOCPerson{
    NSMutableSet *_friends;
}

- (void)addFriend:(EOCPerson *)person{
    [_friends addObject:person];
}

- (void)removeFriend:(EOCPerson *)person{
    [_friends removeObject:person];
}

- (id)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName{
    if ((self = [super init])) {
        _firstName = [firstName copy];
        _lastName = [lastName copy];
        _friends = [NSMutableSet new];
    }
    return self;
}

// 實現NSCopying協議
- (id)copyWithZone:(NSZone *)zone{
    // 以全能初始化方法初始化拷貝對象
    EOCPerson *copy = [[[self class]allocWithZone:zone]initWithFirstName:_firstName lastName:_lastName];
    // 將實例變量拷貝到拷貝對象
    copy->_friends = [_friends mutableCopy];
    return copy;
}

@end

本例中,_friends是使用mutableCopy方法來復制的,此方法來自另一個叫做NSMutableCopying的協議,與NSCopying類似,也只定義了一個方法:

- (id)mutableCopyWithZone:(NSZone*)zone

如果需要返回不可變的拷貝,則應該實現NSCopying協議,而若需要返回可變的拷貝,則應實現NSMutableCopying協議:

[NSMutableArray copy] // 返回NSArray
[NSArray mutableCopy] // 返回NSMutableArray

編寫拷貝方法時,還要決定一個問題,就是應該執行深拷貝還是淺拷貝。深拷貝的意思就是:在拷貝對象自身時,將底層數據也一並復制過去。而淺拷貝只拷貝容器本身,而不復制其中數據。Foundation框架中的所有容器在默認情況下都執行淺拷貝。

在EOCPerson那個例子中,若需要深拷貝的話,可以編寫一個專供深拷貝的方法:

- (id)deepCopy{
    EOCPerson *copy = [[[self class]alloc]initWithFirstName:_firstName lastName:_lastName];
    // copyItem參數設為YES,則會向容器中每個元素都發送copy消息,用拷貝好的元素創建新set返回給調用者。
    copy->_friends = [[NSMutableSet alloc]initWithSet:_friends copyItem:YES];
    return copy;
}
  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved