天天看點

iOS:Objective-C 關鍵字1

-- self、super、instancetype、id、nsnil、nsnull、static;

========================self、super========================

--參考:http://www.cocoachina.com/macdev/objc/2011/0124/2602.html

@interface Person:NSObject {  
}
- (void) setName:(NSString*) yourName;
@end
@interface PersonMe:Person {  
}
- (void) setAge:(NSUInteger) age;
- (void) setName:(NSString*) yourName andAge:(NSUInteger) age;
@end
@implementation PersonMe
- (void) setName:(NSString*) yourName andAge:(NSUInteger) age {
    [self setAge:age];
    [super setName:yourName];
   NSLog(@"self ' class is %@", [self class]);
   NSLog(@"super' class is %@", [super class]);
}
@end
// 調用
 PersonMe* me = [[PersonMe alloc] init];
 [me setName:@"asdf" andAge:18];
//輸出  self 's class is PersonMe  和  super ' s class is PersonMe 
           
例2:self不一定就是通常看到的viewcontroller對象,如果該方法在某個UIButton子類中,它可能表示某個button對象。self表示目前方法調用者
[button addTarget:self action:@selector(clicked/clicked:/clicked:event:) forControlEvents:UIControlEventTouchUpInside];
           
// 例3 :self表示目前方法的調用者
+ (void)initialize {
  if (self == [ClassName self]) {
    // ... do the initialization ...
  }
}
           

        說明:self 是類的 隐藏的參數,指向 目前目前調用方法的類,另一個隐藏參數是 _cmd,代表 目前類方法的 selector。這裡隻關注這個 self。super 是個啥?super 并不是隐藏的參數,它隻是一個 “編譯器訓示符”,它和 self 指向的是相同的消息接收者,拿上面的代碼為例,不論是用 [self setName] 還是 [super setName],接收“setName”這個消息的 接收者都是 PersonMe* me 這個對象。不同的是, 當使用 self 調用方法時,會從目前類的方法清單中開始找,如果沒有,就從父類中再找;而當使用 super 時,super 告訴編譯器,從父類的方法清單中開始找。然後調用父類的這個方法。

       深入分析:

        這種機制到底底層是如何實作的?其實當調用類方法的時候,編譯器會将方法調用轉成一個 C 函數方法調用,Apple 的 objcRuntimeRef 上說:

       Sending Messages

       When it encounters a method invocation, the compiler might generate a call to any of several functions to perform the actual message dispatch, depending on the receiver, the return value, and the arguments. You can use these functions to dynamically invoke methods from your own plain C code, or to use argument forms not permitted by NSObject’s perform… methods. These functions are declared in /usr/include/objc/objc-runtime.h.

■ objc_msgSend sends a message with a simple return value to an instance of a class.

■ objc_msgSend_stret sends a message with a data-structure return value to an instance of  a class.

■ objc_msgSendSuper sends a message with a simple return value to the superclass of an instance of a class.

■ objc_msgSendSuper_stret sends a message with a data-structure return value to the superclass of an instance of a class.

       可以看到會轉成調用上面 4 個方法中的一個,由于 _stret 系列的和沒有 _stret 的那兩個類似,先隻關注 objc_msgSend 和 objc_msgSendSuper 兩個方法。當使用 [self setName] 調用時,會使用 objc_msgSend 的函數,先看下 objc_msgSend 的函數定義:

       id objc_msgSend(id theReceiver, SEL theSelector, ...)

       第一個參數是消息接收者,第二個參數是調用的具體類方法的 selector,後面是 selector 方法的可變參數。我們先不管這個可變參數,以 [self setName:] 為例,編譯器會替換成調用 objc_msgSend 的函數調用,其中 theReceiver 是 self,theSelector 是 @selector(setName:),這個 selector 是從目前 self 的 class 的方法清單開始找的 setName,當找到後把對應的 selector 傳遞過去。

       而當使用 [super setName] 調用時,會使用 objc_msgSendSuper 函數,看下 objc_msgSendSuper 的函數定義:

       id objc_msgSendSuper(struct objc_super *super, SEL op, ...)

      第一個參數是個objc_super的結構體,第二個參數還是類似上面的類方法的selector,先看下objc_super這個結構體是什麼東西:

struct objc_super {

id receiver;

Class superClass;

};

       可以看到這個結構體包含了兩個成員,一個是 receiver,這個類似上面 objc_msgSend 的第一個參數 receiver,第二個成員是記錄寫 super 這個類的父類是什麼,拿上面的代碼為例,當編譯器遇到 PersonMe 裡 setName:andAge 方法裡的 [super setName:] 時,開始做這幾個事:

建構 objc_super 的結構體,此時這個結構體的第一個成員變量 receiver 就是 PersonMe* me,和 self 相同。而第二個成員變量 superClass 就是指類 Person,因為 PersonMe 的超類就是這個 Person。

        調用 objc_msgSendSuper 的方法,将這個結構體和 setName 的 sel 傳遞過去。函數裡面在做的事情類似這樣:從 objc_super 結構體指向的 superClass 的方法清單開始找 setName 的 selector,找到後再以 objc_super->receiver 去調用這個 selector,可能也會使用 objc_msgSend 這個函數,不過此時的第一個參數 theReceiver 就是 objc_super->receiver,第二個參數是從 objc_super->superClass 中找到的 selector。裡面的調用機制大體就是這樣了。

