一奋献、
創(chuàng)建一個(gè)對(duì)象,內(nèi)存是如何分配
1). 子類對(duì)象中有自己的屬性和所有父類的屬性
2). 代碼段中每一個(gè)類都有一個(gè)isa指針灾搏,這個(gè)指針指向它的父類.-
結(jié)構(gòu)體與類
- 相同點(diǎn): 都可以將多個(gè)數(shù)據(jù)封裝為1個(gè)整體
struct Data{ int year; int month; int day; } @interface Data : NSObject{ int year; int month; int day; }
2). 不同點(diǎn):
a. 結(jié)構(gòu)體只能封裝數(shù)據(jù)漂羊,而類不僅可以封裝數(shù)據(jù)還可以封裝行為
b. 結(jié)構(gòu)體變量分配在椈空間(前提是局部變量),而對(duì)象分配在堆空間.
椄刻空間相對(duì)較小,但是訪問效率高;
堆空間較大, 訪問效率低.3). 應(yīng)用場(chǎng)景:
a. 如果表示的實(shí)體不僅有數(shù)據(jù)還有行為,只能使用類
b. 如果表示的實(shí)體只有數(shù)據(jù), 沒有行為磁椒,視屬性多少而定.如果屬性只有幾個(gè),就定義為結(jié)構(gòu)體; 如果屬性較多玫芦,就定義為類. -
類的本質(zhì)
棧
堆
BSS段
數(shù)據(jù)段
代碼段
代碼段是用來存儲(chǔ)代碼的.
類加載浆熔,當(dāng)類第一次被訪問的時(shí)候,這個(gè)類就會(huì)被加載到代碼段存儲(chǔ)起來.
類一旦被加載桥帆,是不會(huì)被回收的医增,除非程序結(jié)束.1). 任何存儲(chǔ)在內(nèi)存中的數(shù)據(jù)都有1個(gè)數(shù)據(jù)類型.
2). 在代碼段存儲(chǔ)類的步驟
a. 先在代碼段中創(chuàng)建Class對(duì)象, Class是Foundation框架中的一個(gè)類
b. 將類的信息存儲(chǔ)在這個(gè)Class對(duì)象之中
Class對(duì)象至少有三個(gè)屬性:
類名/屬性s/方法s
存儲(chǔ)類的這個(gè)Class對(duì)象,叫做類對(duì)象
所以存儲(chǔ)類的類對(duì)象也有一個(gè)isa指針 -
類對(duì)象使用
1). 調(diào)用類的類方法class,就可以了得到存儲(chǔ)類的類對(duì)象的地址
Class c1 = [Person class];
2). 調(diào)用對(duì)象的對(duì)象方法老虫,就可以了得到這個(gè)對(duì)象所屬的類的Class對(duì)象的地址.
Person p1 = [Person new];
Class c2 = [p1 class];
注意: 聲明Class指針的時(shí)候叶骨,不需要加,因?yàn)閠ypedef的時(shí)候就已經(jīng)加*了.
3). 拿到存儲(chǔ)類的類對(duì)象以后
Class c1 = [Person class];
c1對(duì)象就是Person類, c1 完全等價(jià)于Person
a. 使用類對(duì)象來調(diào)用類的類方法,
Class c1 = [Person class];
[c1 sayHi] 等價(jià)于 [Person sayHi];
b. 創(chuàng)建對(duì)象
Class c1 = [Person class];
[c1 new] 等價(jià)于 [Person new];- 注意: 使用類對(duì)象祈匙,只能調(diào)用類的類方法邓萨,不能調(diào)用對(duì)象方法
SEL全程 select 選擇器
SEL是一個(gè)數(shù)據(jù)類型. 要在內(nèi)存中申請(qǐng)空間存儲(chǔ)數(shù)據(jù),SEL其實(shí)是一個(gè)類,SEL對(duì)象用來存儲(chǔ)一個(gè)方法
1). 類是以Class對(duì)象的形式存儲(chǔ)在代碼段中
類名: 存儲(chǔ)這個(gè)類的類名.NSString
a. 如何將方法存儲(chǔ)在類對(duì)象之中
a). 先創(chuàng)建一個(gè)SEL對(duì)象
b). 將方法的信息存儲(chǔ)在這個(gè)SEL對(duì)象之中
c). 再將這個(gè)SEL作為Class對(duì)象的屬性
2). 拿到存儲(chǔ)方法的SEL對(duì)象
a. 因?yàn)镾EL是typedef類型的菊卷,在自定義的時(shí)候已經(jīng)加了,所以聲明SEL指針的時(shí)候不需要加
b. 取到存儲(chǔ)方法的SEL對(duì)象
SEL s1 = @selector(方法名);
示例:
SEL s1 = @selector(sayHi);
3). 調(diào)用方法的本質(zhì)
Person p1 = [Person new];
[p1 sayHi];
內(nèi)部原理:
a. 先拿到存儲(chǔ)sayHi方法的SEL對(duì)象宝剖,也就是拿到sayHi方法的SEL數(shù)據(jù)洁闰,SEL消息
b. 將這個(gè)SEL消息發(fā)送給p1對(duì)象
c. p1對(duì)象接收到這個(gè)SEL消息之后,就知道調(diào)用方法
d. 根據(jù)對(duì)象的isa指針找到存儲(chǔ)類的類對(duì)象
e. 如果找到這個(gè)類對(duì)象之后万细,搜尋是否有和傳入的SEL數(shù)據(jù)相匹配的扑眉,如果有,就執(zhí)行赖钞,如果乜有就找父類腰素,知道NSObject
OC最重要的1個(gè)機(jī)制: 消息機(jī)制
調(diào)用方法的本質(zhì)其實(shí)就是為對(duì)象發(fā)送SEL消息
[p1 sayHi]: 為p1對(duì)象發(fā)送一個(gè)sayHi方法
4). 手動(dòng)為對(duì)象發(fā)送SEL消息
a. 先得到方法的SEL數(shù)據(jù)
b. 把這個(gè)SEL數(shù)據(jù)發(fā)送給p1對(duì)象
- (id)performSelector:(SEL)aSelector;
示例:
Person *p1 = [Person new]; SEL s1 = @selector(sayHi); [p1 performSelector:sel];
c. 調(diào)用一個(gè)對(duì)象的方法有兩種
a). [對(duì)象名 方法名];
b). 手動(dòng)的為對(duì)象發(fā)送SEL消息
5). 注意事項(xiàng):
a. 如果方法名有參數(shù) 那么方法名是帶了參數(shù)的
b. 如果方法有參數(shù),就調(diào)用另外一個(gè)方法
- (id)performSelector:(SEL)aSelector withObject:(id)object;
c. 如果方法有多個(gè)參數(shù)雪营,就將對(duì)象傳遞過去點(diǎn)語法
OC中也可以使用點(diǎn)語法來訪問對(duì)象的屬性弓千,和Java,C#是完全不一樣的.
1). 使用點(diǎn)語法訪問對(duì)象的屬性.
語法:對(duì)象名.去掉下劃線的屬性名
p1.name = @"jack";
這個(gè)時(shí)候就會(huì)將@"jack"賦值給p1對(duì)象的_name屬性;NSString *name = p1.name;
把p1對(duì)象的_name屬性值取出來
2). 點(diǎn)語法原理
p1.age = 18;
這句話的本質(zhì)并不是把18直接賦值給p1對(duì)象的_age屬性,點(diǎn)語法在編譯器編譯的時(shí)候献起,會(huì)將點(diǎn)語法轉(zhuǎn)換為調(diào)用setter/getter的代碼
a. 當(dāng)使用點(diǎn)語法賦值的時(shí)候洋访,這個(gè)時(shí)候編譯器會(huì)將點(diǎn)語法轉(zhuǎn)換為調(diào)用setter方法的代碼.
對(duì)象名.去掉下劃線的屬性名 = 數(shù)據(jù);
轉(zhuǎn)換為:
[對(duì)象名 set去掉下劃線的屬性名首字母大寫] = 數(shù)據(jù);
p1.age = 10相當(dāng)于[p1 setAge:10];
b. 當(dāng)使用點(diǎn)語法取值的時(shí)候镣陕,這個(gè)時(shí)候編譯器會(huì)將點(diǎn)語法轉(zhuǎn)換為調(diào)用getter方法的代碼.
對(duì)象名.去掉下劃線的屬性名;
轉(zhuǎn)換為:
[對(duì)象名 去掉下劃線的屬性名];
int age = p1.ag相當(dāng)于[p1 age];
3). 注意:
a. 在getter/setter方法中慎用點(diǎn)語法, 因?yàn)橛锌赡軙?huì)造成無線遞歸導(dǎo)致程序崩潰
b. 點(diǎn)語法在編譯器編譯的時(shí)候會(huì)轉(zhuǎn)換為setter/getter方法的代碼.
如果我們的setter和getter方法名不符合規(guī)范,那么就會(huì)報(bào)錯(cuò).
c. 如果屬性沒有封裝getter/setter是無法使用點(diǎn)語法的@property
1). 寫一個(gè)類
a. 寫屬性
b. 聲明屬性的getter/setter
c. 再實(shí)現(xiàn)getter/setter
2). @property
a. 作用: 自動(dòng)生成getter/setter的聲明姻政,應(yīng)該寫在@interface聲明之中
b. 語法:
@property 數(shù)據(jù)類型 變量名;
@property int age;
c. 原理:
編譯器在編譯的時(shí)候呆抑,會(huì)根據(jù)@property生成getter/setter方法的實(shí)現(xiàn)
@property 數(shù)據(jù)類型 名稱;
生成為:
- (void)set首字母大寫的名稱:參數(shù);
- (數(shù)據(jù)類型)名稱;
示例:
@property int age;
- (void)setAge:(int)age;
- (int)age;
3). 使用@property注意:
a. @property的類型和屬性的類型一致.
@property的名稱和屬性名稱一致(去掉下劃線).
b. @property的名稱決定了生成getter和setter方法的名稱.
@property的數(shù)據(jù)類型決定了生成的getter和setter方法的數(shù)據(jù)類型.
c. @property只是生成getter和setter方法的聲明,實(shí)現(xiàn)要自己寫.-
@synthesize
1). 作用: 自動(dòng)生成getter/setter方法的實(shí)現(xiàn),應(yīng)該寫在類的實(shí)現(xiàn)當(dāng)中
2). 語法:
@synthesize @property名稱;// 聲明 @interface Person : NSObject{ int _age; } @property int age; @end; // 實(shí)現(xiàn) @implementation Person @synthesize age; @end
3). @synthesize做的事情
a. 生成一個(gè)真私有的屬性,屬性的類型和@synthesize對(duì)應(yīng)的@property類型一致汁展,屬性的名字和@synthesize對(duì)應(yīng)的@property名字一致.
b. 自動(dòng)生成setter方法的實(shí)現(xiàn)
實(shí)現(xiàn)的方式:將參數(shù)直接賦值給自動(dòng)生成的那個(gè)私有屬性.并且沒有做任何的邏輯驗(yàn)證
c. 自動(dòng)生成getter方法的實(shí)現(xiàn)
4). 希望@synthesize不要自動(dòng)生成私有屬性,getter/setter的實(shí)現(xiàn)中操作我們寫好的屬性.
a. 語法
@synthesize @property 名稱 = 已經(jīng)存在的屬性名;
示例: @synthesiez age = _age;
a). 不會(huì)再生成私有屬性
b). 直接生成getter/setter的實(shí)現(xiàn).
setter實(shí)現(xiàn): 把參數(shù)的值直接賦值給指定的屬性.
getter的實(shí)現(xiàn): 直接返回指定的屬性的值.
5). 注意:
a. 如果直接寫一個(gè)@synthesize
@synthesize name;
b. 如果指定操作的屬性.
@synthesize name = _name;
c. 生成的setter方法實(shí)現(xiàn)中沒有任何邏輯驗(yàn)證,生成的getter方法的實(shí)現(xiàn)中式直接返回屬性的值
d. 如果要有自己的邏輯驗(yàn)證,需要自己實(shí)現(xiàn).
6). 批量聲明
a. 如果多個(gè)@property的類型一致,可以批量聲明.
@property float height,weight;
b. @synthesize批量寫, @synthesize name = _name, age = _age; @property增強(qiáng)
1). 從4.4之后, 對(duì)@property做了增強(qiáng).
2). 只需要寫一個(gè)@property鹊碍,編譯器就會(huì)自動(dòng)的生成私有屬性、生成getter/setter的聲明食绿、生成getter/setter的實(shí)現(xiàn).
3). @property NSString *name;
a. 自動(dòng)的生成一個(gè)私有屬性, 屬性的類型和@property類型一致侈咕,屬性的名稱和@property的名稱一致,屬性的名稱自動(dòng)的加下劃線
b. 自動(dòng)的生成getter/setter的聲明
c. 自動(dòng)的生成getter/setter的實(shí)現(xiàn)
setter的實(shí)現(xiàn)直接將參數(shù)的值賦值給自動(dòng)生成的私有屬性.
getter的實(shí)現(xiàn)直接返回私有屬性的值.
4). 使用注意
a. @property的類型一定要和屬性的類型一致,名稱要和屬性的名稱一致
b. 可以批量聲明-
動(dòng)態(tài)類型和靜態(tài)類型
1). OC是一門若語言,編譯器在編譯的時(shí)候炫欺,檢查的時(shí)候沒有那么嚴(yán)格.
優(yōu)點(diǎn):靈活
缺點(diǎn):太靈活強(qiáng)類型的語言: 編譯器在編譯的時(shí)候做語言檢查的時(shí)候乎完,非常嚴(yán)格.
2).
a. 靜態(tài)類型:指的是一個(gè)指針指向的對(duì)象是一個(gè)本類對(duì)象,
b. 動(dòng)態(tài)類型: 指的是一個(gè)指針指向的對(duì)象不是本類對(duì)象品洛。
3). 編譯檢查:編譯器在編譯的時(shí)候树姨,能不能通過1個(gè)指針去調(diào)用指針指向的對(duì)象的方法.
判斷原則:看這個(gè)指針?biāo)鶎俚念愋椭惺欠裼羞@個(gè)方法,如果有就認(rèn)為可以調(diào)用桥状,編譯通過,如果沒有就報(bào)錯(cuò)帽揪。
4). 運(yùn)行檢查: 編譯檢查只是騙過了編譯器,但是這個(gè)方法究竟能不能執(zhí)行還不一定辅斟,運(yùn)行時(shí)會(huì)去檢查對(duì)象當(dāng)中是否有這個(gè)方法 NSObject是OC中所有類的基類,根據(jù)LSP NSObject指針就可以指向任意的OC對(duì)象转晰,所有NSObject指針時(shí)一個(gè)萬能指針.可以指向任意的OC對(duì)象.
缺點(diǎn): 如果要調(diào)用指向的子類對(duì)象的獨(dú)有的方法,就必須要就類型的強(qiáng)轉(zhuǎn)士飒。id指針是一個(gè)萬能指針查邢,可以指向任意的OC對(duì)象.
1). id是一個(gè)typedef類型,在定義的時(shí)候已經(jīng)加了酵幕,所以聲明id指針的時(shí)候就不需要加了
2). id指針是一個(gè)萬能指針扰藕,任意的OC對(duì)象都可以指。
3). NSObject與id
相同點(diǎn): 萬能指針芳撒,都可以指向任意的OC對(duì)象
不同點(diǎn): 通過NSObject指針去調(diào)用對(duì)象的方法的時(shí)候邓深,編譯器會(huì)做編譯檢查.通過id類型的指針去調(diào)用對(duì)象方法的時(shí)候,不會(huì)報(bào)錯(cuò).
4). 注意:id指針只能調(diào)用方法笔刹,不能使用點(diǎn)語法instancetype
1). 如果方法的返回值是instancetype代表方法的返回值是當(dāng)前類的對(duì)象.
// 聲明 - (instancetype)person; // 實(shí)現(xiàn) - (instancetype)person{ return [self new]; }
建議:
a. 如果方法內(nèi)部是在創(chuàng)建當(dāng)前類的對(duì)象芥备,不要寫死成類名[類名 new],而是用self代替類名
b. 如果方法的返回值是當(dāng)前類的對(duì)象,也不要寫死了舌菜,而是寫instancetype.
2). id和instancetype的區(qū)別
a. instancetype只能作為方法的返回值萌壳,不能在其他地方使用,id既可以聲明指針變量也可以作為參數(shù),也可以作為返回值
b. instancetype是一個(gè)有類型的代表當(dāng)前類的對(duì)象讶凉,id是一個(gè)無類型的指針染乌,僅僅是一個(gè)地址,沒有類型的指針-
構(gòu)造方法
1). 創(chuàng)建對(duì)象
類名 *指針名 = [類名 new];
new 實(shí)際上是個(gè)類方法.
new方法作用:
-> 創(chuàng)建對(duì)象
-> 初始化對(duì)象
-> 把對(duì)象的地址返回
new方法的內(nèi)部懂讯,其實(shí)是先調(diào)用alloc方法荷憋,再調(diào)用init方法
alloc方法是一個(gè)類方法,那個(gè)類調(diào)用這個(gè)方法就創(chuàng)建哪個(gè)類對(duì)象
init是一個(gè)對(duì)象方法褐望,初始化對(duì)象
創(chuàng)建對(duì)象的完整步驟:
應(yīng)該是先使用alloc創(chuàng)建一個(gè)對(duì)象勒庄,然后使用init初始化這個(gè)對(duì)象
Person *p1 = [Person new];
完全等價(jià)于
Person *p1 = [[Person alloc] init];2). init方法
作用: 初始化對(duì)象,為對(duì)象的屬性賦初始值,這個(gè)init方法我們叫做構(gòu)造方法
init方法做到的事情: 初始化對(duì)象.
3). 想要讓創(chuàng)建對(duì)象的屬性的默認(rèn)值不是nil,NULL瘫里,0实蔽,那么我們可以重寫init方法.按照我們自己的想法為對(duì)象的屬性賦值.
重寫init方法規(guī)范:
a. 必須先調(diào)用父類的init方法.
b. 調(diào)用方法初始化對(duì)象失敗返回nil
c. 判斷父類是否初始化成功,如果不是nil,說明初始化成功
d. 如果初始化成功,就初始化當(dāng)前對(duì)象的屬性
e. 最后返回self- (instancetype)init{ // 初始化父類屬性的值 self = [super init]; if(self){ self.name = @"jack"; } return self; }
4). 自定義構(gòu)造方法
規(guī)范:
a. 返回值必須是instancetype
b. 自定義構(gòu)造方法開頭必須是initWith
c. 方法的實(shí)現(xiàn)與init的要求一樣- (instancetype)initWithName:(NSString *)name{ // 初始化父類屬性的值 self = [super init]; if(self){ self.name = name; } return self; } Dog *d1 = [[Dog alloc] initWithName:@"小黃"];
-
動(dòng)態(tài)類型檢測(cè)
1). 判斷指針指向的對(duì)象當(dāng)中谨读,指定的方法是否可以調(diào)用Person *p1 = [Person new]; BOOL isHave = [p1 responseToSelector:@selector(setName:)];
2). 判斷類方法是否可以調(diào)用
3). 判斷對(duì)象是否為指定類對(duì)象或者子類對(duì)象NSMutableString *str = [NSMutableString class]; BOOL res = [str isKindOfClass:[NSString class]];
4). 判斷對(duì)象是否為指定類的對(duì)象(不包括子類)
NSMutableString *str = [NSMutableString class]; BOOL res = [str isMemberOfClass:[NSString class]];
5). 判斷指定的類是否為另外一個(gè)類的子類
BOOL res = [NSMutableString isSubclassOfClass:[NSString class]];