字符串 NSString
OC的字符串具有共享性,和恒定性。
<pre><code>
//其實這兩個變量都指向了同一個地址
NSString *str1 = @"Hello World!";
NSString *str4 = @"Hello World!";
</pre></code>為了避免內存浪費,編譯器會做優化,將對象的指針指向同一個地方。但是,這種情況只會在字面量初始化時出現
字符串的初始化:
<pre><code>NSString *str1 =@"Hello World!";//字面量初始化
NSString*str2 = [[NSStringalloc]initWithCString:"Hello
World!" encoding:NSUTF8StringEncoding];
//初始化器初始化
NSString*str3 = [NSStringstringWithCString:"Hello
World!" encoding:NSUTF8StringEncoding];
//工廠初始化
//其中,工廠初始化方法,
是一個類方法,通過類方法內部返回一個新的對象
</pre></code>
字符串基本操作
<pre><code>
[str1 stringByAppendingString: @"Hello World"] //加字符串
[str1 stringByReplacingCharactersInRange: @"Hello World"]//替換字符串
[str1 isEqualToString:str2]// 比較值是否相等
str1 == str2
比較指針是否相等
</pre></code>
<pre><code>
for(int i =0 ;i < [str1 length];i
++){
NSLog(@"%c",[str1 characterAtIndex:i]); //遍歷字符串
}
str1 = str1.uppercaseString;//大寫
str1 = str1.lowercaseString;//小寫
str1 = str1.capitalizedString;//首字母大寫
NSRange range = [str1 rangeOfString:@"Hello"];//查找字符串某處是否包含其它字符,返回location和length。
NSLog(@"location: %lu,length:%lu",range.location,range.length);
NSString* substr = [str1 substringFromIndex:6];//從索引6開始取他的子字符串
BOOLyOrN = [str1 hasPrefix:@"Hello"];//判斷是否有這個字符串前綴
NSString* format=[NSString stringWithFormat:@"[%d,
%d]",100,200];//格式化字符串方法
</pre></code>
<pre><code>[str1 stringByAppendingString:@" Yes!"];
//偽更改,不會去真正的更改str1所指的堆上的值本身,只會返回一個新值,
str1=[str1 stringByAppendingString:@" Yes!"];
//所以要重新給str1
賦值。
</pre></code> 如果在st1
的字符串后面再加一點字符串,這個字符串也不會更改,這是字符串的恒定性,無法改變字符串本身。
還有一個可變字符串 NSMutableString
NSMutableString
具有可變性,它的值是可以被更改的,因此它也不具備共享性。
注意:
它是NSString
的子類
所以當用NSMutableString
定義一個字符串空間mstr1
,
我們可以把mstr1
賦值給NSString
類型的str1
,
因為mstr1
的值是NSMutableString
類型,具有可變性,
所有當用appendString
去更改mstr1
的值時,str1
的值也會跟著改變,
這就違反了NSString
的恒定性。
所以針對這個就要用拷貝屬性[copy str1]
去賦值
<pre><code>
NSMutableString mustr3 =[NSMutableStringstringWithCapacity:100];//手動分配空間
[mustr3 appendString:@"Hello Objective"]; //增加字符串內容
[mustr3 insertString:@"-C" atIndex:mustr3.length];//在指定位置增加內容
[mustr3 setString:@"Hi Objective"];//重新設置字符串
NSRange replaceRange = NSMakeRange(0, 2);//取指定區間的字符串
[mustr3 replaceCharactersInRange:replaceRange withString:@"Hello"];//替換字符串
NSRange deleteRange = NSMakeRange(5, 10);//刪除指定區間的字符串
[mustr3 deleteCharactersInRange:deleteRange];//刪除字符串
</pre></code>
以上這些操作都會直接改變字符串的值
Sizeof(str1)
指的是指針的大小
Str1.length
指的是字符串值的長度
集合類型
數組 Array
OC的數組被定義為一個class,和C的數組不同,
而當訪問越界時,會報錯。
數組的初始化有三種方法:
<pre><code>
NSArray *array1=[NSArray arrayWithObjects:@"Shanghai",@"Beijing",@"New York",@"Paris", nil];
//工廠方法,會返回一個新的值
NSArray *array2=[[NSArray alloc] initWithObjects:@"Shanghai",@"Beijing",@"New York",@"Paris", nil];
//初始化器方法
NSArray *array3=@[@"Shanghai",@"Beijing",@"New York",@"Paris"];
//字面量方法
</pre></code>
在工廠和初始化器最后的nil
表示輸入結束。
array里的元素必須是對象(NSObject的子類)
如果想在array里放值,那么必須用NSNumber封裝成類對象再放入array
NSNumber *numberObject1 =[NSNumber numberWithInteger:number ];
NSInteger
是整數值類型,跟隨CPU架構變換長度
當用字面量初始化時要加@
,用u
結尾。
NSNumber *numberObject2=@300u;//使用字面常量初始
<pre><code>
Point point;
point.h=100;
point.v=200;
NSValue *pointObject= [NSValue value:&point withObjCType:@encode(Point)];
//使用NSValue將struct包裝為對象 再放入數組
</pre></code>
如果想放空值得話,可以用下面的方法
NSNull *nsnull = [NSNull null];
因為每一個數組元素的對象類型可以不一樣,所以在取元素的時候,可能存在類型不安全
數組具有常量性:
數組的 長度 和 元素指針 都不可以被更改(一但這個數組初始化完畢后,長度和元素指針全都不可以更改) 但是元素指針所指向的對象內部可以更改
可變數組 NSMutableArray
NSMutableArray是NSArray的子類
它的初始化方式也和NSArray一樣
因為是可變數組,它可以執行以下操作
[muArray1 addObject:p5];
在數組結尾增加一個元素
[muArray1 removeObjectAtIndex:2];
移除指定索引上的元素
[muArray1 insertObject:p6 atIndex:1];
在指定索引上插入元素
muArray1[0]=p7;
替換指定索引的元素
和可變字符串一樣,可變數組在初始化后,會分配一個緩存容量Capacity
,一般大于實際元素數量,當實際容量大于Capacity
時,Capacity
會以兩倍的方式增長。
最好的方法是在初始化初期就預估分配合理的空間給數組。
intcount=100; NSMutableArray*muArray2=[NSMutableArrayarrayWithCapacity:count];
在實際操作過程中,盡量避免使用removeObjectAtIndex:
和insertObject: atIndex:
因為會改變數組序列,涉及大量的內存拷貝操作,代價太大。
數組的遍歷
<pre><code>
for( BLNPoint* point inarray5)
{
point.x++;
point.y++;
}
//快速遍歷法 for-in 所有遍歷方法中,速度最快,因為它直接訪問內存,優化了索引檢查。
</pre></code>如果不知道數組元素的類型的話,要寫成id*point
或者NSObject* point
<pre><code>NSEnumerator *enumerator = [array5 objectEnumerator];
BLNPoint* item;
while(item = [enumerator nextObject])
{
item.x++;
item.y++;
}
//迭代枚舉法,相比于for-in會慢一點
</pre></code>array5
上面會有一個 objectEnumerator
方法得到一個NSEnumerator
對象類型,那這個對象類型去調用 nextObject
方法 ,再把返回的值賦給 用NSEnumerator
創建的對象 item
,當沒有元素的時候,他的值就是nil
,也就不會再繼續while
循環。
<pre><code>for(inti=0; i<array5.count; i++)
{
NSLog(@"array5[%d],%@",i,array5[i]);
}
for循環遍歷,最慢的方法。
</pre></code>
所有遍歷方法中,推薦使用for-in。
數組查找:
<pre><code>BLNPoint* target=[[BLNPointalloc] initWithX:33WithY:63];
NSUIntegerindex1=[array5 indexOfObject:target];
// 在 array5 中查找是否有和 tearget 值相同的元素,并且返回索引
NSUIntegerindex2=[array5 indexOfObjectIdenticalTo:p3];
// 這個方法只能用數組內的元素名查找,并返回索引
NSLog(@"find at%lu", index1);
NSLog(@"find at
%lu", index2);
</pre></code>
數組排序:
<pre><code>NSArray* sortArray1=[array1 sortedArrayUsingSelector:@selector(compare:)];</pre></code>我們指定了一個方法 compare:
它可以實現數組里元素的比較
因為無法改變原數組,所以要取它的返回值給 sortArray1
它是通過返回一個新數組來表達排序的結果
set集合
NSSet
和數組不一樣,是一個無序集合,存儲的對象不能重復
被定義為class
,引用類型,拷貝時有引用語句
有常量集合NSSet
,可變集合NSMutableSet
,和數組一樣。
初始化方式也和數組類似
<pre><code>NSSet*set1 =
[NSSetsetWithObjects:@"Shanghai",@"Beijing",@"New York",@"Paris", nil];
NSMutableSet*set2 =
[NSMutableSetsetWithObjects:@"Shanghai",@"Beijing",@"New York",@"Paris", nil];
</pre></code>
Capacity
set
容量??
<pre><code>int count=100;
NSMutableSet*muArray2=[NSMutableSetinitWithCapacity:count];
</pre></code>
set
的一下操作
<pre><code>
[set2 addObject:@"London"];//加元素
[set2 removeObject:@"Beijing"];//移除元素
NSLog(@"set2 count:%lu", set2.count);
</pre></code>
<pre><code>
if([set2 containsObject:@"Shanghai"])//判斷是否包含某個對象
</pre></code>
字典
Dictionary 字典 是一個存儲Key-Value
的無序集合
Key
唯一,value
可重復
和Array
、Set
一樣,
有常量字典NSDictionary
和可變字典NSMutableDictionary
。
初始化方式也和數組差不多
字面常量初始化:
value
在 Key
之前,中間用:
隔開<pre><code>
NSDictionary *dictionary1 = @{
@"Shanghai": p1,
@"Beijing": p2,
@"NewYork" : p3,
@"Paris": p4 };
</pre></code>
工廠方法:
Value
在key
之后,中間用" ,
"隔開
<pre><code>
NSMutableDictionary*dictionary2 =
[NSMutableDictionarydictionaryWithObjectsAndKeys:
p1,@"Shanghai",
p2,@"Beijing",
p3,@"New York",
p4,@"Paris",
nil];
</pre></code>
<pre><code>
BLNPoint* result1=[dictionary1 objectForKey:@"Beijing"];
BLNPoint* result2=dictionary1[@"Shanghai"];
</pre></code>可以用這種方式查詢Key
來得到對應的value
。
** tip **:所有的可變集合都是其對應的一個常量集合的子類。
自動引用計數ARC
ARC 是OC的默認內存管理機制,針對堆上的對象,由編譯器自動生成操作ARC指令(retina
或release
)來管理對象的創建和釋放。
retina
和release
在后期的高級編程內容里會深入了解
受ARC管理的有:
- OC的對象指針
- Block 指針
- Attribute((NSObject))定義的typedef
不受ARC管理的有:
- 值類型(如C語言的結構)
- 使用其他方式分配的堆對象(如malloc)
- 非內存資源
實際上,指針本身不受ARC管理,因為ARC管理的是堆上的對象,而指針是存放在棧上的。所有ARC管理的實際上是指針所指向的那個對象。
而值類型的對象也是存放在棧上,所以也不受ARC管理。
引用計數管理
引用計數+1的操作 (retain
操作)
當將對象執行對其他對象的
- 賦值
- 傳參
- 加入集合操作
引用計數-1 (release
操作)
- 全局(局部)變量被賦值為nil 或其他值
- 屬性被賦值為nil或其他值
- 將對象從集合中刪除
自動釋放池 AutoRelease Pool
release
會導致對象立即釋放,如果頻繁的release
,會造成瑣碎的內存管理負擔。這時候,用autorelease
可以將release
的調用推遲到autorelease
被釋放時。
Appkit
和 UIkit
框架在每一次事件迭代時,都會將這個代碼放入autoreleasepool
中。大多數情況都不需要人為干預。
在main
函數內,編譯器都會在開始加上一個autorelease
的塊當程序運行到autorelease
結束時,所有引用了release
的對象都會被立即釋放
<pre><code>
int main(int argc, const char * argv[]) {
@autoreleasepool{
NSLog(@"-------ARC Demo: ----------");
arcDemo();
//當函數內執行完后,會引用release,然后這個釋放信號會暫存在autorelease內
NSLog(@"-------Autorelease Pool Demo: ----------");
poolDemo();
}
//當這個autorelease執行完后,就會被釋放,隨之存在其中的release也會被立即釋放。
return0;
}
</pre></code>每一個事件處理,都是一個autoreleasepool
的建立和釋放
需要手工管理autorelease pool
的情況
- 編寫的程序不基于UI框架(如命令行程序)
- 在循環中創建了大量臨時變量,需要提早釋放,避免臨時對象聚集而導致內存峰值過高。
- 在主線程之外創建了新的線程,需要自己手工添加autorelease pool 塊
- 可以嵌套使用
<pre><code>
void poolDemo(){
@autoreleasepool{
for (int i = 0; i < 10; i++) {
__unused BLNRectangle *rect = [[BLNRectangle alloc]init];
}
}
}
</pre></code>
__unused
修飾符,通知編譯器,如果這個變量未被使用就不參與編譯(消除黃色警告團)
協議protocol
協議是一種約定,它只提供外部描述,不提供具體實現,所以只在.h文件內寫,不用去寫.m文件
協議里可以放
- 屬性
- 實例方法
- 類方法
- 初始化器和析構器(一般不放)
但是不能放實例變量。編譯器會自動生成setter和getter方法,但是不會合成實例變量。
定義協議
<pre><code>@protocol Drawable
//內容
@end
</pre></code>
使用協議
<pre><code>
@interface BLNPoint : NSObject<Drawable>//把協議名放中括號里
//內容
@end
</pre></code>
** 必選協議required **
** 可選協議optional **
協議里的成員默認都是@required
的,當一個類遵守協議時,必須要實現協議內的所有成員。.h
文件里不用去再次聲明協議內的方法,只需要在.m
文件內實現就可以了。
** 但是屬性必須再次聲明 **。如果不聲明的話,就沒法自動合成實例變量。
如果要自己寫一個@required的話,那個在這個@required后面的成員就是必選成員了
與之相對的@optional表示的是可選協議。他之后的成員會被認為成可選協議
如果遵守協議但是沒有去實現協議方法,會出現警告運行時就會報錯。
協議類型變量被賦值非協議類型對象時,會出現警告
比如這樣賦值就會報錯process1(@"x");
,process1
是協議類型的函數
協議可以作為變量聲明類型,但是不能創建實例void process1(id<Drawable> obj)
有時不確定這個對象是否遵守了協議,那么可以用這段代碼來驗證這個對象背后的類型是否遵守了協議。
<pre><code>
void process2(id obj){
if([obj conformsToProtocol:@protocol(AProtocol) ]) {
[obj methodA];
}
</pre></code>
一個協議也可以繼承多個協議。
實現子協議的類型,也必須實現父類協議的成員。
也可以遵守多個協議
<pre><code>
@interface ClassC : NSObject<AProtocol,CProtocol>
@end
</pre></code>
常用的協議
NSObject
:包含對象的常用操作,相等、字符串表示、哈希。
NSCopying
:支持復制的類型必須遵守該協議。
NSMutableCopying
:在NSCopying的基礎上,支持復制數據的可變性。
NSFastEnumeration
:實現快速美劇for-in的類型采用該協議。
NSCoding
:支持將對象圖進行編碼/解碼以支持對象序列化
類別與擴展
類別category
類別就不知道源代碼的情況下,想給原有的類再添加了一些成員,并去實現他們
定義類
@interface BLNPoint : NSObject
定義類別
@interface BLNPoint(Drawing)
** tip **:category
的實現文件不寫在class
的實現文件內,而是寫在category
的實現文件內
命名規范:
文件名 == class
名+category
名
category
可以添加的成員
- 類方法
- 實例方法
- 重寫基類方法
category
不能添加的成員
- 屬性
- 實例變量
- 已存在的同名方法(已經被定義過的)
加了屬性的話,編譯能通過,但是在運行的時候就會報錯(坑爹啊這是)
不能加屬性,實際上是因為不能加屬性背后的那個實例變量。
雖然不能加屬性,但是可以去定義一個setter
和一個getter
訪問器方法。
-(void)setWeight:(NSInteger)weight;
-(NSInteger)weight;
調用class
內的實例變量
適用場景:
- 在沒有源代碼的情況下,向已經封裝的類里添加方法
- 在一些特殊場合下
- 對復雜的大型文件分割實現
都一下位置可以添加category
- 自己創建的
- 系統的
- 第三方庫
擴展extension
和category
最大的區別在于,這是在有class
的源代碼的情況下在class
內添加,可以看做是一個沒有名字的class
,不用定義在 .h
文件,直接在需要擴展的.m
文件內定義.
<pre><code>
@interface Circle ()//加個小括號,寫在.m 文件里
{
NSString* _name;
}
@property (readwrite )NSInteger radius;//修改讀寫屬性
@property
NSIntegercenter;//添加屬性
-(float)getDiameter;//實例方法
+(void)process:(Circle*) circle;//類方法
@end
</pre></code>
如果在主接口定義了文件的讀寫屬性,那么在擴展內可以更改讀寫屬性,
有一點需要注意的是,只能往更高的權限更高,** 不能把權限改低。 **
注意:
最重要的一點區別,擴展的成員只針對類(那個 .m
文件)內可以訪問,在類外不能訪問擴展!
擴展的主要用途就是信息隱藏,可以把一些外部無需訪問,但是類內又要用到的成員私有化。
類的主接口用于 ** 對外公開訪問 **
類的擴展用于 ** 對內訪問 **