前言
Objective-C is the primary programming language you use when writing software for OS X and iOS. It"s a superset of the C programming language and provides object-oriented capabilities and a dynamic runtime. Objective-C inherits the syntax, primitive types, and flow control statements of C and adds syntax for defining classes and methods. It also adds language-level support for object graph management and object literals while providing dynamic typing and binding, deferring many responsibilities until runtime.
Objective-C(下面簡稱OC)是由C語言和Smalltalk擴(kuò)展出來的,是C語言的超集,最大的區(qū)別是OC是面向?qū)ο蟮?,其火星文寫法對于之前從事Java開發(fā)的同學(xué)頗感蛋疼,OC最大特點(diǎn)之一是使用“消息結(jié)構(gòu)”而不是“函數(shù)調(diào)用”,所以在運(yùn)行時(shí)執(zhí)行的代碼由運(yùn)行環(huán)境決定,而Java是由編譯器決定。個(gè)人感覺有關(guān)于IOS學(xué)習(xí)的文章相較于Android質(zhì)量較低,有可能是蘋果系統(tǒng)封閉的原因,本文側(cè)重介紹常用的語法,通過對比Java并結(jié)合本人入門的過程和經(jīng)驗(yàn),幫助有需求的同學(xué)快速掌握OC基本編程,為IOS的入門奠定語言基礎(chǔ)。下面首先是寫出第一行代碼,恭喜正式進(jìn)入OC學(xué)習(xí)階段。
int main(int argc, char *argv[]) { @autoreleasepool //創(chuàng)建自動內(nèi)存釋放池 { //打印輸出 NSLog(@"hello world ios!"); return 0; } }
下面介紹OC代碼的文件擴(kuò)展名:
文件擴(kuò)展名 | 類型 |
---|---|
.h | 頭文件,作用是對類、屬性、變量、函數(shù)等的聲明 |
.m | 實(shí)現(xiàn)文件,對頭文件的生命實(shí)現(xiàn)或者擴(kuò)展 |
.mm | 實(shí)現(xiàn)文件,一般是C++代碼 |
如果實(shí)現(xiàn)文件需要引入頭文件時(shí),推薦使用#import
,跟#include
作用相同,優(yōu)化了確保相同文件只會被引入一次,所以傾向用#import。
基本數(shù)據(jù)類型
包括:int float double char
類型 | 字節(jié)數(shù) | 格式化輸出 |
---|---|---|
char | 1 | %c |
int | 4 | %i,%x,%o |
unsigned int | 4 | %i,%x,%o |
short int | 2 | %hi,%hx,%ho |
unsigned short int | 2 | %hi,%hx,%ho |
long int | 8 | %li,%lx,%lo |
unsigned long int | 8 | %lu,%lx,%lo |
long long int | 8 | %lli,%llx,%llo |
unsigned long long int | 8 | %llu,%llx,%llo |
float | 4 | %f |
double | 8 | %f |
long double | 16 | %Lf |
其他數(shù)據(jù)類型
id類型
可以存放任何數(shù)據(jù)類型的對象,類似Java中的Object類,其被定義為指向?qū)ο蟮闹羔槪ū旧砭褪侵羔樍耍?,故定義比如id instance = nil;id類型是多態(tài)和動態(tài)綁定的基礎(chǔ)。
BOOL類型
布爾值為YES/NO或1/0。Java對應(yīng)是true/false
nil和Nil
nil相當(dāng)于Java中的null,表示一個(gè)對象,這個(gè)對象的指針指向空。Nil是定義一個(gè)指向空的類而不是對象。
NSString(不可變字符串)
字符串是非常重要常用的,務(wù)必要掌握常用的基礎(chǔ)用法,包括創(chuàng)建、截取、遍歷、比較、大小寫轉(zhuǎn)換、搜索等,語義跟基本類似Java。
//字符串 NSString *str1 = @"ABC3456789"; //拼接成新的字符串 NSString *str2 = [str1 stringByAppendingString:@"wwww"]; NSLog(@"str = %@", str2); //遍歷 for (int i = 0; i < [str2 length]; i++) { char temp = [str2 characterAtIndex:i]; NSLog(@"字符串第 %d 位輸出 %c", i, temp); } //比較 // sEqualToString方法 :比較字符串是否完全相等,大小寫不一樣也無法完全匹配。 //hasPrefixe方法:逐一匹配字符串頭部。haSuffix方法:匹配字符串的尾部 if ([str2 isEqualToString:str1]) { NSLog(@"相等"); } if ([str2 hasPrefix:@"www"]) { NSLog(@"有該頭部"); } if ([str2 hasSuffix:@"www"]) { NSLog(@"有該尾部"); } if ([str2 compare:str options:NSCaseInsensitiveSearch | NSNumericSearch] == NSOrderedSame) { } NSLog(@"比較結(jié)果:%d", [str2 caseInsensitiveCompare:str1]); //大小寫轉(zhuǎn)換 NSLog(@"str3轉(zhuǎn)大寫:%@",[str2 uppercaseString]); NSLog(@"str3轉(zhuǎn)小寫:%@",[str2 lowercaseString]); NSLog(@"str3首字母轉(zhuǎn)大寫:%@",[str2 capitalizedString]); //字符串截取 NSRange rang = NSMakeRange(2, 2); NSLog(@"str3截?。?@",[str2 substringWithRange:rang]); //搜索 NSRange rang1 = [str2 rangeOfString:@"www"]; NSLog(@"location: %d,length: %d",rang1.location,rang1.length); //替換 //全部替換 NSString *str3 = [str2 stringByReplacingOccurrencesOfString:@" " withString:@"@"]; NSLog(@"替換后字符串為%@", str3); //局部替換 NSString *str4 = [str2 stringByReplacingCharactersInRange:rang withString:@"met"]; NSLog(@"替換后字符串為%@", str4);
NSMutableString(可變字符串)
創(chuàng)建對象的基本寫法是[[NSMutableString alloc]init],*號代表對象,[]代表方法調(diào)用,只能通過類或者對象才能調(diào)用。[NSMutableString alloc]類似Java中new得到一個(gè)對象,然后再調(diào)用init初始化方法。
//創(chuàng)建對象并初始化 NSMutableString *mStr = [[NSMutableString alloc]init]; //appendstring:向字符串尾部添加一個(gè)字符串。 //appendFormat:向字符串尾部添加多個(gè)類型的字符串,可以添加任意數(shù)量與類型的字符串。 [mStr appendString:@"hello world!"]; NSLog(@"字符串創(chuàng)建%@", mStr); [mStr deleteCharactersInRange:[mStr rangeOfString:@"hello"]]; //刪除 NSLog(@"字符串刪除%@", mStr); //插入 [mStr insertString:@"love you" atIndex: mStr.length]; NSLog(@"字符串插入%@", mStr);
NSInteger、NSUInteger和NSNumber
NSInteger不是一個(gè)對象,而是基本數(shù)據(jù)類型中的typedef,NSUInteger是無符號的。 當(dāng)需要使用int類型的變量時(shí),推薦使用NSInteger,這樣不需要考慮設(shè)備是32位或者64位。NSNumber是一個(gè)類,用于包裝基本數(shù)據(jù)類型成為對象,可以理解為Java中的裝箱,為一些集合只能存放對象使用,通過字面量方式非常方便將基本數(shù)據(jù)類型轉(zhuǎn)成對應(yīng)的對象。例如:
//包裝 NSNumber *intNumber = [[NSNumber alloc]initWithInt:43]; //或者字面量方式 NSNumber *intNumber1 = @43; //還原基本數(shù)據(jù)類型,解包 NSLog(@"%d",[intNumber intValue]);
集合
集合不能接受nil,nil是作為集合結(jié)束標(biāo)識符。
1. NSArray(不可變)
類似Java中的ArrayList,可以存儲不同類型的對象,一般情況下數(shù)組元素的類型是相同的,特點(diǎn)是有序、可重復(fù)。下面展示一位數(shù)組的基本操作:
//字面量創(chuàng)建方式 NSArray *arr2 = @[@"aaa",@"bbbbb"]; //工廠方法創(chuàng)建 NSArray *array = [[NSArray alloc] initWithObjects:@"1", @"2", nil]; //取最后一個(gè)元素 [array lastObject]; // 取第一個(gè)元素 [array firstObject]; // 數(shù)組是否包含某個(gè)元素 [array containsObject:@"1"]; // 數(shù)組的大小 int count = (int) array.count; // 第一種方式遍歷 for (int i = 0; i < count; i++) { NSString *_str = [array objectAtIndex:i]; }
那么數(shù)據(jù)要求是多維的呢?多維數(shù)組可以理解為數(shù)組的數(shù)組,通過嵌套的方式,創(chuàng)建如下:
// 字面量創(chuàng)建二維數(shù)組并訪問 NSArray *arr2 = @[@[@11, @12, @13], @[@21, @22, @23], @[@31, @32, @33]]; // 字面量訪問方式(推薦) NSLog(@"arr2[2][2]:%@", arr2[2][2]); // 數(shù)組對象函數(shù)訪問 NSLog(@"arr2[2][2]:%@", [[arr2 objectAtIndex:2] objectAtIndex:2]);
2. NSMutableArray(可變的)
派生于NSArray,理解為動態(tài)數(shù)組,提供增加、刪除、插入、替換等語法糖。
//創(chuàng)建,當(dāng)然還有其他方式 NSMutableArray *mutableArr = [NSMutableArray arrayWithObjects:@"one",@"two",@"three", nil]; //添加 [mutableArr addObject:@"hello"]; //替換 [mutableArr replaceObjectAtIndex:2 withObject:@"tihuan"]; //刪除 [mutableArr removeObjectAtIndex:1]; //插入 [mutableArr insertObject:@"ios" atIndex:1];
多維數(shù)組創(chuàng)建方式如下:
// 初始化作為列的數(shù)組,看做4列 NSMutableArray *columnArray = [[NSMutableArray alloc]initWithCapacity:4]; // 初始化2個(gè)一維數(shù)組,每個(gè)一維數(shù)組有4個(gè)元素,看做1行4列,2行加起來就是2行4列 NSMutableArray *rowArray1 = [[NSMutableArray alloc]initWithCapacity:4]; NSMutableArray *rowArray2 = [[NSMutableArray alloc]initWithCapacity:4]; // 每個(gè)行依次增加數(shù)組元素 // 第一行 [rowArray1 addObject:@"11"]; [rowArray1 addObject:@"12"]; [rowArray1 addObject:@"13"]; [rowArray1 addObject:@"14"]; // 第二行 [rowArray2 addObject:@"21"]; [rowArray2 addObject:@"22"]; [rowArray2 addObject:@"23"]; [rowArray2 addObject:@"24"]; // 分別打印數(shù)組 NSLog(@"myRowArray1: %@", rowArray1); NSLog(@"myRowArray2: %@", rowArray2); NSLog(@"myColumnArray: %@", columnArray);
字典
類似于Java中的HashMap,是一種映射型數(shù)據(jù)結(jié)果,存儲鍵值對,有可變和不可變兩種類型。
NSDictionary
主要特點(diǎn)是不可變,如果集合初始化完成,將內(nèi)容無法修改,無序。
//標(biāo)準(zhǔn)創(chuàng)建 NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:@"cat",@"name1",@"dog",@"name2", nil]; //字面量創(chuàng)建 NSDictionary *dict1 = @{@"name1":@"cat",@"name2":@"dog"}; //第一種遍歷 for (NSString *key in [dict1 allKeys]) { NSLog(@"key: %@,value: %@", key, dict1[key]); } //第二種遍歷方式,通過遍歷器 NSEnumerator *rator = [dict keyEnumerator]; NSString *temp; while (temp = [rator nextObject]) { NSLog(@"%@", temp); } //獲取元素 dict1[@"name"]; [dict1 objectForKey:@"name"]; //集合元素的個(gè)數(shù) NSInteger count = dict1.count; //沙盒文件存儲和讀取Plist [dict5 writeToFile:@"路徑" atomically:YES]; NSDictionary *dict7 = [NSDictionary dictionaryWithContentsOfFile:@"路徑"];
NSMutableDictionary
NSMutableDictionary是NSDictionary的子類。NSMutableDictionary是可變的,動態(tài)添加、更改、刪除元素,因此不能使用字面量方式(@{})來創(chuàng)建一個(gè)可變字典。如果是不可變字典,出現(xiàn)了同名的key,那么后面的key對應(yīng)的值不會被保存,反之是可變字典,出現(xiàn)了同名的key,那么后面的值會覆蓋前面的值。
//創(chuàng)建 NSMutableDictionary *dict = [NSMutableDictionary dictionary]; //添加 [dict setObject:@"dog" forKey:@"name"]; [dict setValue:@"18" forKey:@"age"]; //會將傳入字典中所有的鍵值對取出來添加到dict中 [dict setValuesForKeysWithDictionary:@{@"name1":@"dog"}]; //取元素 [dict objectForKey:@"name"]; dict[@"name"]; //刪除 [dict removeAllObjects]; [dict removeObjectForKey:@"name"]; [dict removeObjectsForKeys:@[@"name", @"age"]]; //更新,如果利用setObject方法給已經(jīng)存在的key賦值,新值會覆蓋舊值 [dict setObject:@"20" forKey:@"age"]; dict[@"age"] = @"30";
NSSet && NSMutableSet
具有很好的存取和查找功能,與NSArray相比NSSet的元素沒有索引,特點(diǎn)是無序,不可重復(fù),類似Java中的HashSet,其中NSMutableSet提供計(jì)算交并集的方法。
NSSet存儲元素的過程:
注意:推薦使用字面量方式創(chuàng)建對象,可以縮短代碼長度,增加可讀性。但是在創(chuàng)建數(shù)組的時(shí)候要注意,如果含有nil就會拋異常,因?yàn)樽置媪繉?shí)際上”語法糖“,效果等同于先創(chuàng)建一個(gè)數(shù)組,然后再把所有的對象添加進(jìn)來,保證數(shù)組不添加nil。
消息傳遞
前言提到Objective-C最大特點(diǎn)之一是繼承了Smalltalk消息傳遞模型,因此在OC中的方法調(diào)用準(zhǔn)備的說法是消息傳遞,類別與消息關(guān)系松散,調(diào)用方法是給對象發(fā)送消息,而方法是對消息的回應(yīng),所有消息的處理直到運(yùn)行時(shí)(即runtime)才會動態(tài)確定,并交由類自行決定如何處理收到的消息。總結(jié)是一個(gè)類不保證一定會回應(yīng)收到的消息,當(dāng)收到的一個(gè)無法處理的消息,會拋出異常。
Java或者C++方法調(diào)用:
obj.method(argument);
OC方法調(diào)用:
[obj method: argument];
我們都知道在Java或者C++中,如果類沒有定義method方法,那么編譯肯定不會通過,但是在OC中,理解是發(fā)送method的消息給obj,obj收到消息后再決定如何回應(yīng)消息,如果類內(nèi)定義了method方法則運(yùn)行,反之不存在運(yùn)行期拋出異常。
類
所有面向?qū)ο蟮木幊潭加蓄惖母拍睿糜诜庋b數(shù)據(jù),這樣的語言特性都有封裝、繼承和多態(tài)。OC對象是類在運(yùn)行期的實(shí)例,包含了類聲明的實(shí)例變量、內(nèi)存拷貝、類成員的指針等。由于OC是C語言的超集,類由兩個(gè)部分組成,分別是定義(interface)和實(shí)現(xiàn)(implementation),下面舉個(gè)?。新建一個(gè)People類,@interface是接口聲明的開始,@end終止結(jié)束,所有的OC編譯指令都是以”@“開始的。類的實(shí)現(xiàn)是通過@implementation指令開頭以@end結(jié)束。對應(yīng)People.h和People.m兩份文件,下圖是類聲明(People.h)的展示,主要包括繼承關(guān)系、成員變量、屬性、方法聲明等,方法的具體實(shí)現(xiàn)是在People.m。
下圖是方法聲明的展示:
當(dāng)然不止Interface區(qū)塊可以定義變量,Implementation區(qū)塊也可以定義,兩者區(qū)別是訪問權(quán)限不一。
前者默認(rèn)權(quán)限為protected,而implementation區(qū)塊的實(shí)體變量則默認(rèn)為private,所以類別私有可以放在implementation區(qū)塊。
訪問修飾符
- @public:任何位置可以訪問。
- @protected:默認(rèn)情況下成員變量的修飾符。
- @private:變量只限于聲明它的類訪問,不允許被繼承。
- @package:限定在當(dāng)前包內(nèi),類似于Java包的概念。
屬性
成員變量是給類內(nèi)使用的,屬性是作為類外訪問成員變量的接口,用于封裝對象的數(shù)據(jù),通過@property聲明,編譯器自動生成setter和getter方法,此過程稱為”自動合成“。類實(shí)現(xiàn)文件中@synthesize語法可以指定實(shí)例變量的名字,一般不推薦這樣做。@dynamic語法是告訴編譯器不要自動合成,在OC中訪問修飾符很少用到,主要是靠聲明屬性取值。
屬性有五個(gè)常用的特質(zhì)修飾:
assign:針對基本數(shù)據(jù)類型賦值操作。
strong:定義一種”擁有關(guān)系“,屬性設(shè)置新值時(shí),先保留新值,并釋放舊值,然后再將新值設(shè)置。
weak:跟strong相反,屬性所指的對象銷毀時(shí),屬性值也會清空。
copy:設(shè)置方法不保留新值,而是拷貝一份。
nonatomic:非原子,非線程安全類型。
Q&A:為什么NSString 、 NSArray、 NSDictionary的屬性要用copy,集合的深淺拷貝是怎樣的?
copy屬性作用是為變量賦值的時(shí)候系統(tǒng)自動copy一份內(nèi)存出來,修改新變量不會影響舊變量。在Apple規(guī)范中,NSString,NSArray,NSDictonary,推薦使用copy屬性,而其NSMubtableString,NSMutableArray, NSMutableDictonary屬性則使用strong屬性。
NSString *sourceString = [NSString stringWithFormat:@"hello ios"]; //不產(chǎn)生新的內(nèi)存空間 NSString *copyStr = [sourceString copy]; //產(chǎn)生新的內(nèi)存空間 NSMutableString *mutableStr = [sourceString mutableCopy]; NSLog(@"sourceString : %@ %p",sourceString,sourceString); NSLog(@"copyStr : %@ %p",copyStr,copyStr); NSLog(@"mutableStr : %@ %p",mutableStr,mutableStr);
使用strong這個(gè)屬性就有可能指向一個(gè)可變對象,如果這個(gè)可變對象在外部被修改了,那么會影響該屬性。例如:
//代碼塊 NSMutableString *string = [NSMutableString stringWithString:@"origin"];//copy NSString *stringCopy = [string copy]; NSLog(@"string address is: %p",string); NSLog(@"stringCopy address is: %p",stringCopy);
結(jié)果:內(nèi)存地址不同
NSMutableString *string = [NSMutableString stringWithString:@"origin"]; //NSString *stringCopy = [string copy]; NSString *stringCopy = string; [string appendString:@"change"]; NSLog(@"string address is: %p",string); NSLog(@"stringCopy address is: %p",stringCopy);
結(jié)果:內(nèi)存地址相同
結(jié)論:
可變對象指向不可變對象會導(dǎo)致不可變對象的值被篡改,所以需要copy屬性。用@property聲明NSString、NSArray、NSDictionary 經(jīng)常使用copy關(guān)鍵字,是因?yàn)樗麄冇袑?yīng)的可變類型NSMutableString、NSMutableArray、NSMutableDictionary,彼此之間可能進(jìn)行賦值操作,為了不可變對象中的內(nèi)容不會被無意間變動,應(yīng)該在設(shè)置新屬性值時(shí)拷貝一份。
淺拷貝:
在Java中淺拷貝如果是基本數(shù)據(jù),則拷貝的是基本數(shù)據(jù)的值;如果是對象,則拷貝的是內(nèi)存地址,修改該對象會影響另外一個(gè)對象。在OC中是對指針的拷貝,拷貝后的指針和原本對象的指針指向同一塊內(nèi)存地址,故同樣會相互影響。
深拷貝:
OC中不僅拷貝指針,而且拷貝指針指向的內(nèi)容,指針指向不同的內(nèi)存地址,故修改不會相互影響原本對象。
非集合類對象中:對immutable對象進(jìn)行copy操作,是指針復(fù)制(淺拷貝),mutableCopy操作時(shí)內(nèi)容復(fù)制;對mutable對象進(jìn)行copy和mutableCopy都是內(nèi)容復(fù)制(深拷貝)。
方法
通過”+“、”-“分別聲明類方法和實(shí)例方法,方法如果帶有多個(gè)參數(shù),參數(shù)在方法名之后接冒號定義,多個(gè)參數(shù)由空格隔開,如果參數(shù)個(gè)數(shù)可變,使用逗號接省略號。例如:
//無參數(shù) - (void)print; //有參數(shù) - (void)print:(int)a andB:(int)b;
構(gòu)造方法
第一種是重寫init方法,第二種是自定義。
/** 重寫初始化方法 **/ - (instancetype)init { self = [super init]; if (self) { _peopleName = @"hello ios"; } return self; } /** 自定義初始化方法 **/ - (instancetype)initWithNameAndAge:(NSString *)name andAge:(int)age { self = [super init]; if (self) { _peopleName = name; _peopleAge = age; } return self; }
創(chuàng)建類對象
所有對象和類的引用都是通過指針實(shí)現(xiàn),嚴(yán)格地說指針就是一個(gè)地址,是一個(gè)常量,而指針變量可以被賦值不同的指針值,創(chuàng)建的對象就是一個(gè)指針變量,通過[People alloc]創(chuàng)建一個(gè)People對象,分配了內(nèi)存,init是初始化對象。構(gòu)造方法有兩種方式,第一種是重寫init方法,第二種是自定義。
People *p1 = [[People alloc] init]; //調(diào)用自定義的構(gòu)造方法 People *p3 = [[People alloc] initWithNameAndAge:@"mingzi" andAge:12]; //調(diào)用方法 [p3 print];
在OC 2.0中,如果創(chuàng)建的對象不需要參數(shù),可以直接使用new:
People *p1 = [People new];
self
作為OC的一個(gè)關(guān)鍵字,代表當(dāng)前類的對象,類似Java中的this,最大的作用是讓類中的一個(gè)方法調(diào)用該類另外一個(gè)方法或者成員變量,可以理解”當(dāng)前類誰調(diào)用了這個(gè)方法,self就代表誰“。
繼承
同Java一樣只能單繼承,只允許最多有一個(gè)直接父類。例如:定義一個(gè)父類Computer和子類MacBook。注意方法重寫類似Java,子類要重寫父類方法不需要重新聲明重寫方法,在實(shí)現(xiàn)部分直接重寫目標(biāo)方法即可。如果需要子類調(diào)用父類的方法,可以通過super關(guān)鍵字調(diào)用。
//Computer.h文件 #import <Foundation/Foundation.h> @interface Computer : NSObject @property(nonatomic,strong)NSString *name; -(void)calculate; @end // Computer.m #import "Computer.h" @implementation Computer @synthesize name; -(void) calculate{ NSLog(@"i can calculate"); } @end // MacBook.h #import "Computer.h" @interface MacBook : Computer @end // MacBook.m #import "MacBook.h" @implementation MacBook @end //main.m int main(int argc, char *argv[]) { @autoreleasepool { MacBook *macBook = [[MacBook alloc] init]; macBook.name = @"mac"; [macBook calculate]; } }
多態(tài)
封裝、繼承和多態(tài)是面向?qū)ο缶幊陶Z言的三大特性,OC的多態(tài)是不同對象對同一消息的不同響應(yīng)方式,實(shí)際過程主要分為三種:
- 繼承
- 重寫
- 指向子類的指針指向父類
可以看出跟Java的多態(tài)類似,理解起來應(yīng)該比較容易,注意是沒有方法重載的,在OC中不允許。
Runtime
實(shí)例:用Runtime新增一個(gè)類Person, person有name屬性,有sayHi方法
#import <UIKit/UIKit.h> #import "AppDelegate.h" #import <objc/runtime.h> #import <objc/message.h> void sayHi(id self, IMP _cmd, id some) { //self指的是調(diào)用該方法傳過來的類 NSLog(@"%@說:%@,我%@歲", [self valueForKey:@"name"], some, object_getIvar(self, class_getInstanceVariable([self class], "_age"))); } int main(int argc, char *argv[]) { @autoreleasepool { //該方法動態(tài)創(chuàng)建一個(gè)類,arg1:繼承自哪個(gè)類 arg2:新建類的名稱 arg3:extraBytes Class Person = objc_allocateClassPair([NSObject class], "Person", 0); //添加兩個(gè)實(shí)例變量name和age,arg2:變量名稱,arg3:內(nèi)存地址大小,arg5:變量類型 class_addIvar(Person, "_name", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *)); class_addIvar(Person, "_age", sizeof(int), sizeof(int), @encode(int)); //注冊方法名 SEL s = sel_registerName("say:"); //arg3:IMP是“implementation”的縮寫,這個(gè)函數(shù)指針決定了最終執(zhí)行哪段代碼 //arg4:方法的參數(shù)及返回值 class_addMethod(Person, s, (IMP)sayHi, "v@:@"); //通過該類創(chuàng)建一個(gè)實(shí)體的對象 id peopleInstance = [[Person alloc]init]; //給對象的 name 實(shí)例變量賦值,下面是第二種賦值方式 [peopleInstance setValue:@"XQM" forKey:@"name"]; //Ivar nameIvar = class_getInstanceVariable(Person, "_name"); //object_setIvar(peopleInstance, nameIvar, @"XQM"); //獲取實(shí)例變量 Ivar ageIvar = class_getInstanceVariable(Person, "_age"); //為變量賦值 object_setIvar(peopleInstance, ageIvar, @21); //調(diào)用sayHi方法,arg2:注冊指定的方法;arg3:帶上有一個(gè)字符串的參數(shù) ((void(*)(id, SEL, id))objc_msgSend)(peopleInstance, s, @"大家好"); //調(diào)用完成,將對象置為空 peopleInstance = nil; //通過 objc 銷毀類,銷毀的是一個(gè)類不是對象 objc_disposeClassPair(Person); } }
主要流程是:
定義類的方法->objc_allocateClassPair創(chuàng)建類->class_addIvar給類添加成員變量->sel_registerName注冊方法名->class_addMethod給類添加定義的方法->注冊該類->創(chuàng)建類對象->class_getInstanceVariable獲取成員變量,并通過object_setIvar賦值->objc_msgSend調(diào)用方法->釋放對象,銷毀類
Category(類別)
Objective-C借用并擴(kuò)展了Smalltalk實(shí)現(xiàn)中的"分類"概念,用以幫助達(dá)到分解代碼的目的。類別主要特點(diǎn)是不能增加屬性或者成員變量、增加類功能和分離類實(shí)現(xiàn),舉個(gè)例子: 在UIImageView增加了圖片異步加載的功能
@interface UIImageView (ImageViewLoader) <AysncImageDownloaderDelegate> - (void)setOnlineImage:(NSString *)url placeholderImage:(UIImage *)image withRow:(NSNumber *)row; @end @implementation UIImageView (ImageViewLoader) - (void)setOnlineImage:(NSString *)url placeholderImage:(UIImage *)image withRow:(NSNumber *)row; { self.image = image; AsyncImageDownLoader *downloader = [AsyncImageDownLoader sharedImageDownloader]; [downloader startWithUrl:url delegate:self withRow:row]; } @end
Extension(拓展)
拓展也經(jīng)常用到,主要特點(diǎn)是增加ivar、用于接口分離等。例如:ViewController的實(shí)現(xiàn)文件增加@interface ViewController (),支持定義屬性等。
@interface ViewController () @property (nonatomic, copy) block b; @end @implementation ViewController @end
異常處理
OC的異常處理極其類似Java中的,包括4個(gè)指示符,分別是@try、@catch、@throw、@finally??赡艽嬖诋惓5拇a寫在@try塊,異常處理邏輯寫在@catch,@finally塊的代碼總是要執(zhí)行的,@throw作用是拋出異常。
協(xié)議
類似Java中的接口(interface),類似多重繼承功能,支持協(xié)議繼承協(xié)議,通過定義一系列方法,然后由遵從協(xié)議的類實(shí)現(xiàn)這些方法,協(xié)議方法可以用@optional關(guān)鍵字標(biāo)記為可選,@required關(guān)鍵字標(biāo)記為必選,編譯器會出現(xiàn)檢查警告,一般來說還是可以編譯通過。下面看下語法:
@protocol ClickDelegate - (void)click; - (void)unclick; @end
協(xié)議最常應(yīng)用在委托,分為委托方和代理方,委托方負(fù)責(zé)定義協(xié)議、通過id類型持有協(xié)議和調(diào)用協(xié)議的方法,而代理方則遵從協(xié)議、設(shè)置協(xié)議代理對象以及實(shí)現(xiàn)協(xié)議方法即可。
block
類似Java中的Lambda表達(dá)式,比較復(fù)雜,筆者的理解還未達(dá)到一定解說程度,所以這里先不做解釋,放到后續(xù)的文章中介紹。
內(nèi)存管理
Java的內(nèi)存管理是由垃圾回收器負(fù)責(zé),OC中引入自動引用計(jì)數(shù)(ARC),內(nèi)存管理交由編譯器決定。引用計(jì)數(shù)是每個(gè)對象都有一個(gè)計(jì)數(shù)器,如果對象繼續(xù)存活,計(jì)數(shù)器遞增其引用計(jì)數(shù),用完之后遞減引用計(jì)數(shù),如果計(jì)數(shù)變?yōu)?,表示對象可以被釋放了。NSObject協(xié)議聲明了Retain、release和autorelease方法用于操作計(jì)數(shù)器,分別是遞增、遞減、自動釋放操作,所有的對象都是收集器的工作對象。
ARC:自動引用計(jì)數(shù),編譯器自動生成retain/release
MRC:手動管理引用計(jì)數(shù),舊版本使用
autoreleasepool:延遲自動釋放
strong/weak/assgin最佳實(shí)踐
基本類型:assgin;
delegate->week;
集合和block用copy;
其他用strong;
block中的self用weak打破循環(huán)引用。
參考資料
https://www.jianshu.com/p/eb713b1f22dc
https://www.jianshu.com/p/6ebda3cd8052
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtForwarding.html
原文鏈接:https://blog.csdn.net/xiaoming100001/article/details/94337406