<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # Effective Objective-C 2.0 Tips 總結 Chapter 3 & Chapter 4 ## Chapter 3 接口與 API 設計 - Tips 15 使用前綴避免明明空間沖突 - Objective-C 沒有命名空間,所以我們在起名時要設法避免命名沖突 - 避免命名沖突的方法就是使用前綴 - 應用中的所有名稱都需要加前綴(包括實現文件中的全局變量和純 C 函數) - Tips 16 提供“全能(designated)初始化方法” - 一個會被所有初始化方法調用到的初始化方法 - 當底層數據存儲機制變化時,只需要修改這個方法就可以了,不需要改動其他初始化方法 - 如果超類的全能初始化方法不適用于子類,或是與超類不同,那么需要覆蓋這個超類方法 - 子類的全能初始化方法都應該調用超類的對應方法,逐級向上 - Tips 17 實現 description 方法 - 在數組字典等集合對象打印時,都會調用對象的 `description` 方法,方便調試 - 系統默認的 `description` 方法對于自定義的對象并沒有輸出較為有用的內容,所以可以實現這個方法方便我們顯示對象 - 在調試時會調用 `debugDescription` 方法(也就是在調試時 lldb 中輸入 po 時調用的將會是 `debugDescription`),所以實現他可以幫助我們調試時獲得更多的信息 - 可以使用 `NSDictionary` 來實現 `description` 方法,這樣顯示和輸出都會比較方便,例如: ``` // Header File // 這里我略微修改了下原書中的示例代碼 @interface EOCLocation : NSObject @property (nonatomic, copy) NSString *title; @property (nonatomic) CGFloat latitude; @property (nonatomic) CGFloat longitude; @end // 我們要是可以使用 NSLog(@"%@", eoc_location) 直接輸出這個對象的經緯度(也就是所有屬性)就好了,那么可以參考下面的寫法實現 description 方法 @implementation EOCLocation - (NSString *)description { return [NSString stringWithFormat:@"<%@: %p, %@>", [self class], self, @{ @"title": self.title, @"latitude": @(self.latitude), @"longitude": @(self.longitude), }]; } @end ``` - Tips 18 盡量使用不可變對象 - 減少 side effect,在使用了一段時間的 RAC 和學習函數式思想后,一定程度上理解了不可變對象的好處 - 具體開發實踐中,應盡量把對外公布的屬性設為只讀,并且有必要時才對外公布,否則使用私有屬性 - 對于只讀屬性,可以不用指定內存管理語義(也就是 strong,weak,copy) - 對外只讀的屬性可以在對象內部,也就是類擴展(Class-Extension 也叫 Class-Continuation)中重新聲明為可讀寫的 - 可以使用 GCD 來設置讀寫操作為同步操作 - 就算屬性設置為只讀,在外部仍可以使用 KVC 來訪問這些屬性,例如:`[object setValue:@"value" forKey:@"propertyName"]` - 集合屬性(Array,Set,Dictionary)可以提供只讀屬性供外界使用(內部保存可變類型的變量,返回該變量的不可變拷貝),并提供操相應的操作方法,例如下面例子中,使用 `-addFriend:` 和 `-removeFriend:` 方法來實現對 `friends` 集合的操作,這樣保證了添加或刪除盆友的操作對象是知情的。對于直接修改 `friends` 集合的操作對象是不知情的,這樣可能會導致對象內各數據的不一致。 ``` @interface EOCPerson : NSObject @property (nonatomic, strong, readonly) NSSet *friends; @end @implementation EOCPerson { NSMutableSet *_internalFriends; } - (NSSet *)friends { return [_internalFriends copy]; } - (void)addFriend:(EOCPerson *)person { [_internalFriends addObject:person]; } - (void)removeFriend:(EOCPerson *)person { [_internalFriends removeObject:person]; } @end ``` - 不要在返回的對象上查詢其是否是可變對象并對其進行操作,同上條這樣對對象集合屬性的直接修改,容易產生 bug - Tips 19 使用清晰而協調的命名方式 - 方法名的風格要保證與自己的代碼或是需要集成的框架一致,也就是上下文需要一致,這點最重要放第一 - 起名遵循 Objective-C 的命名規范,這樣的接口名字一定程度上提示了接口的作用 - 方法名言簡意賅,從左到右讀起來最好像一個日常用于中的句子 - 方法名里不要使用縮略后的類型名稱 - Objective-C 的方法名相較其他語言要長一些,但是可以更好地表達方法的作用,以及各個參數的意義,比如: ``` Rectangle *recgangle = new Rectangle(5.0f, 10.0f); // 不如下面的命名方式 Rectangle *recgangle = [Rectangle initWithSize:(float)width :(float)height]; // 不如下面的命名方式 Rectangle *recgangle = [Rectangle initWithWidth:(float)width andHeight:(float)height]; ``` - Tips 20 為私有方法名加前綴 - 因為在 Objective-C 中沒有私有方法,所有對象都可以響應任意消息,并且可以通過 runtime 獲取對象可以相應的消息,所以我們使用特定的命名來區分私有方法 - 在使用 Category 或繼承系統中或第三方庫中的類的時候,可以防止命名沖突 - C 語言中使用 `_` 下劃線作為系統內部函數的開頭所以我們不能使用 `_` 作為私有方法的前綴(蘋果的官方庫也使用 `_`) - 原書作者建議使用 `p_` 來作為私有方法的前綴,個人建議使用開發中項目使用的前綴小寫來作為類前綴,比如上文的 `EOCPerson` 中添加私有方法可以使用 `eco_privateMethodName:`,這樣的前綴在第三方類庫中出現重復的概率比較小 - Tips 21 理解 Objective-C 錯誤模型 - ARC 在默認情況下并不是異常安全的,也就是拋出異常的時候,在作用域末尾應該釋放的對象將不會被釋放 - 可以使用 `-fobjc-arc-exceptions` 來告訴編譯器需要生成異常安全的代碼,但是這樣會引入一些額外代碼,并且在不拋出異常時也會執行這部分代碼 - 就算不使用 ARC 使用異常也很容易寫出內存泄漏的代碼,因為需要在拋出異常前清理所有申請的資源,所以現在我們只在非常罕見(嚴重錯誤,比如:抽象類中的方法沒有實現)的情況下拋出異常,拋出之后不需要考慮回復的問題,并且退出應用,這樣就不用編寫復雜的異常安全代碼 - 對于不嚴重的錯誤,我們通過返回 nil/0 或是使用 `NSError` 來處理,`NSError` 中包含了錯誤處理所需的各種信息,我們自己的錯誤需要規劃和設置好對應的 Error Domain,Error Code - 一般通過 delegate 來傳遞錯誤 `- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error` 或是輸入參數返回錯誤 `- (BOOL)doSomething:(NSError **)error` - Tips 22 理解 `NSCopying` 協議 - 實現 `NSCopying` 接口可以讓類實現拷貝(`copy`)方法,`- (id)copyWithZone:(NSZone *)zone` 中的 `zone` 是以前開發時使用的內存區參數,目前已經不使用了,可以不用考慮他 - 實現 `NSMutableCopying` 協議支持可變拷貝(`mutableCopy`)方法 - 對象拷貝時需要決定是深拷貝還是淺拷貝,一般情況下用淺拷貝 - 絕大多數情況下 `NSCopying` 實現的都是淺拷貝,所以如果使用深拷貝,建議創建一個單獨的方法來完成 ## Chapter 4 協議(Protocol)和分類(Category) - Tips 23 使用委托(delegate)和數據源(data source)協議進行對象間通信 - 委托模式(delegate pattern):對象把應對某個行為的責任委托給了另一個類 - 類似我們經常使用的 `UITableView`,`UITableViewDelegate` 和 `UITableViewDataSource` 分別定義了如何處理事件的接口和如何提供數據的接口,實現這兩個接口為 `UITableView` 提供交互邏輯和顯示數據,`UITableView` 本身只負責顯示獲取到的數據 - 委托模式同樣適用于異步事件,比如網絡請求完成后,回調委托對象將結果傳遞回去,實現事件的異步處理 - 使用委托對象的對象中的委托對象屬性需要設置為 weak,防止循環引用 - 使用委托中的方法時,使用 `respondsToSelector:` 先查詢委托對象是否實現了該方法,特別是在協議中使用 `@option` 關鍵字標注的可選方法 - 委托中的方法名要清晰明確,需要說明事件的來源,當前的事件,以及為什么委托對象需要獲取這個事件,所有委托方法都需要將發起委托的對象發送到委托對象(作為第一個參數),讓委托對象判斷事件來源 - 針對需要進行多次調用的委托對象(例如網絡加載時下載進度),可以通過結構體等方法,在設置委托對象的時候,一次檢查需要響應的方法并記錄,之后在使用的時候,直接通過記錄結果來判斷是否實現了某個方法,不用每次都使用 `respondsToSelector:` 方法來查詢是否實現,例: ``` @interface EOCNetworkFetcher() { struct { unsigned int didReceiveData : 1; unsigned int didFailWithError : 1; unsigned int didUpdateProgressTo : 1; } _delegateFlags; } @end @implementation EOCNetworkFetcher - (void)setDelegate:(id<EOCNetworkFetcherDelegate>)delegate { _delegate = delegate; _delegateFlags.didReceiveData = [delegate respondsToSelector:@selector(networkFetcher:didReceiveData:)]; _delegateFlags.didFailWithError = [delegate respondsToSelector:@selector(networkFetcher:didFailWithError:)]; _delegateFlags.didUpdateProgressTo = [delegate respondsToSelector:@selector(networkFetcher:didUpdateProgressTo:)]; } @end // 在需要調用 delegate 方法的時候 if (_delegateFlags.didUpdateProgressTo) { [_delegate networkFetcher:self didUpdateProgressTo:currentProgress]; } ``` - Tips 24 將類的實現代碼分散到便于管理的多個 Category 中 - 在開發的過程中,類的代碼只會越來越大,那么我們可以通過分類機制將類的代碼打散,根據業務分散到不同的分類中 - 應該把私有方法放到叫(Private)的分類中,隱藏實現細節 - Tips 25 總是為第三方分類的分類名稱加前綴 - 如果分類中出現同名方法,容易出現奇怪的 bug,所以在為其他類添加分類的時候,分類名稱和分類中的方法需要添加你自己使用的前綴 - Tips 26 勿在分類中聲明屬性 - 分類中可以定義方法(包括 getter 和 setter),但是不要定義屬性,因為在分類中定義的屬性不會生成實例變量 - 雖然有 `objc_setAssociatedObject` 魔法可以用,但是這容易導致內存管理問題,因為無法使用屬性記錄內存管理語義,但是建議一般情況下不使用 - 分類的主要作用是擴張類的功能,而不是封裝數據 - Tips 27 使用 Class-Continuation 分類,隱藏實現細節 - Class-Continuation 分類必須定義在該類的實現文件中,并且可以聲明實例變量,并且建議僅以此種方式增加實例變量 - 頭文件中聲明為只讀的屬性,可以在實現文件中的 Class-Continuation 分類中擴展為可讀寫 - 私有方法原型,和私有屬性,都可以放到 Class-Continuation 分類中 - 在 Class-Continuation 分類中可以聲明實現的接口,并且外部不會知道 - 可以通過私有屬性很好的封裝 C++/Objective-C++ 的代碼,提供 Objective-C 的接口給其他代碼使用 - Tips 28 通過協議提供匿名對象 - 使用類似 `@property(nonatomic, weak) id<ProtocolName> delegate;` 提供匿名類型對象作為 delegate,可以隱藏類名 - 對于類型不重要,只需要提供可向應方法的對象,可以使用匿名對象,隱藏實現細節 ## 對于 Chapter 1 的補充 第一章第四條中,多用類型常量,少用 `#define` 預處理指令中,建議大家使用類型常量而不是 `#define` 來定義常量,這里增加一個補充內容,swift 中,我們可以使用 `struct` 中的靜態變量來聲明常量,這樣帶來的一個好處是使用和分類管理非常方便 Xcode 8.0 帶的 clang 4.0 后開始支持類常量,也就是定義屬性的時候,可以加入 `class` 來修飾屬性,這樣這個屬性是屬于類的,于是乎,我們可以這樣使用常量了 ``` NSString *notificationName = XXXConstant.notificationNames.XXXUserDidLoginNotificationName; ``` 看上去比類型常量長一些,不過似乎還算比較好看 定義的時候需要這樣定義: ``` @interface XXXConstantNotificationNames : NSObject @property(nonatomic, readonly) NSString *XXXUserDidLoginNotificationName; @end @interface XXXConstant : NSObject @property(nonatomic, class, copy) XXXConstantNotificationNames *notificationNames; @end ``` 并且,類常量是不會被 synthesize 的,也就是說編譯器不會自動為類常量創建相應的變量,所以在實現文件中,我們需要這么寫 ``` @implementation XXXConstantNotificationNames - (NSString *)XXXUserDidLoginNotificationName { return @"XXXUserDidLoginNotificationName"; } @end @implementation XXXConstant static XXXConstantNotificationNames *_notificationNames = nil; + (void)load { _notificationNames = [[XXXConstantNotificationNames alloc] init]; } - (XXXConstantNotificationNames *) { reutrn _notificationNames; } @end ``` 看上去比定義一個 `kXXXUserDidLoginNotificationName` 字符串常量,麻煩了非常多,但是相信在項目代碼量不斷增加,以及工程變得越來越復雜以后,這樣的做法對于代碼管理上是非常有幫助的
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看