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