========================instancetype、id========================

--參考:http://blog.csdn.net/wzzvictory/article/details/16994913

概念:instancetype是clang 3.5開始,clang提供的一個關鍵字,表示某個方法傳回的未知類型的Objective-C對象。我們都知道未知類型的的對象可以用id關鍵字表示,那為什麼還會再有一個instancetype?

----關聯傳回類型(related result types):

根據Cocoa的命名規則,滿足下述規則的方法:

1、類方法中,以alloc或new開頭

2、執行個體方法中,以autorelease,init,retain或self開頭

會傳回一個方法所在類類型的對象,這些方法就被稱為是關聯傳回類型的方法。換句話說,這些方法的傳回結果以方法所在的類為類型,請看下面的例子:

@interface NSObject  
+ (id)alloc;  
- (id)init;  
@end    
@interface NSArray : NSObject  
@end  
           
NSArray *array = [[NSArray alloc] init];  
           

     按照Cocoa的命名規則,語句[NSArray alloc] 的類型就是NSArray*因為alloc的傳回類型屬于關聯傳回類型。同樣,[[NSArray alloc]init] 的傳回結果也是NSArray*

----instancetype作用

@interface NSArray  
+ (id)constructAnArray;  
@end 
           

        當我們使用如下方式初始化NSArray時: [NSArray constructAnArray]; 根據Cocoa的方法命名規範,得到的傳回類型就和 方法聲明的傳回類型一樣,是id .

@interface NSArray  
+ (instancetype)constructAnArray;  
@end
//實際例子
+ (instancetype)newRegionWithName:(NSString *)regionName {
	APLRegion *newRegion = [[self alloc] init];
	newRegion.name = regionName;
	newRegion.mutableTimeZoneWrappers = [[NSMutableArray alloc] init];
	regions[regionName] = newRegion;
	return newRegion;
}
           

       上文中實際例子self 代表着方法調用者,表示APLRegion類,跟一般情況下表示對象不同。

       當使用相同方式初始化NSArray時:[NSArray constructAnArray];到的傳回類型和方法所在類的類型相同,是NSArray* .

       總結一下,instancetype的作用,就是使那些非關聯傳回類型的方法傳回所在類的類型!

       好處:能夠确定對象的類型,能夠幫助編譯器檢測代碼書寫問題。

比較:instancetype和id的異同:

--相同點:都可以作為方法的傳回類型;

--不同點:1.instancetype可以傳回和方法所在類相同類型的對象,id隻能傳回未知類型的對象;2.instancetype隻能作為傳回值,不能像id那樣作為參數,比如下面的寫法:

- (void)setValue:(id)value     // instancetype 不能作為參數
{ ... }  
           

========================nil, Nil, NULL,NSNull========================

參考:http://blog.csdn.net/yhawaii/article/details/7442529

--nil: A null pointer to an Objective-C object. ( #define nil ((id)0) )

--Nil: A null pointer to an Objective-C class.

--NULL: A null pointer to anything else, is for C-style memory pointers. ( #define NULL ((void *)0) )

NSNull: A class defines a singleton object used to represent null values in collection objects (which don't allow nil values). [NSNull null]: The singleton instance of NSNull.

         Technically they're all the same,but in practice they give someone reading your code some hints about what's going on; just like naming classes with a capital letter and instances with lowercase is recommended, but not required。

======================== static || 全局 ========================

ps: 成員變量:在h檔案中@interface  @end中聲明或者在m檔案中@implementation上用@interface  @end添加的;

        全局:在@implementation外定義的變量(在@implementation中定義也是可以但是一般不這麼幹)。

static int hu=3;//全局變量
NSString*str1= @"str";//全局變量
@implementation ViewController
@end 
           

       全局變量分為兩種:

  1) static 修飾的 也稱私有全局變量,隻在該源檔案中可用static隻在聲明的類中可見。在Objective-C 的文法中聲明後的static靜态變量在其他類中是不能通過類名直接通路的,它的作用域隻能是在聲明的這個.m檔案中 。不過可以調用這個類的方法間接的修改這個靜态變量的值。

        2)沒有static 修飾的 ,在任何源檔案中都可以用,注意:定義沒有static的全局變量,命名一定要很獨特,才能區分與其他源檔案中的全局變量,如果出現跟其他源檔案同名的全局變量,則會報錯誤

undefined symbols for architecture x86_64:
"xx" ,referenced from:
      _main in main.0
ld:symbol(s) not found for architecture
clang:error:linker command failed with exit code 1
           

         在其他檔案如果要用該全局變量,在前面加extern NSString*str1;即可使用了 。

備注:

       objective c中靜态變量使用static關鍵字進行定義。 在c中的static作用是“靜态”的意思,作用局部變量改變其生存周期,作用全局變量改變其作用域(生存周期已經夠長了)。和c++不同,objective c不支援類靜态成員變量(也就是不支援class variables);但objective c支援全局變量,用法和c/c++中相同。是以,在OC中作用,對于static簡單資料類型,完全适用c的那些規則;對于static類變量,參照c規則,作用一樣,局部的改變生存周期,全局的改變作用域(類似類靜态成員)。

         (個人猜想)為什麼OC中不支援類靜态成員變量?因為完全可以通過 static + 類全局變量來實作(而且也有友善的類方法(+)來操作它),對于實作單例或者其他功能完全沒有影響,這樣也實作了和c的統一。