本人有若干成套學習視頻, 可試看! 可試看! 可試看, 重要的事情說三遍 包含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對象時, 其結果是:
但是如果我在23行打上斷點, 用po的方法去打印person對象, 那么就會打印更多信息, 這些信息是我在重寫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 集合的深復制與淺復制