ObjectC 上手

本文結(jié)構(gòu)

參考孟巖老師的文章圣贸,對本文結(jié)構(gòu)如下劃分

  • 基本數(shù)據(jù)類型
  • 基本語法
  • 數(shù)組和其他集合類
  • 基本輸入輸出和文件處理艾扮,輸入輸出流類的組織
  • 序列化和反序列化
  • 面向?qū)ο筇匦?/li>
  • 異常咧欣、錯誤處理臭墨、斷言、日志和調(diào)試支持润讥,對單元測試的支持
  • RunTime
  • callback方法調(diào)用转锈,事件驅(qū)動編程模型

參考鏈接

在完成本文過程中,或轉(zhuǎn)載楚殿,或參考了以下鏈接

  • 匿名函數(shù)

http://blog.devtang.com/2013/07/28/a-look-inside-blocks/
http://coolshell.cn/articles/8309.html
http://www.reibang.com/p/29d70274374b

  • 集合

http://blog.csdn.net/whoten/article/details/17892673
http://blog.sunnyxx.com/2014/04/30/ios_iterator/

  • 屬性

http://www.devtalking.com/articles/you-should-to-know-property/
http://www.reibang.com/p/2a9c98a29685

  • 斷言&錯誤&日志

http://blog.csdn.net/lcl130/article/details/41889185
http://www.reibang.com/p/6e444981ab45

  • 面向?qū)ο?/li>

http://ios.jobbole.com/83082/

  • 內(nèi)存

http://blog.devtang.com/2016/07/30/ios-memory-management/

  • 單元測試

https://hjgitbook.gitbooks.io/ios/content/01-thinking/01-the-basic-knowledge-of-unit-test.html
http://www.reibang.com/p/8bbec078cabe
http://www.cocoachina.com/ios/20150702/12253.html

  • 類別

https://tech.meituan.com/DiveIntoCategory.html

  • 回調(diào)

http://blog.csdn.net/wzzvictory/article/details/9295317
http://www.cnblogs.com/TsengYuen/archive/2011/04/20/2022060.html
http://www.reibang.com/p/376ba5343097
https://segmentfault.com/q/1010000000387240
http://wdxtub.com/2016/02/20/dive-in-objc-1/

  • 文件&流

http://www.reibang.com/p/fbb997eb032d
http://blog.csdn.net/swingpyzf/article/details/16325923

  • 運行時

http://www.reibang.com/p/f73ea068efd2
http://yulingtianxia.com/page/8/

  • 其他

http://blog.csdn.net/myan/article/details/3144661
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html
http://www.reibang.com/p/8b76814b3663
https://wiki.haskell.org/Cn/Introduction#Quicksort_in_Haskell
https://github.com/oa414/objc-zen-book-cn#%E9%9D%A2%E5%90%91%E5%88%87%E9%9D%A2%E7%BC%96%E7%A8%8B

基本數(shù)據(jù)類型

  • C的基本數(shù)據(jù)類型
    objC作為C語言的一個超集撮慨,所有C語言支持的基本數(shù)據(jù)類型竿痰,ObjC同樣支持

  • int 與 NSInteger

    NSInteger的定義如下:

    #if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || T  ARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
    typedef long NSInteger;
    typedef unsigned long NSUInteger;
    #else
    typedef int NSInteger;
    typedef unsigned int NSUInteger;
    #endif
    
    

    很明顯,做了32位64位機器的移植性處理。

  • BOOL

    YES 或 NO

  • float 與 CGFloat

    CGFloat對于float相當于NSInteger對于int

  • NSString

    后面單獨介紹砌溺。

    需要注意的是:NSString類型的等號賦值做的是深拷貝

  • NSValue

    NSValue是個可以和各種基本數(shù)據(jù)類型相互轉(zhuǎn)換的類影涉。

    [NSValue valueWithCGSize:CGSizeMake(100, 100)];
    [NSValue valueWithRange:NSMakeRange(0, 10)];
    
  • NSNumber

    NSNumber與上面不同的是,NSNumber不是基本的數(shù)據(jù)類型,而是對象规伐。
    繼承關(guān)系:

    NSNumber->NSValue->NSObject

    同時NSNumber支持和NSString一樣的@符號簡寫

    NSNumber * number = @(123);
    NSNumber * number1 = @(3.1415);
    NSNumber * number2 = @(YES);
    
    NSInteger intValue = [number integerValue];
    CGFloat floatValue = [number1 doubleValue];
    BOOL boolValue = [number2 boolValue];
    
    

基本語法

  • 減號和加號

    1. 減號表示一個函數(shù)或消息的開始

      舉個例子蟹倾,在c#中,一個方法的寫法是

      private void add(bool isAdd){
          ...
      }
      

      用oc寫出來就是

      -(void)add:(Bool)isAdd{
          ...
      }
      
    2. 加號代表是類的靜態(tài)方法猖闪,不需要實例化即可調(diào)用鲜棠。

  • 中括號

    中括號可以理解為調(diào)用方法,在oc中培慌,嚴格來說豁陆,應(yīng)該表述為發(fā)消息

    具體理解如下:

    因為在Objective-C中,message與方法是在執(zhí)行階段綁定的吵护,而不是編譯階段盒音。簡單的說 [a someFunc] 這樣一個調(diào)用,在編譯階段馅而,編譯器并不知道someFunc要執(zhí)行哪段代碼祥诽。這個時候[a someFunc]會被轉(zhuǎn)換為 objc_msgSend(a, "someFunc"),字面的意思也很容易理解瓮恭,就是給a這個instance雄坪,發(fā)“someFunc”這個消息,以selector的形式偎血。在運行階段,執(zhí)行到上述的objc_msgSend這個函數(shù)時盯漂。函數(shù)內(nèi)部會到a對應(yīng)的內(nèi)存地址颇玷,尋找someFunc這個方法的地址,并執(zhí)行就缆。如果找不到帖渠,就會拋一個“unknown selector sent to instance”的異常。(比如.h中聲明了方法竭宰,但.m中沒有實現(xiàn)空郊,就可以重現(xiàn)這個錯誤) 所以嚴格意義上來將,任何Objective C的函數(shù)調(diào)用切揭,編譯階段的表現(xiàn)狞甚,都只能算一種“發(fā)消息”的行為。

    深入理解可見:Objective-C 消息發(fā)送與轉(zhuǎn)發(fā)機制原理

    總之廓旬,從語法層面上來說哼审。

    在C#中,我們這樣寫:

    this.hello(true);
    

    在oc中,我們這樣寫:

    [self hello:YES];
    
  • #import @interface

    1. #import 和 #include

      #import可以認為是#include的升級版涩盾,使用#import可以保證頭文件不會重復(fù)引用十气。

      在c中,防止頭文件的重復(fù)引用春霍,常吃椅鳎可見類似如下代碼

      #ifndef xxx_H
      #define xxx_H
      #include "xxx.h"
      #endif
      

      通常建議是:oc文件使用#import形式,c\c++文件使用#include形式址儒。

      詳見

    2. @interface 和 @implementation

      用一個簡單的例子理解芹枷,定義一個老鷹捉小雞類

      使用c#

      public class Chicken : System{
          private string ckName = "chick";
          private int ckSize = 15;
          
          private bool IsCaught(){
              return true;
          }
      }
      

      使用oc

      • Chicken.h
      @interface Chicken : NSObject{
          NSString *ckName;
          int ckSize;
      }
      
      -(BOOL)IsCaught:;
      @end
      
      • Chicken.mm
      #import "Chicken.h"
      @implementation Chicken
      
      -(void)init{
          ckName=@"chick";
          ckSize=15;
      }
      
      -(BOOL) IsCaught:{
          return YES;
      }
      
      @end
      
  • 參數(shù)格式以及參數(shù)的傳遞

    1. 多個參數(shù)的寫法

      (方法的數(shù)據(jù)類型)方法名:(參數(shù)1數(shù)據(jù)類型)參數(shù)1數(shù)值名 參數(shù)2名:(參數(shù)2數(shù)據(jù)類型)參數(shù)2數(shù)值名 參數(shù)3名:(參數(shù)3數(shù)據(jù)類型)參數(shù)3數(shù)值名 ...

    2. 參數(shù)傳遞

      舉個例子

      [[[MyClass alloc] init:[foo bar]] autorelease] 
      

      對應(yīng)于

      MyClass.alloc().init(foo.bar()).autorelease()
      

