GeekBand Objective-C編程語言學習筆記(第二周)

第八天視頻課程:

認識字符串 NSString

NSString是一個Unicode編碼、16位字符的字符序列。

NSString被定義為類,引用類型,拷貝是具有引用語法。

初始化方法:

NSString *str1 = @"Hello World!"; \\字面量初始化,必須加@符號表示支持NSString是OC的字符串。

NSString *str2 =[[NSString alloc]initWithCString:"Hello World!" encoding:NSUTF8StringEncoding]; \\初始化器,分配內存,傳遞C語言的字符串,進行編碼。

NSString *str3 =[NSString stringWithCString:"Hello World!" encoding:NSUTF8StringEncoding];工廠方法,工廠方法stringWithCString是一個類方法,返回了一個類對象。

NSString擁有恒定性(初始化之后值就不可以改變),所有的操作無法更改字符串本身,如有更改,都是返回新值的形式。

NSString擁有共享機制(由于NSString擁有恒定性,所以用字面量初始化方法(初始化器和工廠方法是動態創建方法這里沒有共享機制效果)初始化的兩個字符串的內同完全相同的話,這兩個字符串將指向同一塊堆空間,即使用同一塊內存空間),引用技術管理對其有特殊的管理規則。

偽更改:返回新字符串

str1 = [str1 stringByAppendString:@''Yes!"]; //這句語句會申請一段新的內存,復制在原來str1字符串的內容,再在后面添加Yes!這幾個字符,返回新的內存地址。原來的str1 地址指向的內存內容不變。

字符串之間判斷相等:

如果要判斷兩個字符串的值是否相等要使用isEqualToString方法: [str1isEqualToString: str2] //比較str1和str2里面的值是否相等。如比較兩個字符串指向的內存是否相等(指針是否相等)就用:str1 == str2來判斷這兩個字符串是否指向了同一塊內存。

認識NSMutableString

NSMutableString具有可變性,NSString具有恒定性。

NSMutableString為NSString的子類

由于NSMutableString是NSString的子類可以吧一個NSMutableString類對象賦值給一個NSString類對象:

NSString *str5 = mustr1;

[mustr1 appendString:@"Very Good!"];

執行完上述操作將發現,NSString類對象str5也跟隨NSMutableString類對象mustr1發生了改變,這樣就違背了NSString的恒定性,這是一個類似漏洞的東西,以后要將NSMutableString類中的內容復制到NSString不可以直接賦值,而是要用stringCopy方法。

NSMutableString不具有共享機制,NSString具有共享機制。

NSMutableString并不是在原有內存上直接增長,而是重新分配一個更大或者更小的緩存容量存放字符

NSMutableString字符串初始化后,會分配一個緩存容量(capacity),其長度一般大于實際的字符串數量。當字符串增長時,如實際需求大于capacity,其capacity會以二倍的方式指數增長,伴隨的代價:

分配新的堆內存 2*capacity

將原來堆內存上的內容拷貝到新內存

釋放原來堆內存

最佳實踐:估計好capacity,預先分配好一定容量,避免以后capacity的增長。

NSMutableString *mustr3 = [NSMutableString stringWithCapacity:100]; //在用工廠方法申請NSMutableString類對象時預留100個字符的緩存容量(capacity)。

字符串的長用方法

NSString:

訪問字符串:

獲取字符串字符:[str1 characterAtIndex:i];

字符串長度:[str1 length];

字面量:

大小寫轉換 :str1 = str1.uppercaseString;

str1 = str1.lowercaseString;

str1 = str1.capitalizedString;

查詢字符串:

定位子串:NSRange range = [str1 rangOfString:@"Hello"];

NSLog(@"location:%lu, length:%lu, range.location, rang.length);

獲取子串:NSString *substr = [str1 substringFromIndex:6];

是否包含子串:BOOL yOrN = [str1 hasPrefix:@"Hello"];

查詢字符集:rangOfCharacterFromSet:

其他操作:

比較字符串:isEqualToString:

替換字符串:stringByReplacingOccurrencesOfString: withString

分解字符串:componentsSeparatedByCharactersInSet:

NSMutableString:

添加字符串:[mustr3 appendString:@"Hello Objective"];

刪除字符串:[mustr3 insertString:@"-C" atIndex:mustr3.length];

修改字符串:[mustr3 setString:@"Hi Objective"];

NSRange replaceRange = NSMakeRange(0, 2);

[mustr3 relpaceCharactersInRange:replaceRange withString:@"Hello"];

NSRange deleteRange = NSMakeRange(5, 10);

[mustr3 deleteCharactersInRange:deleteRange];

第九天視頻課程:

認識數組

數組是一個有序的元素序列,支持隨機存取。索引從0開始,索引訪問越界會拋出運行時異常。注意與C語言數組不同。

NSArray被定義為類,引用類型,拷貝時具有引用語義。

NSArray的初始化:

NSArray *array1 = [NSArray arrayWithObjects:@"Shanghai",@"Beijing",@"New York",@"Paris",nil]; //工廠方法初始化,最后要加nil,表示數組的結束。

NSArray *array2 = [[NSArray alloc]initWithObjects:@"Shanghai",@"Beijing",@"New York",@"Paris",nil]; //分配內存+初始化器,最后也要加nil做結束符

NSArray *array3 = @[Shanghai",@"Beijing",@"New York",@"Paris"]; //直接使用字面常量,在最前面要加@,結束時不需要加nil

NSArray的元素必須是對象,即NSObject的子類:

NSString *text = @"Panda"; //字符串對象

如為基本數值類型,須用NSNumber(繼承于NSValue的類)封裝為對象后,才能放入數組中。

NSInteger number = 100; // NSInteger是基本數值類型(整數),不是一個類

NSNumber *numberObject1 = [NSNumber numberWithInteger:number]; //使用NSNumber將NSInteger包裝為對象

NSNumber *numberObject2 = @300u; //使用字面常量初始化

如為C語言結構類型(struct),須用NSValue封裝為對象類型后,才能放入數組中。

Point point; //Point是一個結構類型(struct)

point.h = 100;

point.v = 200;

NSValue *pointObject = [NSValue value:&point withObjCType:@encode(Point)]; //使用NSValue將struct包裝為對象

數組元素可以是不同對象類型,可能會有類型不安全。

NSNull *nullValue = [NSNull null]; //創建了一個空值類

NSArray *array4 = @[text, numberObject1, numberObject2, pointObject, nullValue ]; //創建的array4數組中大多的元素都是不同的對象類型

NSArray具有常量性:數組的長度和元素指針都不能更改。但指針指向的對象內部可以更改。

BLNPoint *p1=[[BLNPoint alloc] initWithX:10 WithY:20];

BLNPoint *p2=[[BLNPoint alloc] initWithX:20 WithY:40];

BLNPoint *p3=[[BLNPoint alloc] initWithX:30 WithY:60];

BLNPoint *p4=[[BLNPoint alloc] initWithX:40 WithY:80];

NSArray *array5=@[p1,p2,p3,p4];

NSLog(@"array5: %@", array5);

BLNPoint *p5=[[BLNPoint alloc] initWithX:50 WithY:100];

//1. 更改元素長度-----不可以!

//[array5 addObject:p5];

//2. 更改元素指針-----不可以!

//array5[0]=p5;

//3. 更改指針指向的內容-----可以!

for(int i=0;i

????? BLNPoint *pt = array5[i];

????? pt.x++;

????? pt.y++;

}

NSLog(@"array5: %@", array5);

數組遍歷

//快速枚舉(fast enumeration),直接訪問內存,優化索引檢查,快5-10倍

for ( BLNPoint* point in array5)

{

????? point.x++;

????? point.y++;

}

//迭代器模式(NSEnumerator),索引檢查+動態消息調用

NSEnumerator *enumerator = [array5 objectEnumerator];

BLNPoint* item;

while (item = [enumerator nextObject])

{

????? item.x++;

????? item.y++;

}

//for循環, 索引檢查+動態消息調用

for (int i=0; i

????? NSLog(@"array5[%d],%@", i, array5[i]);

}

推薦快速枚舉的方法來遍歷數組,快速枚舉的方法較其他兩種方法簡化了索引檢查和動態消息調用的次數,效率和性能較高。沒有極特殊的理由不要用后兩種方法來遍歷數組。

數組查找

indexOfObject查找是否存在“值相等”的對象(類型需要實現isEqual)

- (BOOL)isEqual:(id)anObject{ //重寫需要比較類的isEqual方法

? ? ? if (! [anObject isKindOfClass: [BLNPoint class]] ){? //isKindOfClass方法用來查看一個實例是不是屬于某個類

?????????? ?return false;

????? }

BLNPoint* other=anObject;

return (self.x==other.x && self.y==other.y);

}

//開始查找數組

BLNPoint* target=[[BLNPoint alloc] initWithX:33 WithY:63];

NSUInteger index1=[array5 indexOfObject:target];

//按指針查找

indexOfObjectIdenticalTo查找是否存在“指針相等”的對象

NSUInteger index2=[array5 indexOfObjectIdenticalTo:p3];

數組排序

不改變原數組(常量性),返回新數組

NSArray* sortArray1=[array1 sortedArrayUsingSelector:@selector(compare:)]; //由于數組的常量性,排列后遠數組不變,必須將返回值賦給一個新的數組

使用NSMutableArray數組

NSMutableArray支持更改數組長度和元素指針。為NSArray子類。

NSMutableArray初始化后,會分配一個緩存容量capacity,一般大于實際元素數量,當長度增長時,如實際需求大于capacity,其capacity會以近似兩倍的方式指數增長。(和NSMutableString類似)

伴隨代價:

分配新的堆內存 2*capacity

將原來堆內存上的內容拷貝到新內存

釋放原來堆內存

最佳實踐:估計好capacity,預先分配好一定容量,避免以后capacity的增長。

盡量避免使用insertObject: atIndex:和removeObjectAtIndex:等操作,因為會改變數組元素序列,涉及大量內存拷貝操作,代價高

NSMutableArray? *muArray1=[NSMutableArray arrayWithObjects:p1,p2,p3,p4, nil];

NSLog(@"before change, muArray1: %@",muArray1);

BLNPoint *p6=[[BLNPoint alloc] initWithX:60 WithY:120];

BLNPoint *p7=[[BLNPoint alloc] initWithX:70 WithY:140];

//修改元素

[muArray1 addObject:p5];

[muArray1 removeObjectAtIndex:2];

[muArray1 insertObject:p6 atIndex:1];

muArray1[0]=p7;? //[muArray1 setObject:p7 atIndexedSubscript:0]; //替換有兩種方法,前一種方法比較方便

NSLog(@"after change, muArray1: %@",muArray1);

//預估容量

int count=100;?NSMutableArray *muArray2=[NSMutableArray arrayWithCapacity:10];//編程時根據實際情況合理分配預估容量,盡量不要讓capacity出現不夠用兒擴容,那樣代價比較大

for (int i=0; i< count; i++) {

????? BLNPoint *pt=[[BLNPoint alloc] initWithX:i*10 WithY:i*20];

????? [muArray2 addObject: pt];

}

認識Set

NSSet是一個無序的集合,其存儲的對象不能重復。

NSSet被定義為class,引用類型,拷貝時具有引用語義。

常量集合NSSet,可變量集合:NSMutableSet:

常量性:長度和元素指針都不能更改,但指針指向的對象內部可以更改。

創建NSMutableSet時用initWithCapacity提前設置capacity

支持Fast Enumeration和NSEnumerator遍歷,前者較快

NSSet *set1 =?[NSSet setWithObjects:@"Shanghai",@"Beijing",@"New York",@"Paris", nil];

NSLog(@"set1 count: %lu", set1.count);

NSMutableSet *set2 =?[NSMutableSet setWithObjects:@"Shanghai",@"Beijing",@"New York",@"Paris", nil];

[set2 addObject:@"London"];

[set2 addObject:@"Paris"];

[set2 removeObject:@"Beijing"];

NSLog(@"set2 count: %lu", set2.count);

if([set2 containsObject:@"Shanghai"])

{

????? NSLog(@"set2 contains Shanghai");

}

for(NSString* item in set2)

{

????? NSLog(@"%@", item);

}

認識Dictionary

NSDictionary是一個存儲key-value的無序集合,key唯一,value可重復。

NSDictionary被定義為class,引用類型,拷貝時具有引用語義。

常量集合NSDictionary,可變量集合:NSMutableDictionary:

常量性:長度和元素指針都不能更改,但指針指向的對象內部可以更改。

創建NSMutableDictionary時用initWithCapacity提前設置capacity

支持Fast Enumeration和NSEnumerator遍歷,前者較快

BLNPoint *p1=[[BLNPoint alloc] initWithX:10 WithY:20];

BLNPoint *p2=[[BLNPoint alloc] initWithX:20 WithY:40];

BLNPoint *p3=[[BLNPoint alloc] initWithX:30 WithY:60];

BLNPoint *p4=[[BLNPoint alloc] initWithX:40 WithY:80];

BLNPoint *p5=[[BLNPoint alloc] initWithX:50 WithY:100];

NSDictionary *dictionary1 = @{

@"Shanghai" : p1,

@"Beijing" : p2,

@"New York" : p3,

@"Paris" : p4};

NSMutableDictionary *dictionary2 = [NSMutableDictionary dictionaryWithObjectsAndKeys:

p1,@"Shanghai",

p2,@"Beijing",

p3,@"New York",

p4,@"Paris",

nil];

NSLog(@"dictionary1 count: %lu", dictionary1.count);

NSLog(@"dictionary2 count: %lu", dictionary2.count);

BLNPoint* result1=[dictionary1 objectForKey:@"Beijing"];

BLNPoint* result2=dictionary1[@"Shanghai"];

NSLog(@"%@", result1);

NSLog(@"%@", result2);

for(NSString* key in dictionary1)

{

????? id object=dictionary1[key];

????? NSLog(@"key:%@, object:%@", key, object);

}

[dictionary2 setObject:p5 forKey:@"London"];

[dictionary2 removeObjectForKey:@"Shanghai"];

NSLog(@"dictionary2: %@", dictionary2);

第十天視頻課程:

了解ARC

自動引用計數器(Automatic Reference Counting)是Objective-C默認的內存管理機制,其針對堆上的對象,由編譯器自動生成操作引用計數的指令(retain或release),來管理對象的創建和釋放。

哪些對象受ARC管理:

OC對象指針

Block指針

使用_atteibute_((NSObject))定義的typedef

哪些對象不受ARC管理:

值類型(簡單值類型,C語言struct)如int 、struct

使用其他方式分配的堆對象(如使用malloc分配)C/C++分配的堆內存也不受ARC管理

非內存資源

引用計數器管理

新創建(使用alloc, new, copy等)一個引用類型對象,引用計數為1

BLNPoint *p1 = [[BLNPoint alloc]init];

BLNRectangle *rect = [[BLNRectangle alloc]init];

對象引用計數器增加1--retain操作:

將對象引用賦值給其他變量或常量:?BLNPoint *p2 = p1;

將對象引用賦值給其他屬性或實例變量:rect.center = p1;

將對象傳遞給函數參數,或者返回值:draw(p1); //函數運行結束時ARC就會減1

將對象加入集合中:

array=[[NSMutableArray alloc]initWithCapacity:10];

[array addObject:p1];

對象引用計數減1--release操作:

將對象局部變量或者全局變量賦值為nil或其他值: p1 = nil;?p2 = nil;

將屬性賦值為nil或其他值:rect.center = nil;

實例屬性所在的對象被釋放:rect = nil;

參數或局部變量離開函數

將對象從集合中刪除:[array removeObjectAtIndex:0];

引用計數變為0時,內存自動被釋放。

引用計數的內存示意圖,注意如果有兩個對象屬性互相引用,則要把一邊設置為弱引用,以防ARC不能正確工作

自動釋放池(Autorelease Pool)

release會導致對象立即釋放。如果頻繁對對象進行release, 可能會造成瑣碎的內存管理負擔。autorelease可以將release的調用延遲到自動釋放池被釋放時。

推薦使用自動釋放池(Autorelease Pool)Block,當其結束時,所有接受autorelease消息的對象將會被立即釋放(即發送release消息)。

AppKit和UIKit框架在處理每一次事件循環迭代時,都會將其放入一個Autorelease Pool中。大多數情況,無需程序員干預。

什么時候需要手動管理Autorelease Pool

編寫的程序不基于UI框架,如命名行程序。

在循環中創建大量臨時對象,需要更早的釋放,避免臨時對象聚集導致內存峰值過大。

void poolDemo(){

????? @autoreleasepool {

?????????? ?for (int i = 0; i < 10; i++) {

???????????????? ?__unused? BLNRectangle *rect = [[BLNRectangle alloc]init];

??????????? }

????? }

}

在主線程之外創建新的線程,在新線程開始執行處,需要創建自己的Autorelease Pool

可以嵌套使用Autorelease Pool

第十一天視頻課程:

認識協議 Protocol

協議:類型的合同約定,只描述外部接口,不提供具體實現。也稱作類型的行為約定。

協議可以包含以下成員:屬性、實例方法、類方法、初始化器(不常用)、析構器(不常用),協議中無法包含實例變量成員。

//聲明一個類并遵守一個協議

@interface BLNPoint : NSObject //<>里表示這個類遵守Drawable這個協議,即必須實現Drawable協議里全部的要求方法(@required)

@property? NSInteger x;

@property? NSInteger y;

@end

//聲明一個類

@protocol Drawable

@property? NSInteger x;

@property? NSInteger y;

-(void)draw;

+(void)createShape;

//-(id)init;

//-(void)dealloc;

@optional //可選擇方法關鍵字(@optional)遵守協議的類可實現也可以不實現下面的可選擇方法,在@optional之上的都是要求方法(@required)

-(void)moveToX:(NSInteger)x withY:(NSInteger)y;

@end

協議中定義的屬性本質上是訪問器方法,編譯器不會合成實例變量

使用協議

一個類遵守協議,需要實現協議約定的所有@required成員,協議中的屬性須在實現類的.h文件中聲明(編譯器合成實例變量需要)協議不會實現實例變量

注意編譯警告信息:

遵守協議后卻沒有實現全部必選協議方法(@required)時,會出現警告提示。

協議類型變量被賦值非協議類型對象時,會出現警告提示。

//實現一個參數遵守協議的函數process1

void process1(idobj)

{

????? [obj draw];

????? NSLog(@"[%ld,%ld]",(long)obj.x,(long)obj.y);

}

//調用process1函數

process1(@“abc”);//此時將出現警告,因為傳遞的@“abc”并沒有遵守協議

協議本質上是一種類型,可以作為聲明類型,但不能創建實例。

檢查協議類型

使用conformsToProtocol:檢查對象是否實現了協議

void process2(id obj){

if ([obj conformsToProtocol:@protocol(AProtocol) ]) {

????? [obj methodA];

}

更多協議形式

協議繼承

一個協議可以繼承一個或多個協議

實現子協議的類型,也必須實現父協議中約定的成員

協議組合

可以使用protocol來組合多個協議

void process4(idobj){

????? [obj methodA];

????? [obj methodC];

}

實現組合協議的類型,必須實現組合協議中的每一個協議

可選協議

協議的某些成員可以定義為optional,不必實現

了解常用協議

NSObject:包含對象的常用操作,相等、字符串表示、哈希。

NSCopying:支持復制的類型必須遵守該協議。

NSMutableCopying:在NSCopying協議的基礎上,支持復制數據的可變性。

NSFastEnumberation:實現快速枚舉for-in的類型采用。

NSCoding協議:支持將對象圖像進行編碼/解碼以支持對象序列化。

第十二天視頻課程:

類別 Categroy

類別支持在沒有源代碼的情況下,基于某些特定的場合,為一個類增加功能。

可以添加:類方法、實例方法、重寫基類方法

不能添加:屬性、實例變量、已存在的同名方法。本質上類別不可以更改原來的內存模型。

可以使用原有類的實例變量,在類別中添加getter和setter訪問器方法。

//NSObject類的聲明

@interface BLNPoint : NSObject

{

????? float _weight;

}

@property NSInteger x;

@property NSInteger y;

-(void)move;

@end

//在類別(Drawing)實現中加入BLNPoint類聲明實例變量_weight的getter和setter訪問器方法,等于把原來類中的實例變量,包裝成了一個屬性。

-(void)setWeight:(NSInteger)weight

{

????? NSLog(@"Point.setWeight");

????? _weight=weight;

}

-(NSInteger)weight

{

????? NSLog(@"Point.weight");

????? return _weight;

}

命名規范:類名+擴展方法,如:NSString+Drawing.h/.m

// ?NSString類的類別Drawing的聲明

@interface NSString(Drawing) //()內的Drawing就是類別

-(void)draw;

+(NSString*) convertToString:(NSInteger)number;

@end

類別的使用

使用場景

適合在沒有源代碼的情況下,向已經封裝的類中添加方法。

為一個類在某些特殊場景下增加功能。

對于復雜的大型文件分割實現,同一個類的不同的類別可以放在不同的.m文件中實現,便于管理,但是不推薦將一個類擴充的特別大。

添加類別

自己創建類

系統的類

第三方庫

擴展 Extension

擴展支持在編譯時、有類的源代碼的前提下,向類添加功能??梢詫U展看做匿名的類別。擴展沒有.h文件對內可以訪問,對外不公開不可以訪問。(有點像私有成員)

接口定義在.m文件中@implementation前聲明,實現代碼仍然在@implementation內實現。

擴展支持添加一下成員:

添加屬性

添加實例成員

添加類方法

添加實例方法

改寫屬性的讀寫屬性(可以由readonly改成readwrite,反之不行)

//擴展的實現

@interface Circle () //擴展在原類名后加()

{

????? NSString * _name;

}

@property (readwrite )NSInteger? radius;//修改讀寫屬性

@property? NSInteger center;//添加屬性

-(float)getDiameter;//實例方法

+(void)process:(Circle*) circle;//類方法

@end

@implementation Circle

+(void)process:(Circle*) circle

{

????? [circle getDiameter];

????? [circle getArea];

}

-(NSInteger)getArea{

????? float area=M_PI*self.radius*self.radius;

????? NSLog(@"Circle Area : %f",area);

????? return area;

}

-(float)getDiameter{

????? float diameter = 2*M_PI*self.radius;

????? NSLog(@"Diameter : %f", diameter );

????? return diameter;

}

@end

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

推薦閱讀更多精彩內容