GeekBand 第二周

字符串 NSString

OC的字符串具有共享性,和恒定性。

NSString共享機制

<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>


NSString恒定機制

<pre><code>[str1 stringByAppendingString:@" Yes!"];

//偽更改,不會去真正的更改str1所指的堆上的值本身,只會返回一個新值,

str1=[str1 stringByAppendingString:@" Yes!"];

//所以要重新給str1賦值。

</pre></code> 如果在st1的字符串后面再加一點字符串,這個字符串也不會更改,這是字符串的恒定性,無法改變字符串本身。

還有一個可變字符串 NSMutableString

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可重復
ArraySet 一樣,
有常量字典NSDictionary 和可變字典NSMutableDictionary。

初始化方式也和數組差不多

字面常量初始化:
valueKey之前,中間用:隔開<pre><code>
NSDictionary *dictionary1 = @{

                            @"Shanghai": p1,             
                            @"Beijing": p2,               
                            @"NewYork" : p3,         
                            @"Paris": p4 };

</pre></code>

工廠方法:
Valuekey之后,中間用" ,"隔開
<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指令(retinarelease)來管理對象的創建和釋放。
retinarelease在后期的高級編程內容里會深入了解

受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被釋放時。

AppkitUIkit框架在每一次事件迭代時,都會將這個代碼放入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的情況

  1. 編寫的程序不基于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文件

協議里可以放

  1. 屬性
  • 實例方法
  • 類方法
  • 初始化器和析構器(一般不放)

但是不能放實例變量。編譯器會自動生成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可以添加的成員

  1. 類方法
  • 實例方法
  • 重寫基類方法

category不能添加的成員

  1. 屬性
  2. 實例變量
  3. 已存在的同名方法(已經被定義過的)

加了屬性的話,編譯能通過,但是在運行的時候就會報錯(坑爹啊這是)
不能加屬性,實際上是因為不能加屬性背后的那個實例變量。
雖然不能加屬性,但是可以去定義一個setter和一個getter訪問器方法。
-(void)setWeight:(NSInteger)weight;
-(NSInteger)weight;
調用class內的實例變量

適用場景:
  1. 在沒有源代碼的情況下,向已經封裝的類里添加方法
  2. 在一些特殊場合下
  • 對復雜的大型文件分割實現

都一下位置可以添加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文件)內可以訪問,在類外不能訪問擴展!

擴展的主要用途就是信息隱藏,可以把一些外部無需訪問,但是類內又要用到的成員私有化。

類的主接口用于 ** 對外公開訪問 **
類的擴展用于 ** 對內訪問 **

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • @字符串處理 NSString是一個Unicode編碼,16位的字符序列,是類,引用類型。 初始化方法有字面常量初...
    5君閱讀 711評論 0 51
  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結起來就是把...
    Dove_iOS閱讀 27,217評論 30 472
  • 37.cocoa內存管理規則 1)當你使用new,alloc或copy方法創建一個對象時,該對象的保留計數器值為1...
    如風家的秘密閱讀 893評論 0 4
  • 多線程、特別是NSOperation 和 GCD 的內部原理。運行時機制的原理和運用場景。SDWebImage的原...
    LZM輪回閱讀 2,043評論 0 12
  • iOS面試小貼士 ———————————————回答好下面的足夠了------------------------...
    不言不愛閱讀 2,014評論 0 7