OC基礎--構造方法 id類型

new方法實現原理:函數

  new作了三件事情spa

       1.開闢存儲空間  + alloc 方法指針

       2.初始化全部的屬性(成員變量) - init 方法code

       3.返回對象的地址 對象

  [Person new]; == [[Person alloc] init];blog

    alloc: 1.開闢存儲空間 2.將全部的屬性設置爲0 3.返回當前實例對象的地址繼承

    init:  1.初始化成員變量, 可是默認狀況下init的實現是什麼都沒有作 2.返回初始化後的實例對象地址編譯器

      注意: alloc返回的地址, 和init返回的地址是同一個地址string

構造方法的概念及用途:it

  在OC中init開頭的方法, 咱們稱之爲構造方法

  用於初始化一個對象, 讓某個對象一建立出來就擁有某些屬性和值

重寫init方法, 在init方法中初始化成員變量:

  重寫init方法必須按照蘋果規定的格式重寫, 若是不按照規定會引起一些未知的錯誤

    1.必須先初始化父類, 再初始化子類 

      子類繼承自父類  那麼子類擁有父類全部成員  子類必須調用父類的構造方法對這些成員進行初始化

    2.必須判斷父類是否初始化成功, 只有父類初始化成功才能繼續初始化子類

      爲了防止父類的初始化方法release掉了self指向的空間並從新alloc了一塊空間。還有[super init]可能alloc失敗,這時就再也不執行if中的語句

    3.返回當前對象的地址

      super 和 self 指向的是相同的消息接收者 即誰調用就表明誰  

      - (instancetype)init
      {
          // 1.初始化父類 只要父類初始化成功 , 就會返回對應的地址, 若是初始化失敗, 就會返回nil  nil == 0 == 假 == 沒有初始化成功
          self = [super init];
          // 2.判斷父類是否初始化成功
          if (self != nil) {
                  // 3.初始化子類 設置屬性的值
                  _age = 6;
          }
          // 4.返回地址
          return self;
      }

    簡版:

      - (instancetype)init
      {
          // 注意: 不要把 = 號寫爲 ==     必定要將[super init]的返回值賦值給self
          if (self = [super init]) {
              // 初始化子類
              _age = 6;
          }
          return self;
      }

自定義構造方法:

   自定義構造方法 其實就是自定義一個init方法:

     1.必定是對象方法

     2.必定返回id/instancetype

     3.方法名稱必定以init開頭

#import "Person.h"

@implementation Person
// 重寫init方法
- (instancetype)init{
    if (self = [super init]) {
        self.name = @"王二小";
        self.age = 12;
    }
    return self;
}
// 自定義構造方法  一個類能夠有0個或者多個自定義構造方法
- (instancetype)initWithName:(NSString *)name{
    if (self = [super init]) {
        _name = name;
    }
    return self;
}
// 自定義構造方法能夠有1個或多個參數
- (instancetype)initWithName:(NSString *) name andAge:(int) age{
    if (self = [super init]) {
        _name = name;
        _age = age;
    }
    return self;
}
@end

自定義構造方法在繼承中得表現:

  誰聲明的成員就由誰去初始化(父類的屬性交給父類去處理  子類方法只處理本身獨有的屬性)

#import "Student.h"
 
@implementation Student
/*
- (instancetype)initWithStudentNO:(NSString *)studentNO andName:(NSString *)name andAge:(int)age{
    if (self = [super init]) {
        self.name = name;
        self.age = age;
        self.studentNO = studentNO;
    }
    return self;
}
 */
- (instancetype)initWithStudentNO:(NSString *)studentNO andName:(NSString *)name andAge:(int)age{
    // 由父類的構造方法去初始化 name 和 age 屬性
    if (self = [super initWithName:name andAge:age]) {
        _studentNO = studentNO;
    }
    return self;
}
@end

調用圖解:

   

自定義類工廠方法:

   什麼是類工廠方法:

     用於快速建立對象的類方法, 咱們稱之爲類工廠方法

     類工廠方法中主要用於 給對象分配存儲空間和初始化這塊存儲空間 

     自定義類工廠方法是蘋果的一個規範, 通常狀況下, 咱們會給一個類提供自定義構造方法和自定義類工廠方法用於建立一個對象

   規範:

     1.必定是類方法 +

     2.方法名稱以類的名稱開頭, 首字母小寫

     3.必定有返回值, 返回值是id/instancetype

    舉例: 

    [[NSString alloc] init];
    [NSString string];
   
    [[NSString alloc] initWithString:(NSString *)];
    [NSString stringWithString:(NSString *)];
 
    [[NSArray alloc] init];
    [NSArray array];

    [NSArray alloc] initWithObjects:(id), ..., nil];
    [NSArray arrayWithObjects:(id), ..., nil];