數(shù)組和其他集合類

  • Foundation framework中用于收集cocoa對象(NSObject對象)的三種集合

  • NSArray 用于對象有序集合(數(shù)組)

  • NSSet 用于對象無序集合 (集合)

  • NSDictionary用于鍵值映射(字典)

以上三種集合類是不可變的(一旦初始化后,就不能改變)

  • 對應(yīng)的可變集合類(這三種可變集合類是對應(yīng)上面三種集合類的子類):

  • NSMutableArray

  • NSMutableSet 可修改的集合离福。主要用于集合運算(并集杖狼,交集,差集)

  • NSMutableDictionary 允許用戶添加和刪除key和value

這些集合類只能容納cocoa對象(NSOjbect對象)妖爷,如果想保存一些原始的C數(shù)據(jù)(例如蝶涩,int, float, double, BOOL等),則需要將這些原始的C數(shù)據(jù)封裝成NSNumber類型進行存儲絮识。NSNumber對象是cocoa對象绿聘,可以被保存在集合類中。

  • 遍歷

  • 索引

    NSArray *array = [NSArray arraywithobjects:@"1",@"2",@"3",@"4",nil];  
    

NSUInteger count = [array count];
for (int i = 0 ; i ! = count;i++){
id obj = [array objectAtIndex:i];
//自定義code...
}

 
 - 迭代器
 

NSEnumerator *enumerator = [array objectEnumerator];
id obj = nil;
while(obj = [enumerator nextobject]){
//自定義code
}


- 快速枚舉

for(id obj in array){
//自定義code
}

***字典使用快速枚舉時次舌,得到的obj是key而不是keypair***

- 代碼塊

 為什么使用代碼塊熄攘,因為代碼塊可以讓循環(huán)操作并發(fā)執(zhí)行。而上面的三種方式都是線性操作彼念。
 
 ```
 [array enumerateObjectsUsingBlock:^(id obj,NSUInteger idx,BOOL *stop){  
 //obj為取出的對象挪圾,idx為對應(yīng)的下標  
}];     
 ```
 
 ```
 if(idx == 1){  
*stop = YES;  
}  
 ```
 
 - 技巧

     1. 倒序遍歷

     NSArray和NSOrderedSet都支持使用reverseObjectEnumerator倒序遍歷,如:
     
     ```
     NSArray *strings = @[@"1", @"2", @"3"];
for (NSString *string in [strings reverseObjectEnumerator]) {
 NSLog(@"%@", string);
}
     ```
     這個方法只在循環(huán)第一次被調(diào)用逐沙,所以也不必擔心循環(huán)每次計算的問題哲思。

     同時,使用enumerateObjectsWithOptions:NSEnumerationReverse也可以實現(xiàn)倒序遍歷:

     ```
     [array enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(Sark *sark, NSUInteger idx, BOOL *stop) {
 [sark doSomething];
}];

     ```
     
     2.使用block同時遍歷字典key吩案,value
     
     block版本的字典遍歷可以同時取key和value(forin只能取key再手動取value)棚赔,如:
     
     ```
     NSDictionary *dict = @{@"a": @"1", @"b": @"2"};
 [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL  *stop) {
 NSLog(@"key: %@, value: %@", key, obj);
 }];[]() 
     ```


## 基本輸入輸出和文件處理,輸入輸出流類的組織

- 輸入輸出

 兼容C的scanf徘郭,printf靠益,不再敘述。

