目錄
- 關于字符串編碼
- NSData
- UIEdgeInsets、contentInset、contentEdgeInsets
- ios中常用的遍歷運算方法
- 排序
1. 關于字符串編碼
ASCII碼跟Unicode沒有本質的區別。只不過Unicode表示范圍比ASCII大。ASCII可以表示127個英文字母,其中每個英文字母都有一個十進制編碼,并且通過這個十進制編碼轉化成二進制數(編碼)存入到內存當中(占1字節)。 而在Unicode中,英文字母的編碼與其在ASCII中沒有不同。只是Unicode每個字符占2個字節,于是轉化為二進制時就變成‘000000 ASCII’。
unicode是一個字符集,utf8是在這個字符集基礎上的一種具體的編碼方案。
- 中文解碼
諸如\u82f1\u6587
NSString* strA = [@"%E4%B8%AD%E5%9B%BD" stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *strB = [@"中國" stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
- URL編碼:有的時候會碰見字符串里有一些特殊字符在轉成URL的時候 會出現轉換不了的情況,這個時候需要對字符串進行編碼
首先來看下什么樣的是URL編碼(字符串中帶有%22 類似這樣的)
NSString *str = @"[http://m.tuniu.com/api/home/data/index/c/%7B%22v%22%3A%227.1.0%22%2C%22ct%22%3A20%2C%22dt%22%3A1%2C%22p%22%3A11210%2C%22cc%22%3A2500%7D/d/%7B%22clientModel%22%3A%22HONOR+H30-L01%22%2C%22width%22%3A720%7D](http://m.tuniu.com/api/home/data/index/c/%7B%22v%22:%227.1.0%22,%22ct%22:20,%22dt%22:1,%22p%22:11210,%22cc%22:2500%7D/d/%7B%22clientModel%22:%22HONOR+H30-L01%22,%22width%22:720%7D%10)"
URL編碼: ios中http請求遇到漢字的時候,需要轉化成UTF-8
//(iOS9.0(包括9.0)以上使用)
NSString *str3 =[str2 stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSetURLQueryAllowedCharacterSet]];
//(iOS9.0以下使用)
NSString *str3 =[str2 stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncodind];
URL解碼:請求后,返回的數據,如何顯示的是這樣的格式:%3A%2F%2F,此時需要我們進行UTF-8解碼,用到的方法是
//(iOS9.0(包括9.0)以上使用):
NSString *str2 = [str stringByRemovingPercentEncoding];
//(iOS9.0以下使用)
NSString *str2 =[str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
- 密碼加密:修改代碼保存密碼不能使用明文,使用base64進行加密。提交數據到服務器不能使用明文,使用base64進行加密。
#pragma mark - Base64
編碼:
-(NSString*)base64Encode:(NSString *)string
{
//1.將字符串轉換成二進制數據
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
//2.利用ios7.0的方法,直接 base64 編碼
return [data base64EncodedStringWithOptions:0];
}
解碼:
- (NSString*)base64Decode:(NSString *)string
{
//1.將base64編碼后的字符串,解碼成二進制數據
//這里不能使用注釋掉的方法轉換成二進制,因為 string 是已經編碼過的字符串
//NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
NSData *data = [[NSData alloc]initWithBase64EncodedString:string options:0];
//2.返回解碼的字符串
return [[NSString alloc] initWithData:dataencoding:NSUTF8StringEncoding];
}
2. NSData
NSData介紹
NSData主要是提供一塊原始數據的封裝,用來包裝數據的,NSData存儲的是二進制數據,方便數據的封裝與流動,屏蔽了數據之間的差異,文本、音頻、圖像等數據都可用NSData來存儲。比較常見的是NSString/NSImage數據的封裝與傳遞。在應用中,最常用于訪問存儲在文件中、網絡資源中的數據以及在多媒體開發時,比較常用,例如拼接音頻、圖片。
應用場景:
-
存儲本地文本、文件等數據:
使用archiveRootObject: toFile:
方法可以將一個對象直接寫入到一個文件中,但有時候可能想將多個對象寫入到同一個文件中,那么就要使用NSData來進行歸檔對象.NSData可以為一些數據提供臨時存儲空間,以便隨后寫入文件,或者存放從磁盤讀取的文件內容。
可以使用[NSMutableData data]創建可變數據空間。
NSKeyedArchiver:- 如果對象是NSString、NSDictionary、NSArray、NSData、NSNumber 等類型,可以直接用NSKeyedArchiver進行歸檔和恢復.
- 但不是所有的對象都可以直接用這種方法進行歸檔,只有遵守了NSCoding協議的對象才可以.
網絡資源中的數據:
在進行網絡數據通信的時候,數據在網絡中是二進制形式傳輸,加密與解密配套時可以正確解碼二進制流信息。經常會遇到NSData類型的數據。在該數據是dictionary結構的情況下,可以用NSJSONSerialization
來解析轉換成dictionary。多媒體開發(音頻、圖片):
通常用于上傳圖片及音頻。
說明:NSData根本不管傳遞的內容到底是什么,僅僅是傳遞一塊內存 —— 僅需內存的起始地址和長度
1. 數據相互轉換
NSData與字符串的相互轉換:
//將字符串轉化為NSData
+(NSData *)toNSData:(NSString *)str{
NSError *error = nil;
NSData *aData = [str dataUsingEncoding:NSUTF8StringEncoding];
if (aData.length && error ==nil) {
return aData;
}else{
return nil;
}
}
//將NSData轉化為字符串
+(NSString *)transformData:(NSData *)data{
NSError *error = nil;
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (string.length && error == nil) {
return string;
}else{
return nil;
}
}
NSData與UIImage的相互轉換:
//將NSData轉UIImage
UIImage *image = [UIImage imageWithData:data];
//將UIImage轉NSData
NSData *data = UIImageJPEGRepresentation(image, 1.0);
NSDictionary -> NSData
// 方法1:NSKeyedArchiver
NSDictionary *dict = @{
@"key1": @"value1",
@"key1": @"value2"
};
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:dict];
// 方法2:NSJSONSerialization
NSError *writeError = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict
options:NSJSONWritingPrettyPrinted
error:&writeError];
if (writeError != nil) {
NSLog(@"Convent to JSON failed: %@", [writeError localizedDescription]);
return;
}
NSData -> NSDictionary
// 方法1:NSKeyedUnarchiver
NSDictionary *myDictionary = (NSDictionary*) [NSKeyedUnarchiver unarchiveObjectWithData:myData];
// 方法2:NSJSONSerialization
NSDictionary *dictFromData = [NSJSONSerialization JSONObjectWithData:dataFromDict
options:NSJSONReadingAllowFragments
NSArray -> NSData
// 方法1:NSKeyedArchiver
NSArray *array = [NSArray arrayWithObjects:@"1",@"2",@"3",nil];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:arr];
// 方法2:NSJSONSerialization
NSError *writeError = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:array
options:NSJSONWritingPrettyPrinted
error:&writeError];
if (writeError != nil) {
NSLog(@"Convent to JSON failed: %@", [writeError localizedDescription]);
return;
}
NSData -> NSArray
// 方法1:NSKeyedUnarchiver
NSArray *data2arry = [NSKeyedUnarchiver unarchiveObjectWithData:data];
// 方法2:NSJSONSerialization
NSArray *arrayFromData = [NSJSONSerialization JSONObjectWithData:dataFromArray
options:NSJSONReadingAllowFragments
error:&error];
總結說明:無論使用NSKeyedUnarchiver還是NSJSONSerialization,其實它接受的參數類型都是id,所以我們也可以將繼承自NSObject的自定義類轉換為NSData,但該對象必須遵循NSCoding協議,即實現:
- (instancetype)initWithCoder:(NSCoder *)aDecoder;
- (void)encodeWithCoder:(NSCoder *)aCoder;
2. NSData數據加密
#import <Foundation/Foundation.h>
@interface NSData (LSCore)
#pragma mark - 加密
// md5
- (NSString*)md5;
// sha
- (NSString *)sha1;
- (NSString *)sha256;
- (NSString *)sha384;
- (NSString *)sha512;
// base64
- (NSString *)base64Encode;
- (NSString *)base64Decode;
// des
- (NSString *)encryptWithKey:(NSString *)key;
- (NSString *)decryptWithKey:(NSString *)key;
// Add libz.dylib to your project.
#pragma mark - gzip
extern NSString* const GzipErrorDomain;
- (NSData*)gzip:(NSError**)error;
@end
3. UIEdgeInsets 、contentInset、contentEdgeInsets
4. ios中常用的遍歷運算方法
想到循環遍歷數組、字典這些常見的集合,大家腦子里第一反應就是for循環和快速遍歷。
ios中常用的遍歷運算方法
遍歷的目的是獲取集合中的某個對象或執行某個操作,所以能滿足這個條件的方法都可以作為備選:
- 經典for循環(常用)
- for in (NSFastEnumeration)(常用)
- makeObjectsPerformSelector
- kvc集合運算符(效率低)
- enumerateObjectsUsingBlock
- enumerateObjectsWithOptions(NSEnumerationConcurrent)
- dispatch_apply
** 1.第一種方式:經典for循環**
Objective-C是基于C語言的,自然可以使用for循環.
遍歷數組:
NSArray *iosArray = @[@"L", @"O", @"V", @"E", @"I", @"O", @"S"];
for (int i = 0; i < iosArray.count; i++) {
//處理數組中數據
NSLog(@"%@", iosArray[i]);
}
遍歷數組很簡單沒問題,下面遍歷字典.
遍歷字典:
NSDictionary *dict = @{@"1":@"11", @"2":@"22", @"3":@"33"};
NSArray *keysArray = [dict allKeys];
for (int i = 0; i < keysArray.count; i++) {
//根據鍵值處理字典中的每一項
NSString *key = keysArray[i];
NSString *value = dict[key];
NSLog(@"%@", value);
}
我們知道字典和set是無序的,所以我們無法根據特定的整數下標來直接訪問其中的值,于是需要先獲取字典中的鍵或者set中的所有對象,這樣就可以在獲取到的有序數組上進行遍歷了。然而創建數組是要額外的開銷的,還會多創建出一個數組對象,他會保留collection中的所有對象,占用了內存。
總結優缺點:
優點:被廣泛使用,容易接受,操作簡單;
缺點:遍歷字典和set是比較繁瑣,會占用比較多的系統資源。
** 2.第二種方式:NSEnumerator**
NSEnumerator是一個抽象基類。其中nextObject是關鍵方法,它返回枚舉里的下一個對象。每次調用該方法,其內部結構都會更新,使得下一次調用方法時能返回下一個對象。等到枚舉中全部的對象都已經返回之后,在調用就會返回nil,表示達到了枚舉的末端。
Foundation框架中的collection都實現了這種遍歷方式,例如:
NSArray *array = @[@"L", @"O", @"V", @"E", @"I", @"O", @"S"];
NSEnumerator *enumerator = [array objectEnumerator];//正向遍歷
NSEnumerator *enumerator = [array reverseObjectEnumerator];//反向遍歷
id object;
while ((object = [enumerator nextObject]) != nil) {
//處理枚舉器中的數據
NSLog(@"%@", object);
}
字典和set實現的方式相似,不同的是字典中有key和value,要根據具體的key取出value。同時提供了正向遍歷和反向遍歷。
總結優缺點:
優點:
代碼更易讀,不需要定義額外的數組;
缺點:
1、無法直接獲取遍歷操作的下標,需要另外聲明變量記錄;
2、需要自行創建NSEnumerator對象,稍顯麻煩。
** 2.第二種方式:快速遍歷**
O快速遍歷與NSEnumerator差不多,然而語法更為簡潔,它為for循環開設了in關鍵字,簡化了遍歷collection所需的語法,例如:
遍歷數組
NSArray *iosArray = @[@"L", @"O", @"V", @"E", @"I", @"O", @"S"];
for (NSString *obj in iosArray) {
//處理數組中的數據
NSLog(@"%@", obj);
}
遍歷字典也同樣簡單:
NSDictionary *dict = @{@"1":@"11", @"2":@"22", @"3":@"33"};
for (NSString *key in dict) {
//處理字典的鍵值
NSString *value = dict[key];
NSLog(@"%@", value);
}
反向遍歷可以使用:
for (NSString *obj in [iosArray reverseObjectEnumerator])
總結優缺點
優點:
語法簡潔,使用方便,效率高;
缺點:
- 無法方便獲取當前遍歷的下標;
- 無法在遍歷過程中修改被遍歷的collection,否則會導致崩潰。
** 4.第四種方式:基于塊的遍歷方式**
遍歷數組
NSArray *iosArray = @[@"L", @"O", @"V", @"E", @"I", @"O", @"S"];
[iosArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"%@", obj);
if ([obj isEqualToString:@"E"]) {
*stop = YES;
}
}];
參數說明:
obj表示數組中的元素;
idx表示元素的下標;
stop可以控制遍歷何時停止,在需要停止時令stop = YES即可(不要忘記前面的)。
這種方法清晰明了,數組元素,下標都可直接獲取,就連何時停止都很容易實現,break都可以退休了,遍歷字典也同樣簡單。
遍歷字典
NSDictionary *dict = @{@"1":@"11", @"2":@"22", @"3":@"33"};
[dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
NSLog(@"%@", obj);
if ([obj isEqualToString:@"22"]) {
*stop = YES;
}
}];
你沒有看錯,就是這么簡單,block直接把字典的key和value都給我們了,再也不用書寫直白而繁瑣的代碼了。
注意:
若已知collection里對象的數據類型,可以修改塊簽名,知道對象的精確類型后,編譯器就可以檢測開發者是否調用了該對象所不具有的方法,并在發現問題時報錯。
NSDictionary *dict = @{@"1":@"11", @"2":@"22", @"3":@"33"};
[dict enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL * _Nonnull stop) {
NSLog(@"%@", obj);
if ([obj isEqualToString:@"22"]) {
*stop = YES;
}
}];
如代碼,直接把key和value的類型修改成NSString類型。
反向遍歷
反向遍歷也同樣方便,調用另外一個方法即可:
NSArray *iosArray = @[@"L", @"O", @"V", @"E", @"I", @"O", @"S"];
[iosArray enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSString *obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"%@", obj);
if ([obj isEqualToString:@"E"]) {
*stop = YES;
}
}];
這個方法相對于正向遍歷多了一個枚舉類型的參數NSEnumerationReverse,打開這個選項就可以反向遍歷了。
并發遍歷
順著這個枚舉類型的參數,就會引出塊枚舉的另一大優勢:并發遍歷,參數是:NSEnumerationConcurrent,也就是可以同時遍歷collection中的幾個元素,具體數量根據系統資源而定。這樣會充分利用系統資源,高效快捷的完成collection的遍歷,系統底層會通過GCD來處理并發事宜,開發者不需要擔心內存和線程,其他方式若要實現高效的并發遍歷十分有難度。通過塊枚舉遍歷,改變collection并不會引起崩潰,代碼如下:
NSArray *iosArray = @[@"L", @"O", @"V", @"E", @"I", @"O", @"S"];
NSMutableArray *iosMutableArray = [NSMutableArray arrayWithArray:iosArray];
[iosMutableArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(NSString *obj, NSUInteger idx, BOOL * _Nonnull stop) {
obj = [NSString stringWithFormat:@"_%@", obj];
[iosMutableArray replaceObjectAtIndex:idx withObject:obj];
NSLog(@"%@", obj);
if ([obj isEqualToString:@"_I"]) {
*stop = YES;
}
}];
優缺點總結
優點:
- 可以完美實現for循環的所有功能;
- 可以方便獲取集合中的每一項元素;
- 提供了循環遍歷的參數,NSEnumerationReverse用來實現倒序循環。NSEnumerationConcurrent用來實現并發遍歷,兩個參數可以同時使用;
- 這種循環方式效率高,能夠提升程序性能,開發者可以專注于業務邏輯,而不必擔心內存和線程的問題;
- 當開啟NSEnumerationConcurrent選項時,可以實現for循環和快速遍歷無法輕易實現的并發循環功能,系統底層會通過GCD處理并發事宜,這樣可以充分利用系統和硬件資源,達到最優的遍歷效果;
- 可以修改塊簽名,當我們已經明確集合中的元素類型時,可以把默認的簽名id類型修改成已知類型,比如常見的NSString,這樣既可以節省系統資源開銷,也可以防止誤向對象發送不存在的方法是引起的崩潰。
缺點:
- 很多開發者不知道這種遍歷方式;
- 這里使用了block,需要注意在block里容易引起的保留環問題,比如使用self調用方法時,把self轉化成若引用即可打破保留環。如:
weak `typeof(self)weakSelf = self
或者
__weak MyController *weakSelf = self;
在block里使用weakSelf即可。
注意
使用基于塊的遍歷時是可以修改遍歷的元素的,不會導致崩潰,但是如果要刪除遍歷的元素會導致后面的元素無法遍歷而崩潰,解決辦法有2種:
一種是復制一份原集合的副本,對副本進行操作,找出所要操作的元素后再處理原集合;
使用反向遍歷,反向遍歷刪除元素后不會導致崩潰。
5. 排序
數組是有序容器,因此集合中只有數組才能排序。
不可變數組排序
方法1
NSArray *arr = @[@"aa",@"rr",@"pp",@"hh",@"xx",@"vv"];
//用系統的方法進行排序,系統缺少兩個元素比較的方法.
//selector方法選擇器.
NSArray *sortArr = [arr sortedArrayUsingSelector:@selector(compare:)];
NSLog(@"%@",sortArr);
方法2:block塊語法
[arr sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [(NSString *)obj1 compare:(NSString *)obj2];
}];
NSLog(@"%@",arr);
}
可變數組排序
方法1
NSMutableArray *arr = [@[@54 ,@33,@12,@23,@65] mutableCopy];
[arr sortUsingSelector:@selector(compare:)];//compare數組中兩個元素比較的方法
NSLog(@"%@",arr);
方法2
[arr sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [(NSNumber *)obj1 compare:(NSNumber *)obj2];
}];
NSLog(@"%@",arr);
}