// 自定義類工廠方法
+ (instancetype)personWithName:(NSString *)name andAge:(int)age{
    Person * person = [[Person alloc] init];
    person.name = name;
    person.age = age;
    return person;
}

自定義類工廠方法在繼承中注意點:

  因爲子類默認會繼承父類全部的方法和屬性, 因此類工廠方法也會被繼承

  在類工廠方法中建立對象必定不要使用類名來建立   必定要使用self來建立

    self在類方法中就表明類對象 (誰調用當前方法, self就表明誰)

    父類的類工廠方法建立實例對象時是使用父類的類名建立的, 若是子類調用父類的類工廠方法建立實例對象,建立出來的仍是父類的實例對象

@interface Person : NSObject
+ (id)person;
@end
 
@implementation Person
+ (id)person
{
    return  [[Person alloc] init];
}
@end
 
@interface Student : Person
@property NSString *name;
@end
 
@implementation Student 
@end
 
int main(int argc, const char * argv[])
{
    Student *stu = [Student person];// 等效於 [[Person alloc] init]  須要該爲 [[self alloc] init]
    [stu setName:@"lnj"]; // 報錯, 由於Person中沒有setName
}
 
// 自定義類工廠方法  使用self 而不是 類名
+ (instancetype)personWithName:(NSString *)name andAge:(int)age{
    // Person * person = [[Person alloc] init];
    Person * person = [[self alloc] init];
    person.name = name;
    person.age = age;
    return person;
}

id類型:

  id是一個數據類型, 而且是一個動態數據類型

       既然是數據類型, 因此就能夠用來

         1.定義變量

         2.做爲函數的參數

         3.做爲函數的返回值

  id == NSObject *   萬能指針

       id和NSObject *的區別: 

         NSObject *是一個靜態數據類型

         id  是一個動態數據類型

  默認狀況下全部的數據類型都是靜態數據類型

     靜態數據類型的特色: 

       在編譯時就知道變量的類型, 

       知道變量中有哪些屬性和方法

       在編譯的時候就能夠訪問這些屬性和方法, 

       若是是經過靜態數據類型定義變量, 若是訪問不了屬於靜態數據類型的屬性和方法, 那麼編譯器就會報錯    

     動態數據類型的特色:

       在編譯的時候編譯器並不知道變量的真實類型, 只有在運行的時候才知道它的真實類型

       若是經過動態數據類型定義變量, 若是訪問了不屬於動態數據類型的屬性和方法, 編譯器不會報錯

  • 經過靜態數據類型定義變量, 不能調用子類特有的方法
  • 經過動態數據類型定義變量, 能夠調用子類特有的方法
  • 經過動態數據類型定義的變量, 能夠調用私有方法
    • 弊端: 因爲動態數據類型能夠調用任意方法, 因此有可能調用到不屬於本身的方法, 而編譯時又不會報錯, 因此可能致使運行時的錯誤
    • 應用場景: 多態, 能夠減小代碼量, 避免調用子類特有的方法須要強制類型轉換
  • 爲了不動態數據類型引起的運行時的錯誤, 通常狀況下若是使用動態數據類型定義一個變量, 在調用這個對象的方法以前會進行一次判斷, 判斷當前對象是否可以調用這個方法
    • isKindOfClass     
        id obj = [Student new];    
        // isKindOfClass , 判斷指定的對象是不是某一個類, 或者是某一個類的子類
        if ([obj isKindOfClass:[Student class]]) {
            [obj eat];
        }     
    • isMemberOfClass
        id obj = [Student new];
        if ([obj isMemberOfClass:[Student class]]) {
            // isMemberOfClass : 判斷指定的對象是不是當前指定的類的實例
            [obj eat];
        }   

instancetype和id的區別:

  instancetype == id == 萬能指針 == 指向一個對象

  id在編譯的時候不能判斷對象的真實類型

  instancetype在編譯的時候能夠判斷對象的真實類型

    (一個在編譯時不知道真實類型, 一個在編譯時知道真實類型)

  id能夠用來定義變量, 能夠做爲返回值, 能夠做爲形參

  instancetype只能用於做爲返回值

    它會進行類型檢查,若是建立出來的對象,賦值了不相干的對象就會有一個警告信息,防止出錯

  注意: 之後但凡自定義構造方法, 返回值儘可能使用instancetype, 不要使用id

相關文章
相關標籤/搜索