- 文件處理

 - 基本讀寫操作

     Objective-C使用NSFileHandle類對文件進行基本操作,iOS文件操作

     NSFileHandle類中得方法可以對文件進行基本的讀寫残揉,偏移量的操作胧后。
     NSFileHandle基本步驟:
     
     1. 打開文件,獲取一個NSFileHandle對象抱环。
     2. 對打開NSFileHandle的文件對象行I/O操作
     3. 關(guān)閉文件對象

 - 簡單對象的讀寫(I/O)操作

     iOS中提供四種類型(包括其子類型)可以直接進行文件存燃薄:
     
     1. NSString
     2. NSDictionary
     3. NSArray
     4. NSData

     其基本操作如下:
     
     ``` 
     // 在Documents下面創(chuàng)建一個文本路徑途样,假設(shè)文本名稱為objc.txt
     NSString *txtPath = [docPath stringByAppendingPathComponent:@"objc.txt"]; // 此時僅存在路徑,文件并沒有真實存在
     NSString *string = @"Objective-C";
 // 字符串寫入時執(zhí)行的方法
 [string writeToFile:txtPath atomically:YES encoding:NSUTF8StringEncoding error:nil];
 NSLog(@"txtPath is %@", txtPath);
 // 字符串讀取的方法
 NSString *resultStr = [NSString stringWithContentsOfFile:txtPath encoding:NSUTF8StringEncoding error:nil];
 NSLog(@"resultStr is %@", resultStr);

     ```

 - 文件管理器

     使用文件管理器(NSFileManager)可以實現(xiàn)對文件進行操作(創(chuàng)建濒憋、刪除何暇、改名等)以及文件信息的獲取

 - 流

     使用Cocoa框架中的輸入輸出流,可以從文件或應(yīng)用中內(nèi)存讀取數(shù)據(jù)凛驮,也可以向文件/應(yīng)用中內(nèi)存寫入數(shù)據(jù)裆站。還可以用于socket的數(shù)據(jù)交互處理。
     
     其主要類與方法如下:
     
     ![NSStream的主要類與方法](http://upload-images.jianshu.io/upload_images/6836572-9f75de0b6c31847d.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
     
## 序列化和反序列化


前面提到過黔夭,NSArray,NSDictionary,NSString,NSNumber,NSDate,NSData以及它們的可變版本(指NSMutableArray,NSMutableDictionary...這一類) 宏胯,都可以方便的將自身的數(shù)據(jù)以某種格式(比如xml格式)序列化后保存成本地文件。

但是如果用于存放數(shù)據(jù)的類是自己定義的本姥,并不是上面這些預(yù)置的對象肩袍,如自定以的Person類,像這種自定義的類是無法在程序內(nèi)部通過writeToFile這個方法寫入到文件內(nèi)

既然復(fù)雜對象無法使用內(nèi)部方法進行數(shù)據(jù)持久化婚惫,那么只能通過將復(fù)雜對象轉(zhuǎn)換成NSData氛赐,然后在通過上面的方法寫入文件,而這種轉(zhuǎn)換的步驟就被稱為歸檔先舷,從文件中讀取NSData數(shù)據(jù)艰管,將NSData轉(zhuǎn)換為復(fù)雜對象,這個步驟就是反歸檔蒋川。

- 要點

 - 復(fù)雜對象寫入文件的過程(復(fù)雜對象->歸檔->NSData->writeToFile)
 - 從文件中讀取出復(fù)雜對象過程(讀取文件->NSData->反歸檔->復(fù)雜對象

- 實現(xiàn)步驟

 1. 首先牲芋,復(fù)雜對象所屬的類要遵守<NSCoding>
 2. 其次,實現(xiàn)協(xié)議中的兩個方法:
     - -(void)encodeWithCoder:(NSCoder *)aCoder; 序列化
     - -(id)initWithCoder:(NSCoder *)aDecoder; 反序列化

- 例子

 1. 首先捺球,遵守NSCoding協(xié)議

     ```
     @interface Person:NSObject<NSCoding> 
         @property(nonatomic,copy) NSString *name 
         @property(nonatomic,assign) integer age; 
 @end
 
     ```
 2. 其次缸浦,實現(xiàn)協(xié)議中的兩個方法:

     ```
     // 對person對象進行歸檔時,此方法執(zhí)行氮兵。
     // 對person中想要進行歸檔的所有屬性裂逐,進行序列化操作。
     -(void)encodeWithCoder:(NSCoder *)aCoder
 {
  [aCoder encodeObject:self.name forKey:@"name"];
 [aCoder encodeInteger:self.age forKey:@"age"];
 }
 // 對person對象進行反歸檔時胆剧,該方法執(zhí)行絮姆。
 // 創(chuàng)建一個新的person對象醉冤,所有屬性都是通過反序列化得到的秩霍。
 -(id)initWithCoder:(NSCoder *)aDecoder 
 {
   self = [super init];
   if (self) {
     self.name = [aDecoder decodeObjectForKey:@"name"];
    self.age = [aDecoder decodeIntegerForKey:@"age"];
  }
  return self;
 }
 // 準備一個NSMutableData, 用于保存歸檔后的對象
 NSMutableData *data = [NSMutableData data];
 // 創(chuàng)建歸檔工具
 NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingMutableData:data];
 // 歸檔
 [archiver encodeObject:p] forKey:@"p1"];
 // 結(jié)束
 [archiver finishEncoding];
 // 拼音寫入沙盒路徑
 NSString *caches = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
NSString *filePath = [caches stringByAppendingPathComPonent:@"person"];
// 寫入沙盒
[data writeToFile:filePath atomically:YES];
// 反歸檔
// 從filePath文件路徑讀取
NSData *data = [NSData dataWithContentsOfFile:filePath];
// 反歸檔工具
NSKeyedUnarchiver *unArchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
// 反歸檔成對象
Person *p2 = [unArchiver decodeObjectForKey:@"p1"];
// 反歸檔結(jié)束
[unArchiver finshDeoding];  
 
     ```
     
## 面向?qū)ο筇匦?
- 繼承&多態(tài)

objc的這兩個特性除了使用語法上,與其他oo語言沒有什么什么不同蚁阳×迦蓿基本語法之前也有提過,@interface用于聲明一個類螺捐,@implementation用于類的定義颠悬。

不過需要值得注意的一點是矮燎,由于objc的動態(tài)消息傳遞機制,objc中不存在真正意義上的私有方法赔癌。但是如果方法不再.h文件中聲明诞外,而只在.m文件中實現(xiàn),基本上也和私有方法差不多灾票。

所以在使用objc時峡谊,一般會將公有的放到.h文件中,將私有的放到.m文件中刊苍。實現(xiàn)私有一般用類擴展實現(xiàn)既们。

- 屬性

在屬性方面,objc相比于其他oo語言正什,有自己的語法糖啥纸,@Property和@synthesize忧设。簡單來說荆萤,.h文件中使用@Property, .m文件中使用@synthesize劳坑,編譯器會自動創(chuàng)建成員屬性以及對應(yīng)的get和set方法莹妒。

比較值得關(guān)注的名船,是@Property的幾個特性。

 - 原子性

     1. atomic(默認使用)旨怠。使用該選項渠驼,保證調(diào)用者對變量的訪問是線程安全的,在多線程環(huán)境下會返回一個多多個值(其他線程前后)鉴腻,而不是一個垃圾值迷扇。

     2. noaomic。如其名爽哎,使用該關(guān)鍵字后蜓席,不能保證線程安全,但是性能及訪問效率優(yōu)于前值课锌。所以在單線程環(huán)境下一般指定該關(guān)鍵字厨内。

 - 寄存器控制

   1. readwrite(默認):readwrite是默認值,表示該屬性同時擁有setter和getter渺贤。

  2. readonly: readonly表示只有g(shù)etter沒有setter雏胃。

 - 內(nèi)存管理 

   1. assign(默認):assign用于值類型,如int志鞍、float瞭亮、double和NSInteger,CGFloat等表示單純的復(fù)制固棚。

      其在set的實現(xiàn)统翩,是采用直接賦值來實現(xiàn)設(shè)值操作的

     ```
       -(void)setVar:(int)newVar{  
         var= newVar;  
     } 
     ```

   2. retain:在set方法中仙蚜,需要對傳入的對象進行引用計數(shù)加1的操作

     簡單來說,就是對傳入的對象擁有所有權(quán)厂汗,只要對該對象擁有所有權(quán)委粉,該對象就不會被釋放。如下代碼所示:

    ```
   -(void)setName:(NSString*)_name{  
  if ( name != _name){  
       [name release];  
       name = [_name retain];  
  }  
}
   ```

     首先判斷是否與舊對象一致娶桦,如果不一致進行賦值艳丛。之所以要增加if判斷,是因為如果是同一個對象的話趟紊,進行if內(nèi)的代碼會造成一個極端的情況:當此name的retain為1時氮双,使此次的set操作讓實例name提前釋放,而達不到賦值目的

3. strong:表示實例變量對傳入的對象要有所有權(quán)關(guān)系霎匈,即強引用戴差。strong跟retain的意思相同并產(chǎn)生相同的代碼,但是語意上更好更能體現(xiàn)對象的關(guān)系铛嘱。

4. weak:在set方法中暖释,需要對傳入的對象不進行引用計數(shù)加1的操作。
簡單來說墨吓,就是對傳入的對象沒有所有權(quán)球匕,當該對象引用計數(shù)為0時,即該對象被釋放后帖烘,用weak聲明的實例變量指向nil亮曹,即實例變量的值為0。

5. copy:與strong類似秘症,但區(qū)別在于實例變量是對傳入對象的副本擁有所有權(quán)照卦,而非對象本身

- Category

這算是oc的一個比較有意思特性。如果我們想給一個已存在的乡摹、很復(fù)雜的類添加一個新的方法(包括系統(tǒng)類)役耕。一般來說,對于自定義類聪廉,我們會找源碼瞬痘,然后添加新方法。但是如果我們新增的邏輯也很復(fù)雜板熊,這樣就會擴大原始設(shè)計的規(guī)模框全,有可能會打亂整個設(shè)計的結(jié)構(gòu)。

Category就是oc提供的為我們解決這一問題的方法邻邮。它可以讓我們動態(tài)的在已經(jīng)存在的類中添加新的方法竣况。對類進行擴展時不需要訪問其源碼克婶,也不需要創(chuàng)建子類筒严。

Category的實現(xiàn)很簡單丹泉,舉個例子。

 ```
 // Deck.h

 #import <Foundation/Foundation.h>
 #import "Card.h"

 @interface Deck : NSObject

 - (Card *)randomDrawCard;

 @end
 ```
 
 這是類Deck的聲明文件鸭蛙,其中包含一個實例方法randomDrawCard摹恨,如果我們想在不修改原始類、不增加子類的情況下娶视,為該類增加一個drawCardFromTop方法晒哄,只需要定義兩個文件Deck+DrawCardFromTop.h和Deck+DrawCardFromTop.m,在聲明文件和實現(xiàn)文件中用()把Category的名稱括起來即可肪获,聲明文件如下:

 ```
 // Deck+DrawCardFromTop.h

 #import "Deck.h"
 #import "Card.h"

 @interface Deck(DrawCardFromTop)

 - (Card *)drawCardFromTop;

 @end
 ```

 實現(xiàn)文件如下:

 ```
 // Deck+DrawCardFromTop.m

 #import "Deck+DrawCardFromTop.h"
 #import "Card.h"

 @implementation Deck(DrawCardFromTop)

 - (Card *)drawCardFromTop
 {
     //TODO.....
 }

 @end
 ```

 DrawCardFromTop是Category的名稱寝凌。這里一般使用約定俗成的習慣,將聲明文件和實現(xiàn)文件統(tǒng)一采用”原類名+Category名”的方式命名孝赫。
使用也非常簡單较木,引入Category的聲明文件,然后正常調(diào)用即可:

 ```
 // main.m

 #import "Deck+DrawCardFromTop.h"
 #import "Card.h"

 int main(int argc, char * argv[])
 {

     Deck *deck = [[Deck alloc] init];
     Card *card = [deck drawCardFromTop];

    return 0;

 }
 ```

 使用類別(Category)青柄,不僅在團隊協(xié)作開發(fā)時帶來方便(至少不會因為同時更改一個文件伐债,svn更新后需要自己解決沖突)。當一些基礎(chǔ)類庫滿足不了我們的需求時我們還可以拓展基礎(chǔ)類庫致开。

 舉個例子峰锁,如果我們要分割一個字符串,然后用一個NSArray記錄每個分割子串的長度双戳。也就是說虹蒋,NSArray每個元素保存子NSstring的length。但是飒货,如之前提到千诬,NSArray只能保存NS對象,基本值類型(在這里是int)無法保存膏斤,扎心了徐绑。于是我們每次都要先獲取子Nstring的length,然后轉(zhuǎn)換為NSnumber莫辨,再保存于數(shù)組中傲茄。此時,我們完全可以擴展NSstring沮榜,使其在獲取長度時返回NSnumber對象盘榨。
 
 使用Category,還可以實現(xiàn)類擴展蟆融。前面說過草巡,objc中定義私有的屬性和方法,一般用class extension實現(xiàn)型酥。其特點如下:
 
 - 不需要名字
 - 可以在自己的類中使用
 - 可以添加實例變量
 - 可以將只讀權(quán)限修改為可讀寫權(quán)限
 - 創(chuàng)建數(shù)量不限

 舉個例子:
     ```
   //Things.h
 @interface Things : NSObject
 @proterty (assign) NSInteger thing1;
 @ptoterty (readonly, assign) NSInteger thing2;
 -(void)resetAllVal;
 @end
     ```

    ```
   //Things.m
 
 @interface Things(){
     NSInteger thing4;
 }
 
 @proterty (readwrite, assign) NSInteger thing2;
 @proterty (assign) NSInteger thing3;
 
 @end
 
 @implementation
 
 ...
 
 @end
 ```
 
 我們使用了類擴展山憨,添加了私有實例變量和私有屬性查乒,還修改了thing2的讀寫權(quán)限,其對外只提供讀郁竟,對內(nèi)可讀寫玛迄。

 但是Category不是萬能的,Category可以訪問原始類的實例變量棚亩,但不能添加變量蓖议,如果想添加變量,可以考慮通過繼承創(chuàng)建子類讥蟆。
 
 - 類擴展與類別的區(qū)別:
     
     1. 類別中只能增加方法
     
     2. 類擴展不僅可以增加方法勒虾,還可以增加實例變量(或者合成屬性),只是該實例變量默認是@private類型的(作用范圍只能在自身類瘸彤,而不是子類或其他地方)从撼;
     3. 類擴展中聲明的方法沒被實現(xiàn),編譯器會報警钧栖,但是類別中的方法沒被實現(xiàn)編譯器是不會有任何警告的低零。這是因為類擴展是在編譯階段被添加到類中,而類別是在運行時添加到類中拯杠。
     4. 類擴展不能像類別那樣擁有獨立的實現(xiàn)部分(@implementation部分)掏婶,也就是說,類擴展所聲明的方法必須依托對應(yīng)類的實現(xiàn)部分來實現(xiàn)潭陪。

- 匿名函數(shù)(block)

 objc中的block相當于c中的函數(shù)指針雄妥。二者仍有一定區(qū)別,如下
 
 - block的代碼是內(nèi)聯(lián)的依溯,效率高于函數(shù)調(diào)用
 - block對于外部變量默認是只讀屬性
 - block被Objective-C看成是對象處理

 block聲明和定義語法如下圖所示老厌。

 ![objc聲明與語法定義](http://upload-images.jianshu.io/upload_images/6836572-f34f5ff05a3b806b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
 
 block特性如下
 
 - 捕獲外界便變量

     ```
     CGPoint center = cell.center;
     CGPoint startCenter = center;
     startCenter.y += LXD_SCREEN_HEIGHT;
     cell.center = startCenter;

     [UIView animateWithDuration: 0.5 delay: 0.35 *      indexPath.item usingSpringWithDamping: 0.6      initialSpringVelocity: 0 options:       UIViewAnimationOptionCurveLinear animations: ^{
     cell.center = center;
     } completion: ^(BOOL finished) {
     NSLog("animation %@ finished", finished? @"is": @"isn't");
     }];
     ```

     這里面就用到了void(^animations)(void)跟void(^completion)(BOOL       finished)兩個block,系統(tǒng)會在動畫開始以及動畫結(jié)束的時候分別調(diào)用者兩個       block黎炉。在實現(xiàn)動畫的block內(nèi)部枝秤,代碼訪問了上文中的center屬性——在動畫開     始的時候這個動畫函數(shù)的生命周期早已結(jié)束,而block會捕獲代碼外的局部變量慷嗜,      當然這只局限于只讀操作淀弹。如果我們在block中修改外部變量,編譯器將會報錯庆械。
     
     同時薇溃,block在捕獲變量的時候只會保存變量被捕獲時的狀態(tài)(對象變量除外),之后即便變量再次改變缭乘,block中的值也不會發(fā)生改變沐序。見下面代碼:
     
     ```
     CGPoint center = CGPointZero;
     CGPoint (^pointAddHandler)(CGPoint addPoint) = ^(CGPoint addPoint) {
     return CGPointMake(center.x + addPoint.x, center.y + addPoint.y);
}
     center = CGPointMake(100, 100);
     NSLog(@"%@", pointAddHandler(CGPointMake(10, 10)));    //輸出{10,10}
     
     ```
     
     要想在block內(nèi)部修改外部變量,可以給變量增加 _block關(guān)鍵字。     
     
 - 循環(huán)引用

     前面說過策幼,block在iOS開發(fā)中被視作是對象邑时,因此其生命周期會一直等到持有者     的生命周期結(jié)束了才會結(jié)束。另一方面垄惧,由于block捕獲變量的機制,使得持有       block的對象也可能被block持有绰寞,從而形成循環(huán)引用到逊,導(dǎo)致兩者都不能被釋放:
 
     ```
     @implementation LXDObject
     {
        void (^_cycleReferenceBlock)(void);
     }

     - (void)viewDidLoad
     {
       [super viewDidLoad];
       _cycleReferenceBlock = ^{ 
      NSLog(@"%@", self);   //引發(fā)循環(huán)引用
      };
     }

     @end

     ```
 
     這種情況最后會導(dǎo)致內(nèi)存泄露,兩者都無法釋放滤钱。跟普通變量存在__block關(guān)鍵字     一樣的觉壶,系統(tǒng)提供給我們__weak的關(guān)鍵字用來修飾對象變量,聲明這是一個弱引用     的對象件缸,從而解決了循環(huán)引用的問題铜靶。
 
     ```
     __weak typeof(*&self) weakSelf = self;
     _cycleReferenceBlock = ^{ 
     NSLog(@"%@", weakSelf);   //弱指針引用,不會造成循環(huán)引用
     };
     
     ```
     
## 異常他炊、錯誤處理争剿、斷言、日志和調(diào)試支持痊末,對單元測試的支持


- 異常

 老生常談的try catch蚕苇,與其他oo語言一樣,不再多述凿叠。
 
 ```
  @try {
     // do something that might throw an exception
 }
 @catch (NSException *exception) {
     // deal with the exception
 }
 @finally {
     // optional block of clean-up code
     // executed whether or not an exception occurred
 }
 
 ```
 
- 錯誤處理

 NSError是objc的系統(tǒng)錯誤信息類涩笤。其有三個較重要的私有變量:
 
 - code
 
     是一個整數(shù),最好是一個枚舉盒件,和特定的錯誤域是對應(yīng)的蹬碧。

 - domain

     一個字符串,標記錯誤域炒刁。
     
 - userInfo

     一個字典恩沽,包括任意的鍵值對。其中有:
     
     1. NSLocalizedDescriptionKey:本地化的錯誤描述
     2. NSLocalizedRecoverySuggestionErrorKey:本地化的恢復(fù)建議
     3. NSLocalizedFailureReasonErrorKey:本地化的失敗原因

 NSError主要有兩個用法:
 
 - 獲取錯誤信息

     ```
     //獲取錯誤  
     NSError *error = nil;  
     BOOL success = [[NSFileManager defaultManager]      contentsOfDirectoryAtPath:@"path" error:&error];  
     if (!success) {  
      NSLog(@"%@", [error localizedDescription]);  
     } 
     ```

 - 編輯錯誤信息
     ```
     //預(yù)定義信息  
     #define JohnnyErrorDomain       @"com.JohnnyError.Domain"  

     typedef NS_ENUM(NSInteger, ErrorFail){  
      ErrorOne = 1,  
      ErrorTwo,  
      ErrorThree  
     };  
     ```
     
     ``` 
     //產(chǎn)生錯誤信息  
     NSDictionary *userInfo = @{  
                        NSLocalizedDescriptionKey: NSLocalizedString(@"Operation fail", nil),  
                        NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"The operation timed out.", nil),  
                        NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Have you tried turning it off and on again?", nil)  
                        };  
     NSError *error = [NSError errorWithDomain:JohnnyErrorDomain  
                                  code:2  
                              userInfo:userInfo];  
     //提示  
     [[[UIAlertView alloc]   initWithTitle:error.localizedDescription  
                             message:error.localizedRecoverySuggestion  
                             delegate:nil  
                             cancelButtonTitle:NSLocalizedString(@"OK", nil)  
                             otherButtonTitles:nil, nil nil] show];  
     
     ```

- 斷言

 - NsAsest 

     NSAssert()是一個宏翔始,用于開發(fā)階段調(diào)試程序中的Bug飒筑,通過為NSAssert()傳       遞條件表達式來斷定是否屬于Bug,滿足條件返回真值绽昏,程序繼續(xù)運行协屡,如果返回       假值,則拋出異常全谤,并且可以自定義異常描述肤晓。
     NSAssert()是這樣定義的
     
     ```
     #define NSAssert(condition, desc)
     
     ```

     NSAssert用法
     
     ```
     int a = 1;
     NSCAssert(a == 2, @"a must equal to 2"); //第一個參數(shù)是條件,如果第一個參數(shù)不滿足條件,就會記錄并打印后面的字符串
     
     ```

 - NSParameterAssert

     NSAssert和 NSParameterAssert的區(qū)別是前者是針對條件斷言, 后者只是針對參數(shù)是否存在的斷言, 調(diào)試時候可以結(jié)合使用,先判斷參數(shù),再進一步斷言,確認原因.
     
     NSParameterAssert用法
     
     ```
         - (void)assertWithPara:(NSString *)str
 {
   NSParameterAssert(str); //只需要一個參數(shù),如果參數(shù)存在程序繼續(xù)運行,如果參數(shù)為空,則程序停止打印日志
 //further code ...
 }
     
     ```
     
 - 自定義NSAssertionHandler

     Objc中的斷言處理使用的是 NSAssertionHandler补憾。
每個線程擁有它自己的斷言處理器漫萄,它是 NSAssertionHandler 類的實例對象。NSAssertionHandler實例是自動創(chuàng)建的盈匾,用于處理錯誤斷言腾务。如果 NSAssert和NSCAssert條件評估為錯誤,會向 NSAssertionHandler實例發(fā)送一個表示錯誤的字符串削饵。每個線程都有它自己的NSAssertionHandler實例岩瘦。
我們可以自定義處理方法,從而使用斷言的時候窿撬,控制臺輸出錯誤启昧,但是程序不會直接崩潰。

 ```
     #import "MyAssertHandler.h"

     @implementation MyAssertHandler

     //處理Objective-C的斷言
     - (void)handleFailureInMethod:(SEL)selector object:     (id)object file:(NSString *)fileName lineNumber:        (NSInteger)line description:(NSString *)format,...
         {
       NSLog(@"NSAssert Failure: Method %@ for object %@ in      %@#%li", NSStringFromSelector(selector), object, fileName,      (long)line);
     }
     //處理C的斷言
     - (void)handleFailureInFunction:(NSString *)functionName        file:(NSString *)fileName lineNumber:(NSInteger)line        description:(NSString *)format,...
     {
       NSLog(@"NSCAssert Failure: Function (%@) in %@#%li",      functionName, fileName, (long)line);
     }

     @end
 
 ```
 
 給線程添加處理類
 
 ```
 NSAssertionHandler *myHandler = [[MyAssertHandler alloc] init];
//給當前的線程
[[[NSThread currentThread] threadDictionary] setValue:myHandler
                                            forKey:NSAssertionHandlerKey];

 ```

 自定義NSAssertionHandler后,程序能夠獲得斷言失敗后的信息,但是程序可以繼續(xù)運行,不會強制退出程序.

- 日志
 
 objc中日志輸出處理主要使用的是NSLog劈伴,要在日志輸出信息中添加上下文信息密末,編譯器提供了常用的表達式。

| Expression  | Format Specifier | Description |
 |-----------------|------------------------|--- -------------|
 |NSStringFromSelector(_cmd)      | %@ |         當前選擇器的名字 |
 | NSStringFromClass([self class])     | %@        |           當前對象類的名字 |
 | [[NSString stringWithUTF8String:\__FILE__] lastPathComponent] | %@        |            源碼文件的名稱|
 | [NSThread callStackSymbols] | %@ |    當前棧信息的刻度字符串數(shù)組跛璧。僅用于調(diào)試严里,不用向終端用戶展示或者在代碼中用作任何邏輯。|


- 單元測試
 
 objc中可以使用OCUnit(即用XCTest進行測試)其實就是蘋果自帶的測試框架追城。
 一般測試用例分為三個階段:排列資源田炭、執(zhí)行行為、斷言結(jié)果漓柑。
 
 - 排列資源

     排列資源教硫,便是提供一切測試方法所需要的東西,而這些東西便稱之為資源辆布。這些資源包括:

     1. 方法的輸入?yún)?shù)
     2. 方法所執(zhí)行的特定上下文
 
     這個階段相當于準備階段瞬矩,一切都是為了這個用例中執(zhí)行行為而作準備,如果沒有任何需要準備的數(shù)據(jù)锋玲,這個階段是可以被忽略的景用。

 ```
     - (void)test_setObject$forKey {
 // arrange
 NSString *key = @"test_key";
 NSString *value = @"test_value";
 NSMutableDictionary *dic = [NSMutableDictionary new];
}
     
     ```

 - 執(zhí)行行為

     當準備階段完畢后,便進入要測試行為的執(zhí)行階段惭蹂,在這個階段伞插,我們會使用準備好的資源,并記錄下行為的輸出以供下個階段使用盾碗。這里的行為輸出不一定就是方法執(zhí)行的返回值媚污,很多時候我們要測試的方法并沒有任何返回值,但一個方法執(zhí)行后廷雅,總歸會有一個預(yù)期的行為會發(fā)生耗美,即便是空方法也是(什么都不會被改變)京髓,而這個預(yù)期行為便是測試行為的輸出。

     加入執(zhí)行行為的代碼:  
  ```
  - (void)test_setObject$forKey {
 // arrange
 NSString *key = @"test_key";
 NSString *value = @"test_value";
 NSMutableDictionary *dic = [NSMutableDictionary new];

  // act
 [dic setObject:value forKey:key];
 }
     
 ```

 - 斷言結(jié)果

     最后一步商架,也是最核心的一步堰怨,它決定著一個測試用例的成功與否,我們需要在這一步斷言執(zhí)行行為的輸出是否達到預(yù)期蛇摸。確定一個行為的輸出备图,我們可能需要有多次斷言,這里需要遵循一個原則:**先執(zhí)行的斷言赶袄,不應(yīng)該以后執(zhí)行的斷言成功為前提**揽涮。
     
     ```
         - (void)test_setObject$forKey {
 // arrange
 NSString *key = @"test_key";
 NSString *value = @"test_value";
 NSMutableDictionary *dic = [NSMutableDictionary new];

     // act
   [dic setObject:value forKey:key];

    // assert
   XCTAssertNotNil([dic objectForKey:key]);
   XCTAssertEqual([dic objectForKey:key], value);
     }
     
     ```
     
     可以看到,最后我們是先斷言是否為空弃鸦,再斷言是否相等绞吁,后者是在前者成功的前提下才可能不失敗幢痘。如果顛倒順序唬格,就很難盡早的發(fā)現(xiàn)錯誤原因。
     
## RunTime


Objc是一門動態(tài)語言颜说,所以它總是想辦法把一些決定工作從編譯連接推遲到運行時购岗。也就是說只有編譯器是不夠的,還需要一個運行時系統(tǒng) (runtime system) 來執(zhí)行編譯后的代碼门粪。

- 使用RunTime的場景

 OC程序使用Runtime 系統(tǒng)有三種情景:Objective-C Source Code喊积、NSObject Methods、Runtime Functions玄妈;

 - Objective-C Source Code
 
 - NSObject Methods

 - Runtime Functions

- 消息機制

 上面提到過乾吻,RunTime的實質(zhì)就是消息的發(fā)送。在objc中拟蜻,調(diào)用方法:
 
 ```
 [receiver messge]
 ```
 在編譯器中會轉(zhuǎn)換成消息機制里的消息發(fā)送形式
 
 ```
 objc_msgSend(receiver, selector)
 
 //帶參數(shù)
 objc_msgSend(receiver, selector, arg1, arg2...)
 ```
 
 消息功能為動態(tài)綁定做了很多必要的工作:
 
 1. 通過selector在消息接收者class里選擇方法實現(xiàn)(method implement)
 2. 調(diào)用方法實現(xiàn)绎签,傳遞到接收對象with參數(shù)
 3. 傳遞方法實現(xiàn)返回值
 
 為了讓編譯器編譯時,消息機制與類的結(jié)構(gòu)關(guān)聯(lián)上酝锅,每個類的結(jié)構(gòu)里添加了兩個基本的元素:
 
 1. 指向父類的指針(isa指針)
 2. 類調(diào)度表(A class dispatch table)诡必,通過Selector方法名在dispatch table里面匹配對應(yīng)的方法地址(class-specific address)

 當一個對象被創(chuàng)建并分配內(nèi)存時,它的實例里的變量會初始化搔扁,里面有一個指向它的類的結(jié)構(gòu)體爸舒,isa指針。
 
 消息發(fā)送到一個對象時稿蹲,通過class結(jié)構(gòu)體里的isa指針在dispatch table里尋找相應(yīng)的selector扭勉,如果找不到便進入父類里找,一直找到NSObject苛聘,一旦定位到selector剖效,便調(diào)用該方法嫉入,傳遞相關(guān)數(shù)據(jù)。為了提高效率璧尸,RunTime會緩存調(diào)用過的selector和方法地址咒林,在到dispatch table查找之前,先到cache里查找爷光。


## 如何進行callback方法調(diào)用垫竞,如何支持事件驅(qū)動編程模型


- 非正式協(xié)議

 引用《Cocoa設(shè)計模式》
 
 > 非正式協(xié)議通常定義為NSObject的類別。類別接口中指定的方法可能會或者可能不會被框架類實際地實現(xiàn)蛀序。非正式協(xié)議位于一種設(shè)計灰區(qū)中欢瞪。正式協(xié)議由編譯器檢查并且代表一種關(guān)于對象能力的保證,但是非正式協(xié)議不會做出保證----而只會給出提示徐裸。
 
 引用官方文檔
 
 > An informal protocol is a category on NSObject, which implicitly makes almost all objects adopters of the protocol. (A category is a language feature that enables you to add methods to a class without subclassing it.) Implementation of the methods in an informal protocol is optional. Before invoking a method, the calling object checks to see whether the target object implements it. Until optional protocol methods were introduced in Objective-C 2.0, informal protocols were essential to the way Foundation and AppKit classes implemented delegation.
 
 可以看出遣鼓,非正式協(xié)議就是類別,凡是NSObject或其子類的類別重贺,都是非正式協(xié)議骑祟。
 
 
- 正式協(xié)議

 正式協(xié)議從概念上理解起來就簡單的多了,它指的是一個以@protocol方式命名的方法列表气笙,與非正式協(xié)議相比不同的是次企,它要求顯示的采用協(xié)議。
 
 - 正式協(xié)議的聲明

     1. @required 該類的方式在遵守相應(yīng)協(xié)議的類中是必須被實現(xiàn)的潜圃,不然編譯器會警告(顯然這是在編譯時做的檢查缸棵,而不是在運行時)
     2. @optional 該類的方法在遵守相應(yīng)協(xié)議的類中是否實現(xiàn)是可選的,@optional已取代非正式協(xié)議

 - 正式協(xié)議的繼承性

     正式協(xié)議和類一樣谭期,是可以繼承的堵第,書寫格式同類繼承相似:
     
     ```
     @protocol NewProtocal  <Protocal>  
     @end  
     
     ```
 

- 委托方法
 
 委托概念什么的這里略過。下面主要敘述如何在objc中定義和使用委托隧出。
 
 - 定義委托

     ```
     #import <BlaClass/BlaClass.h>

     @class MyClass;             //定義類踏志,這樣協(xié)議可以看到MyClass
     @protocol MyClassDelegate   //定義委托協(xié)議
     - (void) myClassDelegateMethod: (MyClass *) sender;  //定義在另一個類里實現(xiàn)的委托方法
     @end //結(jié)束協(xié)議

     @interface MyClass : NSObject {
     }
     @property (nonatomic, weak) id <MyClassDelegate> delegate; //定義 MyClassDelegate為委托

     @end
     
     ```
     
 - 定制委托

     ```
     #import "MyClass.h"
@interface MyVC:UIViewController <MyClassDelegate> { //make it a delegate for MyClassDelegate
}
     
     ```
     
     ```
     myClass.delegate = self;          //設(shè)置委托至自身的某個地方
     
     ```
     
 - 使用委托, 如下形式

     ```
     if([[self delegate] respondsToSelector:@selector(windowDidMove:)]) {
   [[self delegate] windowDidMove:notification];
}
     
     ```

- 響應(yīng)選擇器selector

 selector,實際上是函數(shù)指針的一種實現(xiàn)形式鸳劳,我們用一個 C string 來表示對象中的某個函數(shù)狰贯,所以就可以把這個函數(shù)作為參數(shù),傳到其他的方法中去進行調(diào)用赏廓。
 
 Objective-C 的 Class 在編譯時會變成 C struct涵紊,Class 中包含的方法也會轉(zhuǎn)換成 C function。之后在運行的時候幔摸,runtime 會建立起從 Objective-C Method 到 C function 的映射(可以認為是一個 virtual table)摸柄。
 
 Runtime 會為每個類準備一個 virtual table,里面是一個個鍵值對既忆,key 稱為 selector驱负,類型是 SEL嗦玖,value 實際上是 C function 的函數(shù)指針,類型是 IMP跃脊。而這里的 SEL 類型實際上就是 C string宇挫。
 
 因此 selector 可以看做是函數(shù)的另一個名字,所以很多需要調(diào)用函數(shù)或者建立連接的地方酪术,都可以用到器瘪。

- objc的回調(diào)實現(xiàn)

 - Run loop

     objc提供的NSRunLoop實例會持續(xù)等待著,當特定事件發(fā)生時绘雁,觸發(fā)回調(diào)(callback)橡疼。

     調(diào)用以下方法,即可得到一個run loop庐舟。
     
     ```
     [[NSRunLoop currentRunLoop] run];
     ```

 - target-action/目標-動作對

     實例:
     
     ```
     // 為按鈕添加回調(diào)——Target-action/目標-動作對
// 第一個參數(shù):發(fā)送消息給誰
// 第二個參數(shù):事件發(fā)生后,執(zhí)行什么代碼(回調(diào))
// 第三個參數(shù):發(fā)生哪類型的點擊事件會觸發(fā)回調(diào)
[button addTarget:self
        action:@selector(click:)
forControlEvents:UIControlEventTouchUpInside];

     ```
     
     目標-動作對欣除,就是當事件發(fā)生時,像指定的對象發(fā)送指定的消息挪略。對target历帚,action的對應(yīng)理解,可以這樣認為瘟檩,執(zhí)行某個類(target)的某個方法抹缕。
     
 - Helper object/委托

     委托使用如上文所示澈蟆。
 
 - Notification/通告
     
     objc提供了一個叫做「通告中心」的對象墨辛,可以通過[NSNotificationCenter defaultCenter]獲得,利用這個通告中心趴俘,我們可以「發(fā)通告」睹簇、「監(jiān)測(接收)通告」,利用這個機制寥闪,實現(xiàn)回調(diào)太惠。
 
 - Block

     在objc中使用block實現(xiàn)回調(diào),除了基本聲明語法疲憋,其他與大多oo語言相同凿渊,不再敘述。
     
     ```
     #import <Foundation/Foundation.h>
     @import CoreBluetooth;

     // 步驟1:
     // 將Block重新定義為一種新的數(shù)據(jù)類型
     // 這個Block無返回值;有一個參數(shù)(類型為NSUInteger)
     typedef void(^AllDevicesDidConnectedBlock)(NSUInteger divicesCount);

     @interface MyCnetralManager : NSObject

     // 步驟2:
     // 聲明一個(Block)變量
     @property (nonatomic, strong) AllDevicesDidConnectedBlock callbackForAllDevicesDidConnected;

     @end
     ```
     
     然后缚柳,就可以使用了埃脏。
     
 - 總結(jié)

     1. 當只發(fā)生單個事件(event),只需要完成一件事情進行響應(yīng)秋忙,建議用「Target-action/目標-動作對」彩掐。比如NSTimer、UIButton等灰追。
     2. 當會發(fā)生若干事件(event)堵幽,要完成多件事情進行響應(yīng)狗超,建議使用「Helper objects/輔助對象」,當然了朴下,最常見的是「delegate/委托」(另外還有「data sources/數(shù)據(jù)源」)努咐。
     3. 當發(fā)生單個事件(event),多個對象要進行響應(yīng)殴胧,建議使用「Notifications/通告」
     4. 使用Block麦撵,可以寫出更簡潔的代碼、更好的代碼結(jié)構(gòu)溃肪。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末免胃,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子惫撰,更是在濱河造成了極大的恐慌羔沙,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件厨钻,死亡現(xiàn)場離奇詭異扼雏,居然都是意外死亡,警方通過查閱死者的電腦和手機夯膀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門诗充,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人诱建,你說我怎么就攤上這事蝴蜓。” “怎么了俺猿?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵茎匠,是天一觀的道長。 經(jīng)常有香客問我押袍,道長诵冒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任谊惭,我火速辦了婚禮汽馋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘圈盔。我一直安慰自己豹芯,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布药磺。 她就那樣靜靜地躺著告组,像睡著了一般。 火紅的嫁衣襯著肌膚如雪癌佩。 梳的紋絲不亂的頭發(fā)上木缝,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天便锨,我揣著相機與錄音,去河邊找鬼我碟。 笑死放案,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的矫俺。 我是一名探鬼主播吱殉,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼厘托!你這毒婦竟也來了友雳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤铅匹,失蹤者是張志新(化名)和其女友劉穎押赊,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體包斑,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡流礁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了罗丰。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片神帅。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖萌抵,靈堂內(nèi)的尸體忽然破棺而出找御,到底是詐尸還是另有隱情,我是刑警寧澤谜嫉,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布萎坷,位于F島的核電站凹联,受9級特大地震影響沐兰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蔽挠,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一住闯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧澳淑,春花似錦比原、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至氢拥,卻和暖如春蚌铜,著一層夾襖步出監(jiān)牢的瞬間锨侯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工冬殃, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留囚痴,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓审葬,卻偏偏與公主長得像深滚,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子涣觉,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉痴荐,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,679評論 0 9
  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,124評論 29 470
  • 她說要給我一個東西,還說這東西代表我們的關(guān)系官册。然后在餐廳考試可我和李豪挨著蹬昌,做完題后,我就讓他幫忙猜一下攀隔,然后我倆...
    小狐的冰山閱讀 151評論 0 0
  • 再一次聽到老牛說話實在yy里皂贩,
    飛云閱讀 282評論 0 15
  • 說起林心如明刷,一直以來給人的印象就是“古裝女神”、“清純玉女”满粗。她在古裝劇《還珠格格》中塑造的紫薇辈末,由于太過經(jīng)典,至...
    進擊的小獅妹閱讀 847評論 0 0