標籤:des style color io 使用 ar strong for 檔案
點文法:
點文法的本質還是方法調用 當使用點文法時,編譯器會自動延伸成相應的方法 Person *p = [Person new];
p.age=10; //編譯器編譯的時候會自動將這一行代碼轉換成 [p setAge:10];
int a=p.age; //編譯器編譯的時候會自動將這一行代碼轉換成 int a = [p age]; 使用點文法的時候要注意不要形成死迴圈: - (void)setAge:(int)age
{
//下面的代碼將引發死迴圈,相當於[self setAge:age]
self.age = age;
}
- (int)age
{
//下面的代碼將引發死迴圈,相當於[self age]
return self.age; }
成員變數的4種範圍:
@private:只能在當前類的對象方法中直接存取(@implementation中不寫預設是@private)
@protected:可以再當前類及其子類的對象方法中直接存取(@interface中不寫預設是@protected)
@public:任何地方都可以訪問對象的成員變數@package:只要處在同一個架構中,就能直接存取對象的成員變數,介於@private和@public之間
什麼都不寫的時候預設的存取層級是protected 成員變數也可以寫在實現部分,也就是.m檔案中,
存取層級預設是@private 在 .m檔案中聲明的成員變數不能和 .h檔案中聲明的成員變數重名(也就是@implementation和@interface檔案)
OC中只能單繼承 父類\超類 superclass 子類 subclass\subclasses
@property和@synthesize 這兩個關鍵字是編譯器特性
@property:可以自動產生某個成員變數的set和get方法聲明 成員變數: int _age; int _height; NSString *_name; get和set方法的聲明:
@property int age;就相當於下面的兩句聲明代碼 - (void)setAge:(int)age; - (int)age;
@property NSString *name; 當多個成員變數的類型一致的時候,可以使用
@property 類型 成員1,成員2,成員3; get和set方法的實現:
@synthesize age=_age; //相當於下面兩個方法(@synthesize自動產生age的set和get方法,並且訪問_age這個成員變數)
如果是@synthesize age;//這樣的寫法的話預設訪問的就不是成員變數_age了,而是成員變數age
- (void)setAge:(int)age { _age=age; } - (int)age { return _age; } 不寫成員變數,但是寫@property的話,那麼在@implementation中使用@synthesize的話,訪問成員變數,如果不存在的話,就會自動產生@private的成員變數,資料類型根據@property中的資料類型。(這個變數是產生在@implementation中的)
最簡單的寫法:
直接寫一個@property int age;
這句話將會完成的任務:
1、產生age的get和set方法聲明
2、產生一個int類型的成員變數 int _age;(因為產生的變數是@private,所以如果子類需要訪問的話,還是需要我們自己寫成員變數,這樣就不會產生私人的成員變數了)
3、產生age的get和set方法的實現
@synthesize的細節 @synthesize age=_age 1、setter和getter實現中會訪問成員變數_age; 2、如果成員變數不存在,就會自動產生一個@private的成員變數_age @synthesize age 1、setter和getter實現中會訪問age變數 2、如果成員變數age不存在,就會自動產生一個@private的成員變數age 手動實現 1、若手動實現setter方法,編譯器就只會自動產生getter方法 2、若手動實現getter方法,編譯器就只會自動產生setter方法 3、若手動同時實現了setter和getter方法,編譯器就不會自動產生不存在的成員變數
id id是萬能指標,能指向\操作任何OC對象
id內部包含*,所以聲明變數的時候不需要帶 * NSString * (字串)也是OC對象,所以id也可以指向字串
也就是說 id 相當於 NSObject * id類型的定義: typedef struct objc object{ Class isa; } *id; 局限性: 調用一個不存在的方法的時候,編譯器會馬上報錯
構造方法:對象方法 構造方法:用來初始化對象的方法,是個對象方法,以 -號開頭 重寫構造方法的目的:為了讓對象建立出來,成員變數就會有一些固定的值 Person *p = [Person new];
new方法完整的建立一個對象(分別調用兩個方法完成下面的工作):
1、分配儲存空間(+alloc)
2、初始化(-init) new方法內部進行的工作: //調用+alloc方法分配儲存空間 Person *p1 = [Person alloc]; //調用-init方法進行初始化 Person *p2 = [p1 init]; init方法就是構造方法: 等價於:Person *p = [[Person alloc] init]; 對象初始化之後,對象的成員變數預設為0 當要求每個Person對象建立出來的時候,他的成員變數_age都是10
構造方法的注意點:
1、先調用父類的構造方法 ([super init])
2、在進行子類內部成員變數的初始化
//重寫-init方法
- (id)init
{
//1、一定要調用super的init方法:初始化父類中聲明的一些成員變數和其他屬性
//2、如果對象初始化成功,才有必要進行接下來的初始化
if (
self = [super init]
)
{
//初始化成功
_age = 10;
}
//3、返回一個已經初始化完畢的對象
return self;
}
自訂建構函式: 上面的重寫init方法,只能在內部給成員變數賦初始值,聲明的對象都一樣 自訂建構函式分兩步: 第一步:聲明 第二步:實現
自訂構造方法的規範:
1、一定是對象方法,一定以 - 開頭
2、傳回值一般是id類型
3、方法名一般以initWith開頭 聲明: - (id)initWithName:(NSString *)name; 實現: - (id)initWithName:(NSString *)name { if(self = [super init]) { _name=name; } return self; } 當子類中的初始化方法需要使用父類的成員變數的時候,原則是:子類中的成員變數在子類中初始化,父類的成員變數在父類中初始化。(父類中提供初始化方法,然後子類將值傳遞過來) 這樣做的好處就是:當父類中的成員變數發生變化的時候,子類不需要更改不會報錯。直接改父類就行了
Category:分類
分類:可以給某一個類擴充一些方法(不修改原來類的代碼)-》相當於C#中的partial
//聲明
@interface 類名 (分類名稱)
@end
//實現
@implementaion 類名 (分類名稱)
@end
使用注意:
1、分類只能增加方法,不能增加成員變數
2、分類方法實現中可以訪問原來類中聲明的成員變數
3、如果分類中的方法與原來類中的方法重名,那麼優先調用的是分類中的方法。(這樣就會覆蓋原來類中的方法,原來類中的方法將不能使用)
4、方法調用的優先順序:分類(最後參與編譯的分類優先)-->原來類-->父類
characterAtIndex:<#(NSUInteger)#>:字串的這個方法是擷取指定位置上的字元(位置序號從0開始)
類的本質: 1、類也是個對象。簡稱
“類對象” ->其實類也是一個對象,是一個Class類型的對象。
Class關鍵字內部包含 * ,使用的時候後面不需要加 * 利用Person類對象,建立Person類型的對象
//擷取記憶體中的類對象(擷取到的類對象可以調用類方法)
Person *p = [[Person alloc] init];
Class c = [p class];
或者:Class c = [Person class]; 類的載入過程:
+ (void)load:方法,在類被載入的時候調用
程式啟動的時候會載入所有的類和分類,並調用所有類和分類的+load方法
先載入父類,再載入子類,也就是先調用父類的+load,再調用子類的+load
先載入原始類,再載入分類
不管程式運行過程中有沒有用到這個類,都會調用+load載入
+initialize
在第一次使用某個類時(比如建立對象等),就會調用一次+initialize方法
一個類只會調用一次+initialize方法,先調用父類的,在調用子類的
當程式啟動時,就會附加元件目中所有的類和分類,而且載入後會調用每個類和分類的+load方法,先調用原始類的load方法,後調用分類的load方法
當第一次使用某個類時,就會調用當前類的+initialize方法
先載入父類,再載入子類(先調用父類的+load方法,在調用子類的+load方法)
先初始化父類,再初始化子類(先調用父類的+initialize方法,再調用子類的+initialize方法)
description方法:
-description(決定執行個體對象的輸出結果)
Person *p = [[Person alloc] init]; //預設情況下,利用NSLog和%@輸出對象時,結果是: <類名:記憶體位址> NSLog(@"%@",p);
列印OC對象使用的預留位置是%@ 列印OC對象的話(除了NSString *),顯示的是類名加上類在記憶體中的地址 執行NSLog(@"%@",p);這句代碼的時候: 1、首先會調用對象p的-description方法 2、拿到-description方法的傳回值(NSString *)顯示到螢幕上 3、-description方法預設返回的是“類名:記憶體位址” 當想輸出對象的內容的時候,可以在類的實現中重寫-description方法: - (NSString *)description
{ return [NSString stringWithFormat:@"age=%d,name=%@",_age,_name];}
不要在description中嘗試輸出self:NSLog(@"%@",self),這樣會引發死迴圈
+description(決定類對象的輸出結果) Class c = [Person class]; //會調用類的+description方法 //拿到+description方法的傳回值(NSString *)顯示到螢幕上(預設返回的是類名) NSLog(@"%@",c);
NSLog():
列印指標中儲存的對象的記憶體位址:
NSLog(@"%p",p);
列印指標變數自己的記憶體位址:
NSLog(@"%p", &p);
NSLog()輸出C語言字串的時候,不能有中文 NSLog(@"%s",_func_);//輸出當前方法名稱 NSLog(@"%d",_LINE_);//輸出當前行號 NSLog(@"%s",_FILE_);//輸出當前源檔案的完整路徑 NSLog(@"%s",__PRETTY_FUNCTION__ );//返回當前方法或函數的完整的函數名(包括傳回值和參數)
SEL類型 是一種類型 一個類中都有SEL類型資料,一個SEL資料對應一個方法的地址 Person *p = [[Person alloc] init]; [p test2]; 1、把test2封裝成SEL類型的資料 2、根據SEL資料找到對應的方法地址 3、根據方法地址調用對應的方法(這裡使用的緩衝技術,第一次會尋找,以後就會使用第一次尋找的結果)
間接調用test2方法
[p performSelector:@selector(test2)]; 方法的儲存位置: 每個類的方法列表都儲存在類對象中 每個方法都有一個與之對應的SEL類型的資料 根據一個SEL對象就可以找到方法的地址,進而調用 SEL類型的定義 typedef struct objc_selector *SEL; SEL對象的建立 SEL s=
@selector(test); SEL s2=
NSSelectorFormString(@"test");
NSSelectorFormString(@"test");//將一個字串類型的資料傳進去轉換成SEL資料
每個方法內部都有一個SEL類型的資料_cmd,指向當前方法 在方法中不能使用[self performSelector:_cmd]; SEL其實是對方法的一種封裝,將方法封裝成一個SEL類型的資料,去找對應的方法地址,找到方法地址就可以調用方法了。 其實訊息就是SEL
Objective-C:06_物件導向-核心文法