1、成員變量
- 1 實(shí)例變量(成員變量)既可以在@interface中定義,也可以在@implementation中定義
- 2 寫在@implementation中的成員變量,默認(rèn)就是私有成員變量,并且和利用@private修飾的不太一樣,在@implementation中定義的成員變量在其他類中無法查看,也無法訪問
- 3 在@implementation中定義的私有變量只能在本類中訪問
2、@property是一個(gè)編譯器指令
1 在Xocde4.4之前, 可以使用@porperty來代替getter/setter方法的聲明
也就是說我們只需要寫上@porperty就不用寫getter/setter方法的聲明
編譯器只要看到@property, 就知道我們要生成某一個(gè)屬性的getter/setter方法
的聲明2從Xcode4.4以后apple對(duì)@property進(jìn)行了一個(gè)增強(qiáng), 以后只要利用一個(gè)@property就可以同時(shí)生成setter/getter方法的聲明和實(shí)現(xiàn)
沒有告訴@property要將傳入的參數(shù)賦值給誰, 默認(rèn)@property會(huì)將傳入的屬性賦值給_開頭的成員變量3 @property有一個(gè)弊端: 它只會(huì)生成最簡(jiǎn)單的getter/setter方法的聲明和實(shí)現(xiàn), 并不會(huì)對(duì)傳入的數(shù)據(jù)進(jìn)行過濾
如果想對(duì)傳入的數(shù)據(jù)進(jìn)行過濾, 那么我們就必須重寫getter/setter方法
如果不想對(duì)傳入的數(shù)據(jù)進(jìn)行過濾, 僅僅是提供一個(gè)方法給外界操作成員變量, 那么就可以使用@property4 如果利用@property來生成getter/setter方法, 那么我們可以不寫成員變量,
系統(tǒng)會(huì)自動(dòng)給我們生成一個(gè)_開頭的成員變量
注意: @property自動(dòng)幫我們生成的成員變量是一個(gè)私有的成員變量, 也就是說是在.m文件中生成的, 而不是在.h文件中生成的5 如果重寫了setter方法, 那么property就只會(huì)生成getter方法
如果重寫了getter方法, 那么property就只會(huì)生成setter方法
如果同時(shí)重寫了getter/setter方法, 那么property就不會(huì)自動(dòng)幫我們生成私有的成員變量6 readwrite: 代表既生成getter方法 , 也生成setter方法
默認(rèn)情況下 @property就是readwrite的7 readonly: 代表只生成getter方法不生成setter方法
3、 @synthesize
- 1 @synthesize是一個(gè)編譯器指令, 它可以簡(jiǎn)化我們getter/setter方法的實(shí)現(xiàn)
- 2 什么是實(shí)現(xiàn):
- 在聲明后面寫上大括號(hào)就代表著實(shí)現(xiàn)
- 3 在@synthesize后面告訴編譯器, 需要實(shí)現(xiàn)哪個(gè)@property生成的聲明
- 4 告訴@synthesize, 需要將傳入的值賦值給誰和返回誰的值給調(diào)用者
- 5 如果在@synthesize后面沒有告訴系統(tǒng)將傳入的值賦值給誰, 系統(tǒng)默認(rèn)會(huì)賦值給和@synthesize后面寫得名稱相同的成員變量
//@synthesize age = _age;
/*
- (void)setAge:(int)age
{
_number = age;
}
- (int)age
{
return _number
;
}
*/
//@synthesize age = _number;
// 如果在@synthesize后面沒有告訴系統(tǒng)將傳入的值賦值給誰, 系統(tǒng)默認(rèn)會(huì)賦值給和@synthesize后面寫得名稱相同的成員變量
// _age? age;
@synthesize age;
/*
- (void)setAge:(int)age
{
_age = age;
}
- (int)age
{
return _age;
}
*/
4、id 類型
- 1 id是一個(gè)數(shù)據(jù)類型,并且是一個(gè)動(dòng)態(tài)數(shù)據(jù)類型,數(shù)據(jù)類型可以用來,
- 1 定義變量
- 2 作為函數(shù)的參數(shù)
- 3 作為函數(shù)的返回值
- 2 默認(rèn)情況下所有的數(shù)據(jù)類型都是靜態(tài)數(shù)據(jù)類型,靜態(tài)數(shù)據(jù)類型的特點(diǎn):
- 1 在編譯時(shí)就知道變量類型
- 2 知道變量中有哪些屬性和方法
- 3 在編譯的時(shí)候就可以訪問這些屬性和方法
- 4 并且如果是通過靜態(tài)數(shù)據(jù)類型定義的變量,如果訪問了不屬于靜態(tài)數(shù)據(jù)類型的方法和屬性,那么編譯器就會(huì)報(bào)錯(cuò)。
- 3、
動(dòng)態(tài)
數(shù)據(jù)類型的特點(diǎn)- 1 在編譯的時(shí)候編譯器并不知道變量的真是類型,只有在運(yùn)行的時(shí)候才知道它的真是類型 。并且如果通過動(dòng)態(tài)數(shù)據(jù)類型定義變量,如果訪問了不屬于動(dòng)態(tài)數(shù)據(jù)類型的方法和屬性,編譯器就不會(huì)報(bào)錯(cuò)。
id == NSObject * 萬能指針
id和NSObject *的區(qū)別:
NSObject *是一個(gè)靜態(tài)數(shù)據(jù)類型
id 是一個(gè)動(dòng)態(tài)數(shù)據(jù)類型
弊端: 由于動(dòng)態(tài)數(shù)據(jù)類型可以調(diào)用任意方法, 所以有可能調(diào)用到不屬于自己的方法, 而編譯時(shí)又不會(huì)報(bào)錯(cuò), 所以可能導(dǎo)致運(yùn)行時(shí)的錯(cuò)誤
為了避免動(dòng)態(tài)數(shù)據(jù)類型引發(fā)的運(yùn)行時(shí)的錯(cuò)誤, 一般情況下如果使用動(dòng)態(tài)數(shù)據(jù)類型定義一個(gè)變量, 在調(diào)用這個(gè)對(duì)象的方法之前會(huì)進(jìn)行一次判斷, 判斷當(dāng)前對(duì)象是否能夠調(diào)用這個(gè)方法
5 new方法的實(shí)現(xiàn)和原理
- 1 new做了三件事
1.開辟了存儲(chǔ)空間+alloc 方法
2.初始化所有的屬性(成員變量)- init方法
3.返回對(duì)象的地址
- 2 alloc 做了什么事情?
1 開辟了存儲(chǔ)空間
2 將所有的屬性設(shè)置為0
3 返回當(dāng)前實(shí)例對(duì)象的地址
注意:1.初始化成員變量, 但是默認(rèn)情況下init的實(shí)現(xiàn)是什么都沒有做
2.返回 初始化后的實(shí)例對(duì)象地址
注意: alloc返回的地址, 和init返回的地址是同一個(gè)地址
建議大家以后創(chuàng)建一個(gè)對(duì)象都使用 alloc init, 這樣可以統(tǒng)一編碼格式
6 構(gòu)造方法
在oc中init開頭的方法,我們稱之為構(gòu)造方法
構(gòu)造方法的用途:用于初始化一個(gè)對(duì)象,讓某個(gè)對(duì)象一創(chuàng)建出來就擁有某些屬性和值
重寫init方法,在init方法中初始化成員變量
注意:重寫init方法必須按照蘋果規(guī)定的格式重寫,如果不按照規(guī)定會(huì)引發(fā)一些未知的錯(cuò)誤
1.必須先初始化父類, 再初始化子類
// 2.必須判斷父類是否初始化成功, 只有父類初始化成功才能繼續(xù)初始化子類
// 3.返回當(dāng)前對(duì)象的地址
- (instancetype)init
{
// 1.初始化父類
// 只要父類初始化成功 , 就會(huì)返回對(duì)應(yīng)的地址, 如果初始化失敗, 就會(huì)返回nil
// nil == 0 == 假 == 沒有初始化成功
self = [super init];
// 2.判斷父類是否初始化成功
if (self != nil) {
// 3.初始化子類
// 設(shè)置屬性的值
_age = 6;
}
// 4.返回地址
return self;
}
7 instancetype和id的區(qū)別
如果init方法的返回值是instancetype, 那么將返回值賦值給一個(gè)其它的對(duì)象會(huì)報(bào)一個(gè)警告
如果是在以前, init的返回值是id, 那么將init返回的對(duì)象地址賦值給其它對(duì)象是不會(huì)報(bào)錯(cuò)的
instancetype == id == 萬能指針 == 指向一個(gè)對(duì)象
id在編譯的時(shí)候不能判斷對(duì)象的真實(shí)類型
instancetype在編譯的時(shí)候可以判斷對(duì)象的真實(shí)類型
id和instancetype除了一個(gè)在編譯時(shí)不知道真實(shí)類型, 一個(gè)在編譯時(shí)知道真實(shí)類型以外, 還有一個(gè)區(qū)別
id可以用來定義變量, 可以作為返回值, 可以作為形參
instancetype只能用于作為返回值
注意: 以后但凡自定義構(gòu)造方法, 返回值盡量使用instancetype, 不要使用id
自定義構(gòu)造方法:
其實(shí)就是自定義一個(gè)init方法
1.一定是對(duì)象方法
2.一定返回id/instancetype
3.方法名稱一定以init開頭
8 自定義類工廠方法
自定義類工廠方法是蘋果的一個(gè)規(guī)范, 一般情況下, 我們會(huì)給一個(gè)類提供自定義構(gòu)造方法和自定義類工廠方法用于創(chuàng)建一個(gè)對(duì)象
什么是類工廠方法:
用于快速創(chuàng)建對(duì)象的類方法, 我們稱之為類工廠方法
類工廠方法中主要用于 給對(duì)象分配存儲(chǔ)空間和初始化這塊存儲(chǔ)空間
規(guī)范:
1.一定是類方法 +
2.方法名稱以類的名稱開頭, 首字母小寫
3.一定有返回值, 返回值是id/instancetype
eg:
+ (instancetype)person;
+ (instancetype)person
{
// return [[Person alloc] init];
// 注意: 以后但凡自定義類工廠方法, 在類工廠方法中創(chuàng)建對(duì)象一定不要使用類名來創(chuàng)建
// 一定要使用self來創(chuàng)建
// self在類方法中就代表類對(duì)象, 到底代表哪一個(gè)類對(duì)象呢?
// 誰調(diào)用當(dāng)前方法, self就代表誰
return [[self alloc] init];
}
9 類的本質(zhì)
類的本質(zhì):
類其實(shí)也是一個(gè)對(duì)象, 這個(gè)對(duì)象會(huì)在這個(gè)類第一次被使用的時(shí)候創(chuàng)建
只要有了類對(duì)象, 將來就可以通過類對(duì)象來創(chuàng)建實(shí)例對(duì)象
實(shí)例對(duì)象中有一個(gè)isa指針, 指向創(chuàng)建自己的類對(duì)象
類對(duì)象中保存了當(dāng)前對(duì)象所有的對(duì)象方法
當(dāng)給一個(gè)實(shí)例對(duì)象發(fā)送消息的時(shí)候, 會(huì)根據(jù)實(shí)例對(duì)象中的isa指針去對(duì)應(yīng)的類對(duì)象中查找
類的啟動(dòng)過程
只要程序啟動(dòng)就會(huì)將所有類的代碼加載到內(nèi)存中, 放到代碼區(qū)
load方法會(huì)在當(dāng)前類被加載到內(nèi)存的時(shí)候調(diào)用, 有且僅會(huì)調(diào)用一次
如果存在繼承關(guān)系, 會(huì)先調(diào)用父類的load方法, 再調(diào)用子類的load方法
當(dāng)當(dāng)前類第一次被使用的時(shí)候就會(huì)調(diào)用(創(chuàng)建類對(duì)象的時(shí)候)
initialize方法在整個(gè)程序的運(yùn)行過程中只會(huì)被調(diào)用一次, 無論你使用多少次這個(gè)類都只會(huì)調(diào)用一次
initialize用于對(duì)某一個(gè)類進(jìn)行一次性的初始化
initialize和load一樣, 如果存在繼承關(guān)系, 會(huì)先調(diào)用父類的initialize再調(diào)用子類的initialize
10 SEL類型
// 1.SEL類型的第一個(gè)作用, 配合對(duì)象/類來檢查對(duì)象/類中有沒有實(shí)現(xiàn)某一個(gè)方法
/*
SEL sel = @selector(setAge:);
Person *p = [Person new];
// 判斷p對(duì)象中有沒有實(shí)現(xiàn)-號(hào)開頭的setAge:方法
// 如果P對(duì)象實(shí)現(xiàn)了setAge:方法那么就會(huì)返回YES
// 如果P對(duì)象沒有實(shí)現(xiàn)setAge:方法那么就會(huì)返回NO
BOOL flag = [p respondsToSelector:sel];
NSLog(@"flag = %i", flag);
// respondsToSelector注意點(diǎn): 如果是通過一個(gè)對(duì)象來調(diào)用該方法那么會(huì)判斷該對(duì)象有沒有實(shí)現(xiàn)-號(hào)開頭的方法
// 如果是通過類來調(diào)用該方法, 那么會(huì)判斷該類有沒有實(shí)現(xiàn)+號(hào)開頭的方法
SEL sel1 = @selector(test);
flag = [p respondsToSelector:sel1];
NSLog(@"flag = %i", flag);
flag = [Person respondsToSelector:sel1];
NSLog(@"flag = %i", flag);
*/
// 2.SEL類型的第二個(gè)作用, 配合對(duì)象/類來調(diào)用某一個(gè)SEL方法
/*
SEL sel = @selector(demo);
Person *p = [Person new];
// 調(diào)用p對(duì)象中sel類型對(duì)應(yīng)的方法
[p performSelector:sel];
SEL sel1 = @selector(signalWithNumber:);
// withObject: 需要傳遞的參數(shù)
// 注意: 如果通過performSelector調(diào)用有參數(shù)的方法, 那么參數(shù)必須是對(duì)象類型,
// 也就是說方法的形參必須接受的是一個(gè)對(duì)象, 因?yàn)閣ithObject只能傳遞一個(gè)對(duì)象
[p performSelector:sel1 withObject:@"13838383438"];
SEL sel2 = @selector(setAge:);
[p performSelector:sel2 withObject:@(5)];
NSLog(@"age = %i", p.age);
// 注意:performSelector最多只能傳遞2個(gè)參數(shù)
SEL sel3 = @selector(sendMessageWithNumber:andContent:);
[p performSelector:sel3 withObject:@"138383438" withObject:@"abcdefg"];
*/