title: OC_Note date: 2014-10-21 21:16:49tags: OC-NOTESimageNSLog 與 printf的不同1. NSLog接收oc字符串作為參數,printf接收c語言的字符串作為參數2. NSLog輸出后會自動換行,printf在輸出后不會自動換行3. 使用NSLog時,需要包含頭文件#import;而使用printf時,需要包含頭文件#include4. NSLog可以輸出日期,時間戳,進程號等信息,而printf不能輸出這些信息
常用術語
面向過程 : Procedure Oriented
面向對象 : Object Oriented ,簡稱OO
面向編程 : Object Oriented Programming,簡稱OOP
OC 語法
類的聲明
@interface 類名:父類(默認為 NSObject 該類時根類)
{
定義成員變量;(默認情況下,成員變量為protect類型,)
}
( 可以用@public將其定義為public類型,此時定義的屬性或變量允許被外界訪問)
@property(nonatomic/atomic,readonly/readwrite,assign/strong/retain/wake)類型 屬性名;(定義屬性可以自動生成setter和getter方法)
聲明類方法;(標識 + 該方法向類發消息)
聲明實例方法; (標識 - 該方法向對象發消息) 聲明類方法時 返回值最好定義為`instancetype`類型
@end
類的實現
@implemention 類名
- 實例方法
{
實現代碼;
}
+ 類fangfa
{
實現代碼;
}
重寫父類方法;
如:descripition、 dealloc、init等
@end
3.創建對象
1.包含所創建類的頭文件
2.類名 *對象名;
2.1 類名 *對象名 = [[類名 alloc]init];(創建對象并為其分配內存、初始化)
2.2 類名 *對象名 = [類名 new];(每次都會創建出新的對象,并且返回對象的地址。)
3. 給對象賦值;(可以調用相應的類方法);
4. 給對象發消息,完成所需要進行的操作;[對象 方法];
匿名對象
方法調用:
[[類名 new]方法];/[[[類名 alloc]init]方法];
屬性訪問:
[類名 new]->屬性 = 賦值;(外界訪問定義為public類型的屬性)
OC方法和函數的區別
1. OC方法的聲明只能在@interface 和 @end之間,只能在@implementation 和 @end之間。即OC方法獨立于類存在。
2. C函數不屬于類,跟類沒有聯系,C函數所有權只屬于定義函數的文件
3. C函數不能訪問OC對象的成員變量。
OC語法細節
1. 成員變量不能在{}中進行初始化、不能直接拿去訪問
2. 方法的聲明不能寫在@end之后
3. 方法不能當作函數一樣調用
4. 成員變量、方法不能用static等關鍵字進行修飾
OC方法注意點
方法只有聲明,沒有實現(經典錯誤,系統提示警告)
方法沒有聲明,只有實現(編譯器警告,但是可以調用,OC的弱語法)
編譯的時候,訪問沒有定義的成員變量直接報錯,調用沒有的方法,只是警告
沒有@interface,紙偶@implemenation也可以成功定義一個類
@implemenation Dog : NSObject
{
int _age;
NSString *_name;
}
- (void)print
{
NSLog(@"The dog's name is %@,ang de it's %d years old!",_name,_age);
}
@end
@implemenation 中不能聲明和@interface一樣的成員變量
!!! OC中有BOOL基本數據類型,其值是YES和NO,而不是true 和false,它實際上是一種對帶符號的自負類型(signed char)的定義(typedef),它使用8為存儲空間。YES定義為1,NO定義為0;
image
類方法he實例方法
類方法
* 該方法是直接可以用類名來執行的方法(類本身會在內存中占據存儲空間,里面有類/對象方法列表)
* 以加號 + 開頭
* 只能用類名調用,對象不能調用
* 類方法不能訪問實例變量(成員變量)
* 使用場合:當不需要訪問成員變量時,盡量使用類方法
* 類方法和對象方法可以同名
實例方法
* 該方法是用對象名來執行的方法
* 以減號 — 開頭
* 只能用對象名調用,類不能調用,該方法沒有對象是不能被執行的
* 對象方法能訪問實例變量(成員變量)
setter和getter方法
setter和getter方法的使用場合
@public的成員可以隨意被賦植,應該使用set和get方法來管理成員變量的訪問
setter方法
作用:用來設置成員變量,可以在方法里面過濾一些不合理的值
命名規范:方法以set開頭,而且后面跟上成員變量名,成員變量名必須首字母大寫,盡量形參名稱不要與成員變量名重名(成員變量名最好以下劃線 _ 開頭)
返回值:一般為void
getter方法
作用:返回對象內部的成員變量
命名規范:方法名和成員變量名同名
返回值:一般與成員變量的類型相同
self關鍵字
成員變量和局部變量同名
當成員變量和局部變量同名時,采取就近原則,訪問的是局部變量
當self訪問成員變量,區分同名的局部變量
使用細節
1. 出現的地方:所有的OC方法中(對象方法/類方法),不能出現在函數
2. 作用:
使用“self.屬性”訪問當前方法中的成員變量
使用“self-> 成員變量”訪問當前方法中public成員變量
使用“[self 方法]”調用方法(類方法/實例方法)
3. 類方法中self只能調用類方法,實例方法中self只能調用實例方法
繼承
繼承的概念
1. is-a機制
2. 即當創建的多個類有共同的屬性和行為時,可以抽出一個類作為父類,在父類中定義相同的屬性,聲明實現相同的行為(方法);
3. 子類可以使用父類的所有屬性和方法,并且子類可以在父類的基礎上拓補自己的屬性和方法,包括重寫父類方法。重寫父類方法時,子類對象會優先調用子類重寫后的方法。
4. 子類屬性和方法訪問的過程: 如果子類沒有相應的方法或屬性,則去訪問父類,一次遞進知道找到NSObject根類,如果仍然沒有找到相對應的方法和屬性,則報錯。
繼承的專業術語
父類/超類 superclass
子類 subclass/subclasses
繼承的細節
單繼承,不支持多繼承
子類和父類不能有相同的成員變量
子類可以重寫父類中聲明的方法(在代碼運行時,oc確保調用相應類的重寫方的實現)
繼承的優缺點
優點:
在不改變原來模型的基礎上,拓充方法
建立了類與類的聯系
抽取了公共代碼
缺點:
耦合性強
super關鍵字
super既不是參數,也不是實例變量,而是oc編譯器提供的功能
用于提供一種在子類中顯示調用父類的方法
繼承的局限性
父類不能訪問子類屬性、調用子類方法
不能繼承累簇(如 NSString累簇)
多態
多態的基本概念
某一類事物的多種形態
OC對象具有多態性
多態的體現
子類對象可以賦值給父類指針;
如:Father *f = [children new];
父類指針可以訪問對應的屬性和方法
如: f.age = 23;
[f study];
多態的好處
用父類接收參數,節省代碼
多態的局限性
不能訪問子類的屬性(可以考慮強制轉換)
多態的細節
動態綁定,在運行時根據對象的類型確定動態調用的方法
復合
復合包括組合和聚合
has-a機制
組合和聚合表示將各個部分組合在一起,用于表達整體與部分的關系。在面向對象的編程思想里,就是用已有類的對象封裝新的類。
組合 :表示一種強的、嚴格的整體與部分的關系,部分和整體的生命周期一樣。 比如:人和人頭
聚合 :表示一種弱的整體與部分的關系,比如: 汽車和輪胎
Foundition框架
1. OC集合只能存儲OC對象,不能存儲c語言中的基本數據類型,如int,float,enum,struct,且不能在集合中存儲nil。
字符串
不可變字符串 : NSString *string1;
可變字符串 : NSMutableString *string2;
對可變字符串的操作:
2.1 增加元素
string2 appendFormat:@"hello"];
2.2 刪除元素
[string2 replaceCharacterInRange:NAMakeRange(2,3)];
2.3 修改元素(替換)
[string2 replaceCharacterInRange:NAMakeRange(2,3)withString:@"word"];
字符串的操作
3.1 比較
//判斷兩個字符串是否相等,返回的是BOOL值
[str1 isEqualTo:str2];
NSCompareResult res = [str1 copmare:str2];
不區分大小寫的比較:caseInsensitiveCompare
有選擇參數的比較: [str compare:str2 option:NSStringCompareOption]
NSStringCompareOption選項可以傳入的參數
enum {
NSCaseInsensitiveSearch = 1,? 不區分大小寫
NSLiteralSearch = 2,? ? ? ? ? 對于相等的字符串逐個比較
NSBackwardsSearch = 4,? ? ? 從后向前比較
NSAnchoredSearch = 8,? ? ? ? 限制比較從開始還是結尾
NSNumericSearch = 64,? ? ? ? 對于數字按數字比較
NSDiacriticInsensitiveSearch = 128,? 不區分音節
NSWidthInsensitiveSearch = 256,? ? ? 忽略full-width half-width (如 Unicode code point U+FF41 和 Unicode code point U+0061 的字母 “a”is equal)
NSForcedOrderingSearch = 512,? ? ? ? ? 對于不區分大小寫比較相等的字符串,強制返回NSOderedAscending or NSOrderedDeascending (如“aa” is grater than “AA”)
NSRegularExpressionSearch = 1024? ? ? ? treated as an ICU-compatible regular expression
};
NSCompareResult 有三種值:
NSOrderedSame? 兩字符串相等
NSOrderedAscending? str1 < str2
NSOrderedDeascending? str1 > str2;
3.2 求長度
NSUInteger strlen = [str1 length];
3.3 大小寫轉換
str2 = [str1 uupercaseString];
str2 = [str1 lowercaseString];
3.4 獲取文件前綴、后綴
str2 =[str1 hasPrefix:@"word"];
str2 = [str1 hasSuffix:@"txt"];
3.5 獲取子串
//獲取str2在str1中的位置,即range.location and range.length
range = [str1 rangOfString:str3];
range = [str1 rangOfString:@"hello"];
//獲取str1 第6個位置之后的字符串賦值給str2
str2 = [str1 substringFromIndex:6];
//獲取str1 從開始到第6個位置之間的字符串賦值給str2
str2 = [str1 substringTOIndex:6];
//獲取str1 從第6個位置開始長度為5的字符串賦值給str2
str2 = [str1 substringWithRange:NSMakeRange(6,7)];
3.6 文件路徑的轉換
//間文件路徑字符串str1 = @“~/test.html”的路徑轉換為絕對路徑賦值給str2
str2 = [str1 stringByExpandingTildeInPath];
//間文件路徑字符串str1 = @“/users/qingyun/test.html”的路徑轉換為相對路徑賦值給str2
str2 = [str1 stringByAbbreviatingWithTildeInPath];
3.7 文件路徑的擴展名
str2 = [str1 pathExtension]; 此時 str2 = @“html”;
3.7 刪除文件路徑的后綴
str2 = [str1 stringByDeletePathExtension]; 此時 str2 = @“~/test”;
3.9 追加字符串
str1 = @"hello word";
str2 = [str1 stringByAppendingFormat:@"wellcom"]; 此時 str2 = @“hello word wellcom”;
數組
不可變數組 : NSArray *array1;
可變數組 : NSMutableArray *array2;
數組初始化:
NSArray *array1 = [NSArray arrayWithObjects:@"hello",@"word",@"two",nil];
NSArray *array1 = @[@12,@34,@"hello",@"error"];
//創建空數組
NSMutableArray *array2 = [NSMutableArray array];
//用已有的數組創建新數組
NSMutableArray *array2 = [NSMutableArray arrayWithArray:array];
//創建一個數組,并預分配內存
NSMutableArray *array2 = [NSMutableArray arrayWithCapacity:40];
對可變數組的操作:
2.1 增加元素
[array2 addObjects:@"hello"];
2.2 刪除元素
//刪除下標為2的對象
[array2 removeObjectsAtIndex:2];
//刪除一定范圍內的所有@“hello”
[array2 removeObject:@"hello" inRange:NSMakeRange(2, 3)];
[array3 removeObjectIdenticalTo:@"is" inRange:NSMakeRange(1, 5)];
//刪除該數組內的所有@“hello”
[array2 removeObjectIdenticalTo:@"hello"];
2.3 修改元素(替換)
[array2 removeObjectsAtIndex:2 withObject:@"dog"];
[array2 removeObjectsAtIndex:2 withObject:str];
[array2 removeObjectsInRange:NSMakeRang(0,2) withObjectFromArray:array];
2.4 插入元素
[array2 insertObjects:str1 AtIndex:2];
2.5 訪問數組某個對象
array2[下標]
字典
不可變數組 : NSDictionary *dictionary1;
可變數組 : NSMutableDictionary *dictionary2;
字典初始化:
Dictionary *dictionary1 = [NSDictionary dictionaryWithObjectsAndKeys:str1,@"hello",str2,@"word",str3,@"two",nil];
Dictionary *dictionary1 = @{@"num1":@12,@"num2":@34,@"str1":@"hello",@"str2":@"error"};
//創建空字典
NSMutableDictionary *dictionary2 = [NSMutableDictionary dictionary];
//用已有的字典創建新字典
NSMutableDictionary *dictionary2 = [NSMutableDictionary dictionaryWithDictionary:array];
//創建一個字典,并預分配內存
NSMutableDictionary *dictionary2 = [NSMutableDictionary dictionaryWithCapacity:40];
對可變字典的操作:
2.1 增加元素
[dictionary2 addObjects:(id)forKey:@"key"];
2.2 刪除元素
[dictionary2 removeObjectForKey:@"key"];
2.3 修改元素(替換)
[dictionary2 setObject:(id)forKey:@"key"];
2.4 訪問字典
dictionary2[@“key”];
[dictionary2 objectForKey:@"key"]
裝箱-開箱
對基本數據類型的裝箱-NSNumber
//裝箱方法1
NSNumber *number = [NSNumber numberWithChar:'X'];
NSNumber *number = [NSNumber numberWithINT:23];
NSNumber *number = [NSNumber numberWithBOOL:YES];
NSNumber *number = [NSNumber numberWithDouble:34.5];
//裝箱方法2
@23,@34.5
//開箱
[number charValue];
[number intValue];
[number BOOLValue];
[number DoubleValue];
對所有非對象類型的裝箱(包括基本數據類型)- NSValue
//對NSRect,NSPoint,NSRange裝箱,也可以對基本數據類型進行裝箱
NSRect rect = NSMakeRect(10,20,30,40);
NSValue *value = [NSvalue valueWithBytes:&rect objCType:@encode(NSRect)];
int a = 5;
NSValue *value = [NSvalue valueWithBytes:&a objCType:@encode(int)];
//開箱
NSRect rect2 = {0};
[value getValue:&rect];
int b = 0;
[value getValue:&b];
//僅對NSRect,NSPoint,NSRange裝箱
NSValue *value = [NSValue valueWithRect];
NSValue *value = [NSValue valueWithRange];
NSValue *value = [NSValue valueWithPoint];
//開箱
NSRect rect2 = {0};
[value rectValue];
NSRange range = {0};
[value rangeValue];
NSPoint point = {0};
[value pointValue];
枚舉
//normal enumerator
NSEnumerator *enumer = [array objectEnumerator];
id obj;
while (obj = [enumer nextObject]) {
NSLog(@"%@",obj);
}
NSEnumerator *enumer2 = [array reverseObjectEnumerator];
while (obj = [enumer2 nextObject]) {
NSLog(@"%@",obj);
}
//fast enumerator
for (id obj2 in array) {
NSLog(@"%@",obj2);
}
類別
類別的簡述
為現有的類(自定義的類、第三方的類或者是系統定義的類)添加一些新的行為;
類別可以解決繼承不能為累簇添加新方法的問題。
類別的聲明和實現
格式: 類名 + 類別名
如:為NSString 創建一個類別 NSString+NumberConvenience;
只要保證類別名稱唯一,可以向一個類中添加任意數量的類別。
聲明:
@interface NSString (NumberConvenience)
- (NSNumber *)lengthAsNumber;
@end
實現:
@implementation NSString (NumberConvenience)
- (NSNumber *)lengthAsNumber {
}
@end
類別的優缺點
缺點:
* 只能添加方法,只可以訪問原始類的實例變量,無法向類別中添加新的實例變量
* 名稱沖突。類別具有最高優先級,即當類別中定義與對應類中已有的方法同名的方法,對象調用該方法時,會優先調用類別中定義的方法。
* 多個Category中如果實現了相同的方法,只有最后一個參與編譯的才會有效
優點:
* 將類的實現代碼分散到多個不同文件或框架中。
* 可以創建對類中私有方法的前向引用,
* 向對象添加非正式協議。
使用類別實現類的擴展
類的擴展等同于在類聲明的源代碼中聲明一個無名的(即括號“ () ”里面為空)類別,并實現;
類的擴展可以在源代碼中使用
可以添加實例變量作為類的私有變量和方法
可以將只讀權限改為讀寫權限
創建數量不限
利用類別分散實現代碼的優點
. 在大型的項目中, 一個類的實現可能非常大,并且.m文件不能分離。但是使用類別可以將一個類的實現分散且有規律的組織在不同的文件中。還可以將一個類的實現分散到不同的框架中。
. 編程人員可以更加容易閱讀代碼并實現多人合作編碼
. 版本管理降低沖突
. 維護人員更容易理解代碼
非常正式協議
非正式協議就是為NSObject類創建一個類別;
響應選擇器
使用@selector()編譯指令來指定選擇器,圓括號里是具體的方法名。如:
@selector(setEngine:)
@selector(setTire:atIndex)
選擇器的類型關鍵字:SEL
(BOOL)respondsToSelector:(SEL)@Selector; 使用此方法可以判斷某一對象是否可以執行指定的方法。
QYStudent *student = [[QYStudent alloc]init];
如: [student respondToSelector:(SEL)@selector(study)];
協議
基本用途
可以用來聲明一大堆方法(不能聲明成員變量)
只要某個類遵守了這個協議,就相當于擁有這個協議中的所有方法聲明
只要父類遵守了某個協議,就相當于子類也遵守了
格式 @protocol 協議名
方法聲明列表
@end
某個類遵守某個協議
@interface 類名 : 父類名<協議名>
@end
關鍵字
協議中有2個關鍵字可以控制方法是否要實現(默認是@required),在大多數情況下,用途在于程序員之間的交流
@required: 該關鍵字以下且@optional關鍵字以上的方法必須要實現(若不實現,編譯器會發出警告
@optional: 該關鍵字以下且@required關鍵字以上的方法可以選擇性的實現
協議遵守協議
一個協議可以遵守其他多個協議,多個協議之間用逗號 , 隔開
一個協議遵守了其他協議,就相當于擁有了其他協議中的方法聲明
@protocol 協議名稱 <協議1, 協議2>
@end
基協議
NSObject是一個基類,最根本最基本的類,任何其他類最終都要繼承它
其實還有一個協議,名字也叫NSObject,它是一個基協議,最根本最基本的協議
NSObject協議中聲明很多最基本的方法,比如description、retain、release等
建議每個新的協議都要遵守NSObject協議
定義變量時指定協議
// NSObject類型的對象,并且要遵守NSCopying協議
NSObject *obj;
// 任何OC對象,并且要遵守NSCoding協議
id obj2;
內存管理
內存管理機制:引用計數
引用計數的計算
alloc 、new 、copy(copy生成接收對象的一個副本) //使用這三個方法創建對象時,對象的引用計數器為1
(id) retain; //給對象發送retain消息后,對象的引用計數器加1
(void) release; //給對像發送release消息后,對象的引用計數器減1
-(void)dealloc; //當一個對象的引用計數器變為0而即將被銷毀時,Objective-C自動向對 象發送一條dealloc消息,我們通常都會在自己的對象中重寫dealloc方法
(unsigned) retainCount;//獲取當前對象的引用計數器的值
非ARC環境下內存的管理
當某個對象被持有有,[對象名 retain]; 當某個對象不再被持有時,[對象名 release];
ARC環境下內存的管理
規則
只要還有一個強指針變量指向對象,對象就會保持在內存中
強引用,弱引用
? 默認所有實例變量和局部變量都是Strong指針
? 弱指針指向的對象被回收后,弱指針會自動變為nil指針,不會引發野指針錯誤。其修飾符號為__weak;
注意點
? 不能調用release、retain、autorelease、retainCount
? 可以重寫dealloc,但是不能調用[super dealloc]
? @property : 想長期擁有某個對象,應該用strong,其他對象用weak
? 其他基本數據類型依然用assign
? 兩端互相引用時,一端用strong、一端用weak
自動釋放池
自動釋放池是一個存放實體的集合,這些實體可能是對象,這些對象能夠被自動釋放。
/ - (id) autorelease; //是NSObject類提供的方法,此方法在某一個預定的時候,向對象發送release消息,返回值是接收消息的對象。實際上當給一個對象發送autorelease消息的時候,就是將這個對象添加到的自動釋放池(NSAutoreleasePool)中,當自動釋放池銷毀時,會向該池中的所有對象發送release消息。
如: - (NSString ) description
{
NSString desc;
desc = [[NSString alloc] initWithFormat: @” I am %d years old”,29];
return ([desc autorelease]);
}
內存管理規則
如果我使用了new , alloc 或者copy方法獲得一個對象,則我必須釋放或自釋放該對象。
如果你對對象調用了retain消息,那么你必須負責釋放(release)這個對象,保證retain和release的使用次數相等。
拷貝
淺拷貝(shallow copy)
不會復制所引用的對象,新復制的對象只會指向現有的引用對象上。(引用計數加 1 ,地址不變)
深拷貝(deep copy)
真正意義的復制概念。得到的結果是多個,而非只是對象的引用。(引用計數 不變 ,地址發生變化)
關鍵字
copy:對不可變的集合copy為淺拷貝,對可變的集合copy為深拷貝
mutableCopy:對可變的或不可變的集合mutableCopy都是深拷貝,但是對于集合內部對象的拷貝時淺拷貝。
BLOCK
基本概念
代碼塊本質上是和其他變量類似。不同的是,代碼塊存儲的數據是一個函數體。使用代碼塊時,可以像調用其他標準函數一樣,傳入參數數,并得到返回值。
Block封裝了一段代碼,可以在任何時候執行
Block可以作為函數參數或者函數的返回值,而其本身又可以帶輸入參數或返回值。
蘋果官方建議盡量多用block。在多線程、異步任務、集合遍歷、集合排序、動畫轉場用的很多
定義
int (^MySum)(int, int) = ^(int a, int b) {
return a+b;
};
定義了一個叫MySum的blocks對象,它帶有兩個int參數,返回int。等式右邊就是blocks的具體實現
對變量的訪問權限
對全局變量具有讀寫權限
對靜態變量具有讀寫權限
對局部變量只有訪問權限(可以用__block修飾局部變量,這樣可以對其進行修改)
與函數指針的對比
定義函數指針
int (*p)(int,int);
定義Blocks
int (^Blocks)(int,int);
調用函數指針
(*p)(10, 20);
調用Blocks
Blocks(10, 20);
typedef和賦值
在聲明的同時定義變量,然后賦值
int (^MySum)(int,int) = ^(int a,int b) {
return a + b;
};
也可先用typedef先聲明類型,再定義變量進行賦值
typedef int (^MySum)(int,int);
MySum sum = ^(int a,int b) {
return a + b;
};
KVC(Key Valuble Coding)
基本概念
是一種間接更改對象狀態(或者說是屬性值)的方式:key-value coding 簡稱KVC.
主要本質特點是采用字符串來標識對象的屬性變量,并可以利用這個標識來更改對象的狀態(或者說是屬性值)
基本用法
(id)valueForKey:(NSString *)key //以key作為標識符,獲取其對應的屬性值
(void)setValue:(id)value forKey:(NSString *)key //以key作為標識符設置其對應的屬性值。
調用機制
valueForKey:會首先查找以參數名命名(格式為-key或者isKey)的getter方法,如果找到的話則調用這個方法;如果沒有找到這樣的getter方法,它將會在對象內部尋找名稱格式為_key或者key的實例變量,然后返回。
setValue:forKey:的機制跟valueForKey相似。它首先查找參數名命名的setter方法,如果找到的話則完成設置;如果沒有找到setter方法, 則直接在類中找到名稱格式為_key或者key的實例變量, 然后將value賦值給它。
鍵路徑
鍵路徑的概念和表示:可以在對象和不同的變量名稱之間用圓點分開來表示。
-(id)valueForKeyPath:(NSString *)keyPath //以keyPath作為標識符,獲取其對應的屬性值
-(void)setValue:(id)value forKeyPath:(NSString *)keyPath //以keyPath為標識符,設置其對應的屬性的值。
謂詞