<EffectiveObjective-C2.0編寫高質量iOS與OS X代碼的52個有效方法>讀書筆記(三)

本人有若干成套學習視頻, 可試看! 可試看! 可試看, 重要的事情說三遍 包含Java, 數據結構與算法, iOS, 安卓, python, flutter等等, 如有需要, 聯系微信tsaievan.

這本書在第三章"接口與API設計"中提到了一些知識點, 有必要拿出來說說:
(一)首先來說說description方法.

在自定義的一些對象中, 如果打印對象指針的話, 只會打印類名和指針地址, 并不會將一些細節打印出來, 這個時候,我們往往會去重寫description方法. 但是我們往往忽略了debugDescription方法. 那么, 這兩種方法有什么不同呢?

如果是重寫description方法, 打印的時候并不會走debugDescription方法, 但是如果你打了斷點, 那么在你po對象的時候, 就會把debugDescription方法里需要打印的內容打印在控制臺上.

比如我新建一個YFPerson類, 重寫其description方法和debugDescription方法:


#import "YFPerson.h"

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

@implementation YFPerson
- (instancetype)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName {
    if (self = [super init]) {
        _firstName = [firstName copy];
        _lastName = [lastName copy];
    }
    return self;
}

- (NSString *)description {
    return [NSString stringWithFormat:@"對象的類為: %@, 對象的地址為: %p", [self class], self];
}

- (NSString *)debugDescription {
    return [NSString stringWithFormat:@"對象的類為: %@, 對象的地址為: %p, 姓為: %@, 名為: %@", [self class], self, _lastName, _firstName];
}

@end

那么我直接打印person對象時, 其結果是:

`description`方法

但是如果我在23行打上斷點, 用po的方法去打印person對象, 那么就會打印更多信息, 這些信息是我在重寫debugDescription方法的時候返回的:

`debugDescription`方法
(二)然后說說"盡量使用不可變對象"

書中提到了一個例子:

比如現在有一個YFPerson類, 然后有一個朋友列表功能, 你需要添加朋友和刪除朋友, 這時候你會想到用一個屬性, 比如NSMutableSet, 但是書中建議將NSMutableSet寫成一個私有的成員變量:

@implementation YFPerson {
    NSMutableSet *_friendList;
}

然后暴露兩個接口:

- (void)addFriend:(YFPerson *)newFriend;

- (void)removeFriend:(YFPerson *)oldFriend;

這兩個接口給外界修改朋友列表.

書中不建議的做法是直接暴露集合, 然后直接修改集合, 這樣可能在YFPerson不知情的情況下直接修改了集合, 造成數據混亂

所以書中說: 盡量使用不可變對象.
  • 我的理解是盡量讓對象暴露給外界的接口越少越好, 除非是不得已的時候, 才暴露接口對外, 如果對外暴露屬性的話, 最好是只讀屬性, 特別是對那些從服務器拉下來的屬性, 這些屬性值都是服務器給的, 客戶端修改不了, 所以都可以寫成只讀屬性.

  • 還有一些集合類, 數組, 字典, 集合等, 盡量不要將可變的數組, 字典, 集合暴露給外界, 而是封裝一套API供外界使用, 而不是讓外界直接操縱可變數組, 字典, 集合.

以上的做法提高了對象的穩定性!

(三)為私有方法名加前綴

這個想必大家都知道并且注意到了, 在很多源碼里, 大家也都是這么寫的, 但是有一條很多源碼也沒有注意到:

★ 不要單用一條下劃線做私有方法的前綴, 因為這種做法是預留給蘋果公司用的.
(四)理解OC錯誤模型

主要介紹了NSError的使用, 書中不建議頻繁使用拋出異常:

只有發生了可使整個應用程序崩潰的嚴重錯誤時, 才應使用異常

書中也提供了兩種方式來處理 :

  • 指派"代理方法" (delegate method) 來處理錯誤
  • 把錯誤信息放在NSError對象里, 經由"輸出參數"返回給調用者.
一種是拿到用戶傳入的NSError對象的指針地址, 然后根據指針地址, 修改NSError對象的值:

比如我添加好友, 如果lastName為空的話, 就報錯, 就可以這么寫:

- (void)addFriend:(YFPerson *)newFriend withError:(NSError *__autoreleasing *)error {
    if (error) {
        if ([newFriend.lastName isEqualToString:@""] || newFriend.lastName == nil) {
            *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:101 userInfo:@{
                                                                                        NSLocalizedDescriptionKey : @"添加的好友沒有姓"
                                                                                        }];
            return;
        }
    }
    [_friendList addObject:newFriend];
}

外界在調用時, 將error所在的地址作為參數傳過去, 然后運行結束之后, 如果發生錯誤的話, error的值會被修改:

    NSError *error = nil;
    [person addFriend:[YFPerson new] withError:&error];
    if (error) {
        NSLog(@"添加好友失敗: %@", error);
    }else {
        NSLog(@"添加好友成功");
    }

本例中, 我傳入了一個空的YFPerson對象, 打印結果如下:

添加好友失敗錯誤提示
另外一種是依靠代理來做錯誤處理:

比如我刪除好友, 如果傳入的參數為空的話, 我也做一個錯誤處理:
先寫一個代理方法:

@class YFPerson;

@protocol YFPersonDelegate <NSObject>

- (void)removeFriend:(YFPerson *)oldFriend withError:(NSError *)error;

@end

然后在刪除好友時做一個判斷:

- (void)removeFriend:(YFPerson *)oldFriend {
    if (!oldFriend) {
        NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:102 userInfo:@{
                                                                                            NSLocalizedDescriptionKey : @"刪除好友不能為空"
                                                                                            }];
        if ([self.delegate respondsToSelector:@selector(removeFriend:withError:)]) {
            [self.delegate removeFriend:oldFriend withError:error];
        }
        return;
    }
    [_friendList removeObject:oldFriend];
}

如果好友為空, 則調用代理方法, 把錯誤對象傳進去, 這樣, 外界在傳入參數為空時, 就會調用代理方法, 我在外界就可以獲取到錯誤信息:

[person removeFriend:nil];
- (void)removeFriend:(YFPerson *)oldFriend withError:(NSError *)error {
    NSLog(@"刪除好友失敗 : %@", error);
}

控制臺打印的錯誤信息為:

刪除好友失敗錯誤信息

以上就是利用NSError的兩種基本方法!

(五)理解NSCopying協議

又遇到蛋疼的深拷貝淺拷貝的問題了, 在網上找到了一篇很好的文章, 覺得比自己總結得好, 套用文中的一句話:

概念性的東西,沒有必要糾結于此。只要知道進行拷貝操作時,被拷貝的是指針還是內容即可
文章地址貼在下面了, 需要的童鞋可以看看:
iOS 集合的深復制與淺復制

PS. 本人有若干成套學習視頻, 包含Java, 數據結構與算法, iOS, 安卓, python, flutter等等, 如有需要, 聯系微信tsaievan.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容