歡迎你們關注個人公衆號,我會按期分享一些我在項目中遇到問題的解決辦法和一些iOS實用的技巧,現階段主要是整理出一些基礎的知識記錄下來
編程
文章也會同步更新到個人博客:
ppsheep.com安全
本篇文章,主要是對OC中的一些原理的講解,可能會有一些枯燥,可是真正當你理解時,會有一種豁然開朗的感受。這裏會涉及到 對象,屬性,消息以及運行期的一些介紹,只有咱們真正理解了這些原理以後,咱們的開發水平纔會進一步提高,而不是止步於view的簡單編寫,view的編寫想要寫得好,也須要了解這一些原理。框架
在開始講屬性以前,咱們先來理解一下幾個概念:函數
上面三個概念,在OC編程中尤爲重要,雖然如今可能你沒有很深入的理解,可是隨着學習深刻,你確定可以體會到。性能
"屬性(property)"相信你們都很熟悉,是OC中用來存儲對象的數據的實例變量。實例變量通常是經過"存取方法"來訪問,"設置方法"來設置實例變量。在以前咱們已經講過,對於實例變量,若是是自己訪問,那麼讀取最好是直接讀取(採用下劃線方式直接訪問),而設置最好使用屬性來設置。具體的,能夠參見上一篇(iOS-有效編寫高質量Objective-C方法-二)。學習
對於一些簡單的概念性的東西我就不講了,給出結論就行:atom
接下來,咱們來講幾個關鍵字:spa
@synthesize線程
咱們能夠在代碼中經過這個關鍵字來指定咱們想要的實例變量代理
例如:在頭文件中
@interface : NSObject
@property(nonatomic, copy) NSString *name;
@end複製代碼
這個屬性,在咱們運行期環境下,生成的實例變量爲_name,可是咱們在.m中並不想使用這個名稱,那麼咱們在實現文件裏就能夠這樣寫:
@implementation
@synthesize name = _myName
@end複製代碼
那麼咱們在.m實現文件中,均可以直接使用_myName來操做屬性name
不過爲了書寫的規範,和團隊之間協做,我仍是建議按照規範的OC代碼風格來編寫代碼,團隊成員之間,一看就可以看清楚代碼
@dynamic
這個關鍵字 是用來阻止編譯器自動合成存取方法,不過這個關鍵字我都用的不多,上面的關鍵字一樣的,也使用較少。
這個關鍵字的意思是:阻止編譯器合成屬性所對應的實例變量,也不要合成實例變量的存取方法
這裏講一下實例變量就是帶下劃線的_name而屬性是經過property聲明的name
這兩個須要區分開來
所謂的屬性特質,就是指咱們在申明屬性的時候,property括號中跟的一些關鍵字
@property(nonatomic, readwrite, copy);複製代碼
其中的nonatomic,readwrite,copy這些都是屬性特質,咱們先來講nonatomic
這個關鍵字叫作屬性的原子性,通俗來講,這個關鍵字主要是來控制屬性的同步鎖。
同步鎖:不一樣的線程在讀取屬性的時候,若是屬性是經過atomic來聲明的,那麼這兩個線程老是可以讀到屬性的有效值(注意這裏是有效的屬性值,並無說是正確的屬性值),若是屬性是經過nonatomic聲明的,那麼不一樣的線程讀取屬性值時,若是有線程正在修改該屬性的值,另外的線程正在讀取屬性值,那麼就可能將還未修改完成的屬性值讀取出來(這裏是還沒有修改完成的屬性值,有可能讀出一個徹底沒有任何意義的屬性值)。
那麼又要來講一說,爲何咱們老是看到在編寫iOS程序時,屬性老是使用nonatomic來聲明的呢,這是由於在iOS中使用同步鎖的開銷太大,這會帶來性能問題。在通常狀況下,咱們並不要求屬性必須具備原子性,由於這個原子性並非說就是"線程安全了",若是咱們須要實現線程安全,那麼還須要使用更爲底層的同步鎖定機制才行,即使是使用atomic來聲明,不一樣的線程仍是可能讀取到不一樣的屬性值,只是說這個屬性值是有效的,有意義的。
因此咱們在開發iOS程序時,仍是使用nonaomic來聲明,可是在macOS程序開發中,卻不會遇到這種性能瓶頸,性能配置不同嘛
這個屬性特質,咱們根據字面意思就能看出來,就是聲明屬性權限的,這個也沒什麼好說的了。
這個多是咱們平時用的最多的,也是思考最多的,其實平時咱們怎麼用,都是知道的,可是爲何這麼用呢?
assign:"設置方法"只會針對"純量類型"進行賦值,例如CGFloat、NSInteger這種
strong:此特質象徵了一種擁有關係,在"設置方法"中,這種屬性是先保留新值,而且釋放舊值,而後將新值設置上去
copy:這種方法和strong類型有點類似,可是它並非保留新值,而是直接就想新值拷貝,當屬性爲NSString類型時,咱們常用這種,那麼爲何咱們在NSString常用拷貝呢,覺得咱們在設置時,可能會傳進來一個NSMutableString對象,這個對象是NSString的子類,是能夠賦值給NSString的,若是咱們不使用拷貝,那麼當外部改變NSMultableString值時,咱們的屬性值也會直接被修改掉,因此這時,咱們就須要拷貝一份不可變的
weak:這個是一種弱引用,爲這種方法設置時,既不會保留新值,也不會釋放舊值,當屬性所指的對象銷燬時,屬性值也會被清空,在ViewController中定義view時咱們常常會使用到weak,可是咱們常常仍是將view聲明爲strong,固然這使用起來不會有很大的影響,可是咱們的應用在運行過程當中,就會產生不少的沒用view屬性值沒有被釋放掉,佔用無效內存。因此建議你們在使用view時,仍是聲明爲weak
@property(nonatomic,weak) UILable *lable;
//初始化lable時
UILable * lable = [[UILable alloc] init];
[self.view addSubview: lable];
self.lable = lable;複製代碼
在咱們定義的屬性爲Boolean值時,咱們的習慣是獲取方法,通常是"is"
開頭,那麼咱們就能夠在聲明時,這樣書寫
@property(nonatomic,getter=isOn) Bool on;複製代碼
屬性的獲取方法,就成了isOn;
咱們在屬性中定義了屬性特質,那麼咱們在書寫賦值時,就應該嚴格按照屬性特質賦值,例如,咱們有一個初始方法,須要對咱們的屬性NSString name賦值
- (instancetype)initWithName:(NSString *)name{
if(self = [super init]){
//此處就應該使用copy來對name賦值
_name = [name copy];
}
}複製代碼
類族是一種隱藏抽象基類背後實現細節的頗有用的模式。。那麼什麼叫作類族呢?
舉個例子:
在UIKit中有一個名叫UIButton的類,若是想要建立按鈕,咱們能夠調用一個類方法
+ (UIButton *)buttonWithType:(UIButtonType)type;複製代碼
該方法返回的對象,取決於傳入按鈕的類型,然而,無論傳入的是什麼類型,返回的類都是繼承自同一個基類:UIButton。 這樣,全部繼承自UIButton的類組成了一個類族。
在系統框架中,使用到了不少的類族。。
那麼爲何要這樣作呢?
UIButton這個例子,在實現時,使用者無需關心建立出來的按鈕是屬於哪個類,也不用考慮按鈕的繪製細節,我只須要知道,我怎麼建立按鈕,如何設置標題。如何增長點擊操做等。
咱們如今來假設一個處理僱員的類,每一個僱員都擁有本身的名字和薪水這兩個屬性,管理者能夠命令器執行平常工做。可是,各類僱員的工做內容卻不一樣,經理在帶領僱員作項目的時候,無需關心每一個人怎樣完成本身的工做,只須要指示其開工便可。
首先咱們定義一個基類:
typedef NS_ENUM(NSUinteger, PPSEmployeeType){
PPSEmployeeTypeDeveloper,
PPSEmployeeTypeDesigner,
PPSEmployeeTypeFinance,
}
@interface PPSEmployee : NSObject
@property (nonatomic, copy) NSStirng *name;
@property (nonamotic, assign) NSUInteger salary;
//建立方法
+ (PPSEmployee *)employeeWithType:(PPSEmployeeType) type;
//指示開工
- (void)doADaysWork;
@end複製代碼
@implementation PPSEmployee
+ (PPSEmployee *)employeeWithType:(PPSEmployeeType) type{
switch (type){
case PPSEmployeeTypeDeveloper:
return [PPSEmployeeDeveloper new];
break;
case PPSEmployeeTypeDesigner:
return [PPSEmployeeDesigner new];
break;
case PPSEmployeeTypeFinance:
return [PPSEmployeeFinance new];
break;
}
}
- (void)doADaysWork{
// 子類覆寫
}
@end複製代碼
每一個實體子類都是從基類繼承而來
@interface PPSEmployeeDeveloper : PPSEmployee
@end複製代碼
@implementation PPSEmployeeDeveloper
- (void)doADaysWork{
[self coding];
}
@end複製代碼
咱們上面實現的方式,是根據僱員的類別,建立僱員類的實例,這實際上是一種工廠模式。在Java中,咱們知道這種方式通常是經過抽象類來實現,可是OC中沒有抽象類這一說,因而開發者一般會在文檔中寫明使用方法。
在Cocoa中 有不少的類族 大部分的集合類型都是類族 咱們在一個對象是不是屬於某一個類時,若是咱們採用下面的方式,每每得不到咱們想要的效果:
id myArr = @[@"a",@"b"];
if([myArr class] == [NSArray class]){
//這裏永遠不會跑到 由於 咱們知道這裏[myArr class]返回的永遠是NSArr的一個子類,NSArray只是一個基類
}複製代碼
固然,咱們要作這個判斷的時候,應該都知道是使用isKindOfClass 這個方法,這個方法實際上是用來判斷是不是同一類族,而不是某個類
有時候咱們須要在對象中存放相關的信息,這時候咱們能想到的方法就是,建立一個子類,而後咱們使用的時候 直接使用子類。 可是並非全部狀況都可以這樣作,有時候類的實例多是因爲某種機制所創建的,咱們開發者是沒有辦法建立出本身創建的子類的實例。幸虧,咱們能夠經過OC的一項強大的特性"關聯對象"來解決這個問題。
那麼什麼事關聯對象呢?
咱們能夠給某個對象關聯許多其餘對象,這些對象經過"鍵"來區分。存儲值的時候,能夠經過指明存儲策略來維護內存,存儲策略就是你存儲的是一個NSString啊,那你就該把從存儲策略改成copy,相似於這種
下面是對象關聯類型:
關聯類型 | 等效的@property屬性 |
---|---|
OBJC_ASSOCIATION_ASSIGN | assign |
OBJC_ASSOCIATION_COPY_NONATOMIC | nonatomic,copy |
OBJC_ASSOCIATION_RETAIN_NONATOMIC | nonatomic,retain |
OBJC_ASSOCIATION_COPY | copy |
OBJC_ASSOCIATION_RETAIN | retain |
管理關聯對象的相關方法:
使用這種方法時,咱們能夠把某對象想象成一個NSDictionary,把關聯到該對象的值理解爲字典中的條目,那麼這些關聯對象,就至關於設置字典裏的值和獲取字典裏的值了
在iOS中,咱們若是想要使用UIAlertView 咱們須要這樣定義
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"你肯定嗎?"
message:@"可能沒那麼肯定吧"
delegate:self
cancelButtonTitle:@"取消"
otherButtonTitles:@"繼續", nil];
[alert show];複製代碼
而後咱們須要實現UIAlertView的代理,來進行操做的識別判斷
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 0) {
[self doCancle];
}else{
[self doContinue];
}
}複製代碼
這樣寫,如今看來是沒有什麼問題的,可是若是咱們須要在當前的一個類中,處理多個警告信息,那麼代碼將會變得複雜,咱們須要在delegate中判斷當前的UIAlertView的信息,根據不一樣的信息實行不一樣的操做。
若是 咱們能在建立UIAlertView的時候 就將這些操做作好,那麼,在delegate中咱們就不須要判斷UIAlertView了。事實上這種方法是可行的。
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"你肯定嗎?"
message:@"可能沒那麼肯定吧"
delegate:self
cancelButtonTitle:@"取消"
otherButtonTitles:@"繼續", nil];
void (^block) (NSInteger) = ^(NSInteger buttonIndex){
if (buttonIndex == 0) {
NSLog(@"cancle");
}else{
NSLog(@"continue");
}
};
//將block設置爲UIAlertView的關聯對象
objc_setAssociatedObject(alert, PPSMyAlertViewKey, block, OBJC_ASSOCIATION_COPY);
[alert show];複製代碼
咱們只須要在delegate中拿到block就行
void (^block)(NSInteger) = objc_getAssociatedObject(alertView, PPSMyAlertViewKey);
block(buttonIndex);複製代碼