iOS開發經驗(5)-數據類型及編碼解碼(NSData、NSString..)

目錄

  1. 關于字符串編碼
  2. NSData
  3. UIEdgeInsets、contentInset、contentEdgeInsets
  4. ios中常用的遍歷運算方法
  5. 排序
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中常用的遍歷運算方法

遍歷的目的是獲取集合中的某個對象或執行某個操作,所以能滿足這個條件的方法都可以作為備選:

  1. 經典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])

總結優缺點
優點:
語法簡潔,使用方便,效率高;
缺點:

  1. 無法方便獲取當前遍歷的下標;
  2. 無法在遍歷過程中修改被遍歷的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;
        }
    }];

優缺點總結
優點:

  1. 可以完美實現for循環的所有功能;
  • 可以方便獲取集合中的每一項元素;
  • 提供了循環遍歷的參數,NSEnumerationReverse用來實現倒序循環。NSEnumerationConcurrent用來實現并發遍歷,兩個參數可以同時使用;
  • 這種循環方式效率高,能夠提升程序性能,開發者可以專注于業務邏輯,而不必擔心內存和線程的問題;
  • 當開啟NSEnumerationConcurrent選項時,可以實現for循環和快速遍歷無法輕易實現的并發循環功能,系統底層會通過GCD處理并發事宜,這樣可以充分利用系統和硬件資源,達到最優的遍歷效果;
  • 可以修改塊簽名,當我們已經明確集合中的元素類型時,可以把默認的簽名id類型修改成已知類型,比如常見的NSString,這樣既可以節省系統資源開銷,也可以防止誤向對象發送不存在的方法是引起的崩潰。

缺點:

  1. 很多開發者不知道這種遍歷方式;
  • 這里使用了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);
    }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374

推薦閱讀更多精彩內容