iOS開發(fā)系列—Objective-C之Foundation框架

常用結構體

在Foundation中定義了很多常用結構體類型來簡化我們的日常開發(fā)置鼻,這些結構體完全采用Objective-C定義镇饮,和我們自己定義的結構體沒有任何區(qū)別,之所以由框架為我們提供完全是為了簡化我們的開發(fā)箕母。常用的結構體有NSRange储藐、NSPoint、NSSize嘶是、NSRect等

//

//? main.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>/*NSRange表示一個范圍*/void test1(){

? ? NSRange rg={3,5};//第一參數(shù)是起始位置第二個參數(shù)是長度

? ? //NSRange rg;

? ? //rg.location=3;

? ? //rg.length=5;

? ? //NSRange rg={.location=3,.length=5};

? ? //常用下面的方式定義 NSRange rg2=NSMakeRange(3,5);//使用NSMakeRange定義一個NSRange

? ? //打印NSRange可以使用Foundation中方法 NSLog(@"rg2 is %@", NSStringFromRange(rg2));//注意不能直接NSLog(@"rg2 is %@", rg2)钙勃,因為rg2不是對象(準確的說%@是指針)而是結構體}/*NSPoint表示一個點*/void test2(){

? ? NSPoint p=NSMakePoint(10, 15);//NSPoint其實就是CGPoint

? ? //這種方式比較常見 NSPoint p2=CGPointMake(10, 15);

? ? NSLog(NSStringFromPoint(p2));

}/*NSSize表示大小*/void test3(){

? ? NSSize s=NSMakeSize(10, 15);//NSSize其實就是CGSize

? ? //這種方式比較常見 CGSize s2=CGSizeMake(10, 15);

? ? NSLog(NSStringFromSize(s2));

}/*NSRect表示一個矩形*/void test4(){

? ? NSRect r=NSMakeRect(10, 5, 100, 200);//NSRect其實就是CGRect

? ? //這種方式比較常見 NSRect r2=CGRectMake(10, 5, 100, 200);

? ? NSLog(NSStringFromRect(r2));

}int main(int argc, const char * argv[]) {

? ? @autoreleasepool {

? ? ? ? test1();

? ? ? ? test2();

? ? ? ? test3();

? ? ? ? test4();

? ? } return 0;

}

可以看到對于常用結構體在Foundation框架中都有一個對應的make方法進行創(chuàng)建,這也是我們日后比較常用的操作;而且與之對應的還都有一個NSStringFromXX方法來進行字符串轉換聂喇,方便我們調試辖源。上面也提到NSSize其實就是CGSize,NSRect其實就是CGRect希太,我們可以通過查看代碼進行確認克饶,例如NSSize定義:

繼續(xù)查看CGSize的代碼:

日期

接下來熟悉一下Foundation框架中日期的操作

//

//? main.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>int main(int argc, const char * argv[]) {


? ? NSDate *date1=[NSDate date];//獲得當前日期

? ? NSLog(@"%@",date1); //結果:2014-07-16 07:25:28 +0000


? ? NSDate *date2=[NSDate dateWithTimeIntervalSinceNow:100];//在當前日期的基礎上加上100秒,注意在ObjC中多數(shù)時間單位都是秒

? ? NSLog(@"%@",date2); //結果:2014-07-16 07:27:08 +0000


? ? NSDate *date3=[NSDate distantFuture];//隨機獲取一個將來的日期

? ? NSLog(@"%@",date3); //結果:4001-01-01 00:00:00 +0000


? ? NSTimeInterval time=[date2 timeIntervalSinceDate:date1];//日期之差,返回單位為秒

? ? NSLog(@"%f",time); //結果:100.008833


? ? NSDate *date5=[date1 earlierDate:date3];//返回比較早的日期

? ? NSLog(@"%@",date5); //結果:2014-07-16 07:25:28 +0000


? ? //日期格式化

? ? NSDateFormatter *formater1=[[NSDateFormatter alloc]init];

? ? formater1.dateFormat=@"yy-MM-dd HH:mm:ss";

? ? NSString *datestr1=[formater1 stringFromDate:date1];

? ? NSLog(@"%@",datestr1); //結果:14-07-16 15:25:28

? ? //字符串轉化為日期

? ? NSDate *date6=[formater1 dateFromString:@"14-02-14 11:07:16"];

? ? NSLog(@"%@",date6); //結果:2014-02-14 03:07:16 +0000

? ? return 0;

}

字符串

不可變字符串

在ObjC中字符串操作要比在C語言中簡單的多誊辉,在下面的例子中你將看到字符串的初始化矾湃、大小寫轉化、后綴前綴判斷堕澄、字符串比較洲尊、字符串截取、字符串轉換等奈偏,通過下面的例子我們基本可以掌握常用的字符串操作(注意這些內容雖然基本坞嘀,但卻是十分常用的操作,需要牢記):

//

//? main.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>/**字符串操作*/void test1(){

? ? char *str1="C string";//這是C語言創(chuàng)建的字符串

? ? NSString *str2=@"OC string";//ObjC字符串需要加@惊来,并且這種方式創(chuàng)建的對象不需要自己釋放內存

? ? //下面的創(chuàng)建方法都應該釋放內存

? ? NSString *str3=[[NSString alloc] init];

? ? str3=@"OC string";

? ? NSString *str4=[[NSString alloc] initWithString:@"Objective-C string"];

? ? NSString *str5=[[NSString alloc] initWithFormat:@"age is %i,name is %.2f",19,1.72f];

? ? NSString *str6=[[NSString alloc] initWithUTF8String:"C string"];//C語言的字符串轉換為ObjC字符串

? ? //以上方法都有對應靜態(tài)方法(一般以string開頭),不需要管理內存(系統(tǒng)靜態(tài)方法一般都是自動釋放)

? ? NSString *str7=[NSString stringWithString:@"Objective-C string"];

}void test2(){

? ? NSLog(@"\"Hello world!\" to upper is %@",[@"Hello world!" uppercaseString]);

? ? //結果:"Hello world!" to upper is HELLO WORLD!

? ? NSLog(@"\"Hello world!\" to lowwer is %@",[@"Hello world!" lowercaseString]);

? ? //結果:"Hello world!" to lowwer is hello world!


? ? //首字母大寫丽涩,其他字母小寫

? ? NSLog(@"\"Hello world!\" to capitalize is %@",[@"Hello world!" capitalizedString]);

? ? //結果:"Hello world!" to capitalize is Hello World!


? ? BOOL result= [@"abc" isEqualToString:@"aBc"];

? ? NSLog(@"%i",result);

? ? //結果:0

? ? NSComparisonResult result2= [@"abc" compare:@"aBc"];//如果是[@"abc" caseInsensitiveCompare:@"aBc"]則忽略大小寫比較

? ? if(result2==NSOrderedAscending){

? ? ? ? NSLog(@"left<right.");

? ? }else if(result2==NSOrderedDescending){

? ? ? ? NSLog(@"left>right.");

? ? }else if(result2==NSOrderedSame){

? ? ? ? NSLog(@"left=right.");

? ? }

? ? //結果:left>right.}void test3(){

? ? NSLog(@"has prefix ab? %i",[@"abcdef" hasPrefix:@"ab"]);

? ? //結果:has prefix ab? 1

? ? NSLog(@"has suffix ab? %i",[@"abcdef" hasSuffix:@"ef"]);

? ? //結果:has suffix ab? 1

? ? NSRange range=[@"abcdefabcdef" rangeOfString:@"cde"];//注意如果遇到cde則不再往后面搜索,如果從后面搜索或其他搜索方式可以設置第二個options參數(shù)

? ? if(range.location==NSNotFound){

? ? ? ? NSLog(@"not found.");

? ? }else{

? ? ? ? NSLog(@"range is %@",NSStringFromRange(range));

? ? }

? ? //結果:range is {2, 3}}//字符串分割void test4(){

? ? NSLog(@"%@",[@"abcdef" substringFromIndex:3]);//從第三個索引開始(包括第三個索引對應的字符)截取到最后一位

? ? //結果:def

? ? NSLog(@"%@",[@"abcdef" substringToIndex:3]);////從0開始截取到第三個索引(不包括第三個索引對應的字符)

? ? //結果:abc

? ? NSLog(@"%@",[@"abcdef" substringWithRange:NSMakeRange(2, 3)]);

? ? //結果:cde

? ? NSString *str1=@"12.abcd.3a";

? ? NSArray *array1=[str1 componentsSeparatedByString:@"."];//字符串分割

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

? ? /*結果:

? ? ? (

? ? ? ? 12,

? ? ? ? abcd,

? ? ? ? 3a

? ? ? )

? ? ? */

}//其他操作void test5(){

? ? NSLog(@"%i",[@"12" intValue]);//類型轉換

? ? //結果:12

? ? NSLog(@"%zi",[@"hello world,世界你好!" length]);//字符串長度注意不是字節(jié)數(shù)

? ? //結果:17

? ? NSLog(@"%c",[@"abc" characterAtIndex:0]);//取出制定位置的字符

? ? //結果:a

? ? const char *s=[@"abc" UTF8String];//轉換為C語言字符串

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

? ? //結果:abc}int main(int argc, const char * argv[]) {

? ? test1();

? ? test2();

? ? test3();

? ? test4();

? ? test5();

? ? return 0;

}

注意:上面代碼注釋中提到的需要釋放內存指的是在MRC下的情況裁蚁,當然本質上在ARC下也需要釋放矢渊,只是這部分代碼編譯器會自動創(chuàng)建。

擴展--文件操作

在ObjC中路徑枉证、文件讀寫等操作是利用字符串來完成的矮男,這里通過幾個簡單的例子來演示(首先在桌面上新建一個test.txt文件,里面存儲的內容是”hello world,世界你好室谚!”)

//

//? main.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>void test1(){

? ? //讀取文件內容

? ? NSString *path=@"/Users/kenshincui/Desktop/test.txt";

? ? NSString *str1=[NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];

? ? //注意上面也可以使用gb2312 gbk等,例如kCFStringEncodingGB_18030_2000毡鉴,但是需要用CFStringConvertEncodingToNSStringEncoding轉換

? ? NSLog(@"str1 is %@",str1);

? ? //結果:str1 is hello world,世界你好崔泵!




? ? //上面我們看到了讀取文件,但并沒有處理錯誤,當然在ObjC中可以@try @catch @finnally但通常我們并不那么做

? ? //由于我們的test.txt中有中文猪瞬,所以使用下面的編碼讀取會報錯憎瘸,下面的代碼演示了錯誤獲取的過程

? ? NSError *error;

? ? NSString *str2=[NSString stringWithContentsOfFile:path encoding:kCFStringEncodingGB_18030_2000 error:&error];//注意這句話中的error變量是**error,就是指針的指針那就是指針的地址陈瘦,由于error就是一個指針此處也就是error的地址&error幌甘,具體原因見下面補充

? ? if(error){

? ? ? ? NSLog(@"read error ,the error is %@",error);

? ? }else{

? ? ? ? NSLog(@"read success,the file content is %@",str2);

? ? }

? ? //結果:read error ,the error is Error Domain=NSCocoaErrorDomain Code=261 "The file couldn’t be opened using the specified text encoding." UserInfo=0x100109620 {NSFilePath=/Users/kenshincui/Desktop/test.txt, NSStringEncoding=1586}




? ? //讀取文件內容還有一種方式就是利用URl,它除了可以讀取本地文件還可以讀取網絡文件

? ? //NSURL *url=[NSURL URLWithString:@"file:///Users/kenshincui/Desktop/test.txt"];

? ? NSURL *url=[NSURL URLWithString:@"http://www.apple.com"];

? ? NSString *str3=[NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];

? ? NSLog(@"str3 is %@",str3);

}void test2(){

? ? //下面是文件寫入

? ? NSString *path1=@"/Users/kenshincui/Desktop/test2.txt";

? ? NSError *error1;

? ? NSString *str11=@"hello world,世界你好痊项!";

? ? [str11 writeToFile:path1 atomically:YES encoding:NSUTF8StringEncoding error:&error1];//automically代表一次性寫入锅风,如果寫到中間出錯了最后就全部不寫入

? ? if(error1){

? ? ? ? NSLog(@"write fail,the error is %@",[error1 localizedDescription]);//調用localizedDescription是只打印關鍵錯誤信息

? ? }else{

? ? ? ? NSLog(@"write success!");

? ? }

? ? //結果:write success!}//路徑操作void test3(){

? ? NSMutableArray *marray=[NSMutableArray array];//可變數(shù)組

? ? [marray addObject:@"Users"];

? ? [marray addObject:@"KenshinCui"];

? ? [marray addObject:@"Desktop"];

? ? NSString *path=[NSString pathWithComponents:marray];

? ? NSLog(@"%@",path);//字符串拼接成路徑

? ? //結果:Users/KenshinCui/Desktop

? ? NSLog(@"%@",[path pathComponents]);//路徑分割成數(shù)組

? ? /*結果:

? ? (

? ? ? ? Users,

? ? ? ? KenshinCui,

? ? ? ? Desktop

? ? )

? ? */

? ? NSLog(@"%i",[path isAbsolutePath]);//是否絕對路徑(其實就是看字符串是否以“/”開頭)

? ? //結果:0

? ? NSLog(@"%@",[path lastPathComponent]);//取得最后一個目錄

? ? //結果:Desktop

? ? NSLog(@"%@",[path stringByDeletingLastPathComponent]);//刪除最后一個目錄,注意path本身是常量不會被修改,只是返回一個新字符串

? ? //結果:Users/KenshinCui

? ? NSLog(@"%@",[path stringByAppendingPathComponent:@"Documents"]);//路徑拼接

? ? //結果:Users/KenshinCui/Desktop/Documents}

//擴展名操作void test4(){

? ? NSString *path=@"Users/KenshinCui/Desktop/test.txt";

? ? NSLog(@"%@",[path pathExtension]);//取得擴展名鞍泉,注意ObjC中擴展名不包括"."

? ? //結果:txt

? ? NSLog(@"%@",[path stringByDeletingPathExtension]);//刪除擴展名遏弱,注意包含"."

? ? //結果:Users/KenshinCui/Desktop/test

? ? NSLog(@"%@",[@"Users/KenshinCui/Desktop/test" stringByAppendingPathExtension:@"mp3"]);//添加擴展名

? ? //結果:Users/KenshinCui/Desktop/test.mp3}int main(int argc, const char * argv[]) {

? ? test1();

? ? test2();

? ? test3();

? ? test4();

? ? return 0;

}

注意:在上面的例子中我們用到了可變數(shù)組,下面會專門介紹塞弊。

可變字符串

我們知道在字符串操作過程中我們經常希望改變原來的字符串,當然這在C語言中實現(xiàn)比較復雜泪姨,但是ObjC為我們提供了新的可變字符串類NSMutableString游沿,它是NSString的子類。

//

//? main.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>int main(int argc, const char * argv[]) {


? ? /*可變字符串肮砾,注意NSMutableString是NSString子類*/

? ? //注意雖然initWithCapacity分配字符串大小诀黍,但是不是絕對的不可以超過此范圍,聲明此變量對性能有好處

? ? NSMutableString *str1= [[NSMutableString alloc] initWithCapacity:10];

? ? [str1 setString:@"hello"];//設置字符串

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

? ? //結果:hello

? ? [str1 appendString:@",world!"];//追加字符串

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

? ? //結果:hello,world!

? ? [str1 appendFormat:@"我的年齡是%i仗处。dear,I love you.",18];

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

? ? //結果:hello,world!我的年齡是18眯勾。dear,I love you.


? ? //替換字符串

? ? NSRange range=[str1 rangeOfString:@"dear"];

? ? [str1 replaceCharactersInRange:range withString:@"Honey"];

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

? ? //結果:hello,world!我的年齡是18。Honey,I love you.


? ? //插入字符串

? ? [str1 insertString:@"My name is Kenshin." atIndex:12];

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

? ? //結果:hello,world!My name is Kenshin.我的年齡是18婆誓。Honey,I love you.


? ? //刪除指定字符串

? ? [str1 deleteCharactersInRange:[str1 rangeOfString:@"My name is Kenshin."]];//刪除指定范圍的字符串

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

? ? //結果:hello,world!我的年齡是18吃环。Honey,I love you.


? ? return 0;

}

數(shù)組

不可變數(shù)組

下面將演示常用的數(shù)組操作:初始化、數(shù)組對象的方法執(zhí)行洋幻、數(shù)組元素的遍歷郁轻、在原有數(shù)組基礎上產生新數(shù)組、數(shù)組排序等

//

//? main.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>#import "Person.h"void test1(){

? ? //NSArray長度不可變所以初始化的時候就賦值文留,并且最后以nil結尾

? ? //此外需要注意NSArray不能存放C語言的基礎類型

? ? NSObject *obj=[[NSObject alloc]init];

? ? //NSArray *array1=[[NSArray alloc] initWithObjects:@"abc",obj,@"cde",@"opq", nil];

? ? NSArray *array1=[NSArray arrayWithObjects:@"abc",obj,@"cde",@"opq",@25, nil];

? ? NSLog(@"%zi",array1.count);//數(shù)組長度,結果:5

? ? NSLog(@"%i",[array1 containsObject:@"cde"]);//是否包含某個對象好唯,結果:1

? ? NSLog(@"%@",[array1 lastObject]);//最后一個對象,結果:25

? ? NSLog(@"%zi",[array1 indexOfObject:@"abc"]);//對象所在的位置:0


? ? Person *person1=[Person personWithName:@"Kenshin"];

? ? Person *person2=[Person personWithName:@"Kaoru"];

? ? Person *person3=[Person personWithName:@"Rosa"];

? ? NSArray *array2=[[NSArray alloc]initWithObjects:person1,person2,person3, nil];

? ? [array2 makeObjectsPerformSelector:@selector(showMessage:) withObject:@"Hello,world!"];//執(zhí)行所有元素的showMessage方法,后面的參數(shù)最多只能有一個

? ? /*結果:

? ? My name is Kenshin,the infomation is "Hello,world!".

? ? My name is Kaoru,the infomation is "Hello,world!".

? ? My name is Rosa,the infomation is "Hello,world!".

? ? */}//數(shù)組的遍歷void test2(){

? ? NSObject *obj=[[NSObject alloc]init];

? ? NSArray *array=[[NSArray alloc] initWithObjects:@"abc",obj,@"cde",@"opq",@25, nil];

? ? //方法1

? ? for(int i=0,len=array.count;i<len;++i){

? ? ? ? NSLog(@"method1:index %i is %@",i,[array objectAtIndex:i]);

? ? }

? ? /*結果:

? ? method1:index 0 is abc

? ? method1:index 1 is <NSObject: 0x100106de0>

? ? method1:index 2 is cde

? ? method1:index 3 is opq

? ? method1:index 4 is 25

? ? */



? ? //方法2

? ? for(id obj in array){

? ? ? ? NSLog(@"method2:index %zi is %@",[array indexOfObject:obj],obj);

? ? }

? ? /*結果:

? ? method2:index 0 is abc

? ? method2:index 1 is <NSObject: 0x100602f00>

? ? method2:index 2 is cde

? ? method2:index 3 is opq

? ? method2:index 4 is 25

? ? */



? ? //方法3,利用代碼塊方法

? ? [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {

? ? ? ? NSLog(@"method3:index %zi is %@",idx,obj);

? ? ? ? if(idx==2){//當idx=2時設置*stop為YES停止遍歷

? ? ? ? ? ? *stop=YES;

? ? ? ? }

? ? }];

? ? /*結果:

? ? method3:index 0 is abc

? ? method3:index 1 is <NSObject: 0x100106de0>

? ? method3:index 2 is cde

? ? */



? ? //方法4燥翅,利用迭代器

? ? //NSEnumerator *enumerator= [array objectEnumerator];//獲得一個迭代器

? ? NSEnumerator *enumerator=[array reverseObjectEnumerator];//獲取一個反向迭代器

? ? //NSLog(@"all:%@",[enumerator allObjects]);//獲取所有迭代對象骑篙,注意調用完此方法迭代器就遍歷完了,下面的nextObject就沒有值了

? ? id obj2=nil;

? ? while (obj2=[enumerator nextObject]) {

? ? ? ? NSLog(@"method4:%@",obj2);

? ? }

? ? /*結果:

? ? method4:25

? ? method4:opq

? ? method4:cde

? ? method4:<NSObject: 0x100106de0>

? ? method4:abc

? ? */}//數(shù)組派生出新的數(shù)組void test3(){

? ? NSArray *array=[NSArray arrayWithObjects:@"1",@"2",@"3", nil];

? ? NSArray *array2=[array arrayByAddingObject:@"4"];//注意此時array并沒有變

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

? ? /*結果:

? ? (

? ? ? ? 1,

? ? ? ? 2,

? ? ? ? 3,

? ? ? ? 4

? ? )

? ? */



? ? NSLog(@"%@",[array2 arrayByAddingObjectsFromArray:[NSArray arrayWithObjects:@"5",@"6", nil]]);//追加形成新的數(shù)組

? ? /*結果:

? ? (

? ? ? ? 1,

? ? ? ? 2,

? ? ? ? 3,

? ? ? ? 4,

? ? ? ? 5,

? ? ? ? 6

? ? )

? ? */



? ? NSLog(@"%@",[array2 subarrayWithRange:NSMakeRange(1, 3)]);//根據(jù)一定范圍取得生成一個新的數(shù)組

? ? /*結果:

? ? (

? ? ? ? 2,

? ? ? ? 3,

? ? ? ? 4

? ? )

? ? */



? ? NSLog(@"%@",[array componentsJoinedByString:@","]);//數(shù)組連接森书,形成一個字符串

? ? //結果:1,2,3


? ? //讀寫文件

? ? NSString *path=@"/Users/KenshinCui/Desktop/array.xml";

? ? [array writeToFile:path atomically:YES];

? ? NSArray *array3=[NSArray arrayWithContentsOfFile:path];

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

? ? /*結果:

? ? (

? ? ? ? 1,

? ? ? ? 2,

? ? ? ? 3

? ? )

? ? */}//數(shù)組排序void test4(){

? ? //方法1,使用自帶的比較器

? ? NSArray *array=[NSArray arrayWithObjects:@"3",@"1",@"2", nil];

? ? NSArray *array2= [array sortedArrayUsingSelector:@selector(compare:)];

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

? ? /*結果:

? ? (

? ? ? ? 1,

? ? ? ? 2,

? ? ? ? 3

? ? )

? ? */



? ? //方法2,自己定義比較器

? ? Person *person1=[Person personWithName:@"Kenshin"];

? ? Person *person2=[Person personWithName:@"Kaoru"];

? ? Person *person3=[Person personWithName:@"Rosa"];

? ? NSArray *array3=[NSArray arrayWithObjects:person1,person2,person3, nil];

? ? NSArray *array4=[array3 sortedArrayUsingSelector:@selector(comparePerson:)];

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

? ? /*結果:

? ? (

? ? ? ? "name=Kaoru",

? ? ? ? "name=Kenshin",

? ? ? ? "name=Rosa"

? ? )

? ? */



? ? //方法3使用代碼塊

? ? NSArray *array5=[array3 sortedArrayUsingComparator:^NSComparisonResult(Person *obj1, Person *obj2) {

? ? ? ? return [obj2.name compare:obj1.name];//降序

? ? }];

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

? ? /*結果:

? ? (

? ? ? ? "name=Rosa",

? ? ? ? "name=Kenshin",

? ? ? ? "name=Kaoru"

? ? )

? ? */



? ? //方法4 通過描述器定義排序規(guī)則

? ? Person *person4=[Person personWithName:@"Jack"];

? ? Person *person5=[Person personWithName:@"Jerry"];

? ? Person *person6=[Person personWithName:@"Tom"];

? ? Person *person7=[Person personWithName:@"Terry"];

? ? NSArray *array6=[NSArray arrayWithObjects:person4,person5,person6,person7, nil];

? ? //定義一個排序描述

? ? NSSortDescriptor *personName=[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES];

? ? NSSortDescriptor *accountBalance=[NSSortDescriptor sortDescriptorWithKey:@"account.balance" ascending:YES];

? ? NSArray *des=[NSArray arrayWithObjects:personName,accountBalance, nil];//先按照person的name排序再按照account的balance排序

? ? NSArray *array7=[array6 sortedArrayUsingDescriptors:des];

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

? ? /*結果:

? ? (

? ? ? ? "name=Jack",

? ? ? ? "name=Jerry",

? ? ? ? "name=Terry",

? ? ? ? "name=Tom"

? ? )

? ? */}int main(int argc, const char * argv[]) {

? ? test1();

? ? test2();

? ? test3();

? ? test4();

? ? return 0;

}

需要注意幾點:

NSArray中只能存放對象靶端,不能存放基本數(shù)據(jù)類型谎势,通常我們可以通過在基本數(shù)據(jù)類型前加@進行轉換;

數(shù)組中的元素后面必須加nil以表示數(shù)據(jù)結束躲查;

makeObjectsPerformSelector執(zhí)行數(shù)組中對象的方法它浅,其參數(shù)最多只能有一個;

上面數(shù)組操作中無論是數(shù)組的追加镣煮、刪除姐霍、截取都沒有改變原來的數(shù)組,只是產生了新的數(shù)組而已典唇;

對象的比較除了使用系統(tǒng)自帶的方法镊折,我們可以通過自定義比較器的方法來實現(xiàn);

可變數(shù)組

下面看一下可變數(shù)組的內容:

//

//? main.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>#import "Person.h"void test1(){

? ? Person *person1=[Person personWithName:@"Kenshin"];

? ? Person *person2=[Person personWithName:@"Kaoru"];

? ? Person *person3=[Person personWithName:@"Rosa"];

? ? NSMutableArray *array1=[NSMutableArray arrayWithObjects:person1,person2,person3, nil];

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

? ? /*結果:

? ? (

? ? ? ? "name=Kenshin",

? ? ? ? "name=Kaoru",

? ? ? ? "name=Rosa"

? ? )

? ? */


? ? Person *person4=[Person personWithName:@"Jack"];//此時person4的retainCount為1

? ? [array1 addObject:person4];//添加一個元素,此時person4的retainCount為2

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

? ? /*結果:

? ? (

? ? ? ? "name=Kenshin",

? ? ? ? "name=Kaoru",

? ? ? ? "name=Rosa",

? ? ? ? "name=Jack"

? ? )

? ? */


? ? [array1 removeObject:person3];//刪除一個元素

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

? ? /*結果:

? ? (

? ? ? ? "name=Kenshin",

? ? ? ? "name=Kaoru",

? ? ? ? "name=Jack"

? ? )

? ? */


? ? [array1 removeLastObject];//刪除最后一個元素,//此時person4的retainCount為1

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

? ? /*結果:

? ? (

? ? ? ? "name=Kenshin",

? ? ? ? "name=Kaoru"

? ? )

? ? */


? ? [array1 removeAllObjects];//刪除所以元素


? ? //注意當往數(shù)組中添加一個元素時會retain因此計數(shù)器+1介衔,當從數(shù)組中移除一個元素時會release因此計數(shù)器-1

? ? //當NSMutalbeArray對象release的時候會依次調用每一個對象的release}void test2(){

? ? NSMutableArray *array1=[NSMutableArray arrayWithObjects:@"1",@"3",@"2", nil];

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

? ? /*結果:

? ? (

? ? ? ? 1,

? ? ? ? 3,

? ? ? ? 2

? ? )

? ? */


? ? NSArray *array2= [array1 sortedArrayUsingSelector:@selector(compare:)];//注意這個方法沒有修改array1

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

? ? /*結果:

? ? (

? ? ? ? 1,

? ? ? ? 3,

? ? ? ? 2

? ? )

? ? */


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

? ? /*結果:

? ? (

? ? ? ? 1,

? ? ? ? 2,

? ? ? ? 3

? ? )

? ? */

? ? [array1 sortUsingSelector:@selector(compare:)];//這個方法會修改array1

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

? ? /*結果:

? ? (

? ? ? ? 1,

? ? ? ? 2,

? ? ? ? 3

? ? )

? ? */

? ? }int main(int argc, const char * argv[]) {


? ? test1();


? ? test2();


? ? return 0;

}

可變數(shù)組中的元素后面必須加nil以表示數(shù)據(jù)結束恨胚;

往一個可變數(shù)組中添加一個對象,此時這個對象的引用計數(shù)器會加1炎咖,當這個對象從可變數(shù)組中移除其引用計數(shù)器減1赃泡。同時當整個數(shù)組銷毀之后會依次調用每個對象的releaes方法。

在不可變數(shù)組中無論對數(shù)組怎么排序乘盼,原來的數(shù)組順序都不會改變升熊,但是在可變數(shù)組中如果使用sortUsingSelector:排序原來的數(shù)組順序就發(fā)生了變化。


字典

字典在我們日常開發(fā)中也是比較常用的绸栅,通過下面的代碼我們看一下在ObjC中的字典的常用操作:初始化级野、遍歷、排序

//

//? main.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>void test1(){

? ? NSDictionary *dic1=[NSDictionary dictionaryWithObject:@"1" forKey:@"a"];

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

? ? /*結果:

? ? {

? ? ? ? a = 1;

? ? }

? ? */


? ? //常用的方式

? ? NSDictionary *dic2=[NSDictionary dictionaryWithObjectsAndKeys:

? ? ? ? ? ? ? ? ? ? ? ? @"1",@"a",

? ? ? ? ? ? ? ? ? ? ? ? @"2",@"b",

? ? ? ? ? ? ? ? ? ? ? ? @"3",@"c",

? ? ? ? ? ? ? ? ? ? ? ? nil];

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

? ? /*結果:

? ? {

? ? ? ? a = 1;

? ? ? ? b = 2;

? ? ? ? c = 3;

? ? }

? ? */



? ? NSDictionary *dic3=[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"1",@"2", nil] forKeys:[NSArray arrayWithObjects:@"a",@"b", nil]];

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

? ? /*結果:

? ? {

? ? ? ? a = 1;

? ? ? ? b = 2;

? ? }

? ? */



? ? //更簡單的方式

? ? NSDictionary *dic4=@{@"1":@"a",@"2":@"b",@"3":@"c"};

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

? ? /*結果:

? ? {

? ? ? ? 1 = a;

? ? ? ? 2 = b;

? ? ? ? 3 = c;

? ? }

? ? */}void test2(){

? ? NSDictionary *dic1=[NSDictionary dictionaryWithObjectsAndKeys:

? ? ? ? ? ? ? ? ? ? ? ? @"1",@"a",

? ? ? ? ? ? ? ? ? ? ? ? @"2",@"b",

? ? ? ? ? ? ? ? ? ? ? ? @"3",@"c",

? ? ? ? ? ? ? ? ? ? ? ? @"2",@"d",

? ? ? ? ? ? ? ? ? ? ? ? nil];

? ? NSLog(@"%zi",[dic1 count]); //結果:4

? ? NSLog(@"%@",[dic1 valueForKey:@"b"]);//根據(jù)鍵取得值粹胯,結果:2

? ? NSLog(@"%@",dic1[@"b"]);//還可以這樣讀取蓖柔,結果:2

? ? NSLog(@"%@,%@",[dic1 allKeys],[dic1 allValues]);

? ? /*結果:

? ? (

? ? ? ? d,

? ? ? ? b,

? ? ? ? c,

? ? ? ? a

? ? ),(

? ? ? ? 2,

? ? ? ? 2,

? ? ? ? 3,

? ? ? ? 1

? ? )

? ? */


? ? NSLog(@"%@",[dic1 objectsForKeys:[NSArray arrayWithObjects:@"a",@"e" , nil]notFoundMarker:@"not fount"]);//后面一個參數(shù)notFoundMarker是如果找不到對應的key用什么值代替

? ? /*結果:

? ? (

? ? ? ? 1,

? ? ? ? "not fount"

? ? )

? ? */}void test3(){

? ? NSDictionary *dic1=[NSDictionary dictionaryWithObjectsAndKeys:

? ? ? ? ? ? ? ? ? ? ? ? @"1",@"a",

? ? ? ? ? ? ? ? ? ? ? ? @"2",@"b",

? ? ? ? ? ? ? ? ? ? ? ? @"3",@"c",

? ? ? ? ? ? ? ? ? ? ? ? @"2",@"d",

? ? ? ? ? ? ? ? ? ? ? ? nil];

? ? //遍歷1

? ? for (id key in dic1) {//注意對于字典for遍歷循環(huán)的是key

? ? ? ? NSLog(@"%@=%@",key,[dic1 objectForKey:key]);

? ? }

? ? /*結果:

? ? d=2

? ? b=2

? ? c=3

? ? a=1

? ? */


? ? //遍歷2

? ? NSEnumerator *enumerator=[dic1 keyEnumerator];//還有值的迭代器[dic1 objectEnumerator]

? ? id key=nil;

? ? while (key=[enumerator nextObject]) {

? ? ? ? NSLog(@"%@=%@",key,[dic1 objectForKey:key]);


? ? }

? ? /*結果:

? ? d=2

? ? b=2

? ? c=3

? ? a=1

? ? */


? ? //遍歷3

? ? [dic1 enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {

? ? ? ? NSLog(@"%@=%@",key,obj);

? ? }];

? ? /*結果:

? ? d=2

? ? b=2

? ? c=3

? ? a=1

? ? */}void test4(){

? ? NSMutableDictionary *dic=[NSMutableDictionary dictionaryWithObjectsAndKeys:@"1",@"a",

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? @"2",@"b",

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? @"3",@"c",

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? @"2",@"d",

? ? ? ? ? ? ? ? ? ? ? ? ? ? nil];

? ? [dic removeObjectForKey:@"b"];

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

? ? /*結果:

? ? {

? ? ? ? a = 1;

? ? ? ? c = 3;

? ? ? ? d = 2;

? ? }

? ? */


? ? [dic addEntriesFromDictionary:@{@"e":@"7",@"f":@"6"}];

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

? ? /*結果:

? ? {

? ? ? ? a = 1;

? ? ? ? c = 3;

? ? ? ? d = 2;

? ? ? ? e = 7;

? ? ? ? f = 6;

? ? }

? ? */


? ? [dic setValue:@"5" forKey:@"a"];

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

? ? /*結果:

? ? {

? ? ? ? a = 5;

? ? ? ? c = 3;

? ? ? ? d = 2;

? ? ? ? e = 7;

? ? ? ? f = 6;

? ? }

? ? */



? ? //注意,一個字典的key或value添加到字典中時計數(shù)器+1风纠;字典釋放時調用key或value的release一次况鸣,計數(shù)器-1}int main(int argc, const char * argv[]) {

? ? test1();

? ? test2();

? ? test3();

? ? test4();

? ? return 0;

}

注意:同數(shù)組一樣,不管是可變字典還是不可變字典初始化元素后面必須加上nil以表示結束竹观。

裝箱和拆箱

其實從上面的例子中我們也可以看到懒闷,數(shù)組和字典中只能存儲對象類型,其他基本類型和結構體是沒有辦法放到數(shù)組和字典中的栈幸,當然你也是無法給它們發(fā)送消息的(也就是說有些NSObject的方法是無法調用的)愤估,這個時候通常會用到裝箱(boxing)和拆箱(unboxing)。其實各種高級語言基本上都有裝箱和拆箱的過程速址,例如C#中我們將基本數(shù)據(jù)類型轉化為Object就是一個裝箱的過程玩焰,將這個Object對象轉換為基本數(shù)據(jù)類型的過程就是拆箱,而且在C#中裝箱的過程可以自動完成芍锚,基本數(shù)據(jù)類型可以直接賦值給Object對象昔园。但是在ObjC中裝箱的過程必須手動實現(xiàn)蔓榄,ObjC不支持自動裝箱。

在ObjC中我們一般將基本數(shù)據(jù)類型裝箱成NSNumber類型(當然它也是NSObject的子類默刚,但是NSNumber不能對結構體裝箱)甥郑,調用其對應的方法進行轉換:

+(NSNumber *)numberWithChar:(char)value;

+(NSNumber *)numberWithInt:(int)value;

+(NSNumber *)numberWithFloat:(float)value;

+(NSNumber *)numberWithDouble:(double)value;

+(NSNumber *)numberWithBool:(BOOL)value;

+(NSNumber *)numberWithInteger:(NSInteger)value;

拆箱的過程就更加簡單了,可以調用如下方法:

-(char)charValue;

-(int)intValue;

-(float)floatValue;

-(double)doubleValue;

-(BOOL)boolValue;

簡單看一個例子

//

//? main.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>/*可以存放基本類型到數(shù)組荤西、字典*/void test1(){

? ? //包裝類NSNumber澜搅,可以包裝基本類型但是無法包裝結構體類型

? ? NSNumber *number1=[NSNumber numberWithChar:'a'];//'a'是一個C語言的char類型我們無法放倒NSArray中,但是我們可以通過NSNumber包裝

? ? NSArray *array1=[NSArray arrayWithObject:number1];

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

? ? /*結果:

? ? (

? ? ? ? 97

? ? )

? ? */


? ? NSNumber *number2= [array1 lastObject];

? ? NSLog(@"%@",number2);//返回的不是基本類型,結果:97



? ? char char1=[number2 charValue];//number轉化為char

? ? NSLog(@"%c",char1); //結果:a}int main(int argc, const char * argv[]) {

? ? test1();

? ? return? 0;

}

上面我們看到了基本數(shù)據(jù)類型的裝箱和拆箱過程邪锌,那么結構體呢勉躺?這個時候我們需要引入另外一個類型NSValue,其實上面的NSNumber就是NSValue的子類觅丰,它包裝了一些基本數(shù)據(jù)類型的常用裝箱饵溅、拆箱方法,當要對結構體進行裝箱妇萄、拆箱操作我們需要使用NSValue蜕企,NSValue可以對任何數(shù)據(jù)類型進行裝箱、拆箱操作冠句。

事實上對于常用的結構體Foundation已經為我們提供好了具體的裝箱方法:

+(NSValue *)valueWithPoint:(NSPoint)point;

+(NSValue *)valueWithSize:(NSSize)size;

+(NSValue *)valueWithRect:(NSRect)rect;

對應的拆箱方法:

-(NSPoint)pointValue;

-(NSSize)sizeValue;

-(NSRect)rectValue;

//

//? main.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>//NSNumber是NSValue的子類轻掩,而NSValue可以包裝任何類型,包括結構體void test1(){

? ? CGPoint point1=CGPointMake(10, 20);

? ? NSValue *value1=[NSValue valueWithPoint:point1];//對于系統(tǒng)自帶類型一般都有直接的方法進行包裝

? ? NSArray *array1=[NSArray arrayWithObject:value1];//放倒數(shù)組中

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

? ? /*結果:

? ? (

? ? ? ? "NSPoint: {10, 20}"

? ? )

? ? */


? ? NSValue *value2=[array1 lastObject];

? ? CGPoint point2=[value2 pointValue];//同樣對于系統(tǒng)自帶的結構體有對應的取值方法(例如本例pointValue)

? ? NSLog(@"x=%f,y=%f",point2.x,point2.y);//結果:x=10.000000,y=20.000000}int main(int argc, const char * argv[]) {

? ? test1();

? ? return? 0;

}


那么如果是我們自定義的結構體類型呢轩端,這個時候我們需要使用NSValue如下方法進行裝箱:

+(NSValue *)valueWithBytes:(const void *)value objCType:(const char *)type;

調用下面的方法進行拆箱:

-(void)getValue:(void *)value;

//

//? main.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>typedef struct {

? ? int year;

? ? int month;

? ? int day;

} Date;//NSNumber是NSValue的子類,而NSValue可以包裝任何類型逝变,包括結構體void test1(){

? ? //如果我們自己定義的結構體包裝

? ? Date date={2014,2,28};

? ? char *type=@encode(Date);

? ? NSValue *value3=[NSValue value:&date withObjCType:type];//第一參數(shù)傳遞結構體地址基茵,第二個參數(shù)傳遞類型字符串

? ? NSArray *array2=[NSArray arrayWithObject:value3];

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

? ? /*結果:

? ? (

? ? ? ? "<de070000 02000000 1c000000>"

? ? )

? ? */


? ? Date date2;

? ? [value3 getValue:&date2];//取出對應的結構體,注意沒有返回值

? ? //[value3 objCType]//取出包裝內容的類型

? ? NSLog(@"%i,%i,%i",date2.year,date2.month,date2.day); //結果:2014,2,28

? ? }int main(int argc, const char * argv[]) {

? ? test1();

? ? return? 0;

}

擴展1-NSNull

通過前面的介紹大家都知道無論在數(shù)組還是在字典中都必須以nil結尾壳影,否則數(shù)組或字典無法判斷是否這個數(shù)組或字典已經結束(與C語言中的字符串比較類似拱层,C語言中定義字符串后面必須加一個”\0”)。但是我們有時候確實想在數(shù)據(jù)或字典中存儲nil值而不是作為結束標記怎么辦呢宴咧?這個時候需要使用NSNull根灯,這個類是一個單例,只有一個null方法掺栅。簡單看一下:

//

//? main.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>int main(int argc, const char * argv[]) {


? ? NSNull *nl=[NSNull null];//注意這是一個對象烙肺,是一個單例,只有一個方法null創(chuàng)建一個對象

? ? NSNull *nl2=[NSNull null];

? ? NSLog(@"%i",nl==nl2);//由于是單例所以地址相等,結果:1


? ? NSArray *array1=[NSArray arrayWithObjects:@"abc",nl,@123, nil];

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

? ? /*結果:

? ? (

? ? ? ? abc,

? ? ? ? "<null>",

? ? ? ? 123

? ? )

? ? */

? ? return? 0;

}


擴展2-@符號

我們知道在ObjC中很多關鍵字前都必須加上@符號氧卧,例如@protocol桃笙、@property等,當然ObjC中的字符串必須使用@符號沙绝,還有就是%@可以表示輸出一個對象搏明。其實@符號在新版的ObjC中還有一個作用:裝箱鼠锈。

相信聰明的童鞋在前面的例子中已經看到了,這里簡單的介紹一下(在下面的演示中你也將看到很多ObjC新特性)星著。

//

//? main.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>typedef enum {

? ? spring,

? ? summer,

? ? autumn,

? ? winter

} Season;int main(int argc, const char * argv[]) {

? ? /*裝箱*/

? ? NSNumber *number1=@100;

? ? NSArray *array1=[NSArray arrayWithObjects:number1,@"abc",@16,@'A',@16.7,@YES, nil];

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

? ? /*結果:

? ? (

? ? ? ? 100,

? ? ? ? abc,

? ? ? ? 16,

? ? ? ? 65,

? ? ? ? "16.7"

? ? ? ? 1

? ? )

? ? */

? ? NSNumber *number2=@(1+2*3);

? ? NSLog(@"%@",number2); //結果:7

? ? NSNumber *number3=@(autumn);

? ? NSLog(@"%@",number3); //結果:2


? ? NSArray*array2=@[@"abc",@16,@'A',@16.7,@YES];//使用這種方式最后不用添加nil值了NSLog(@"%@",array2[2]); //結果:65

? ? NSMutableArray *array3=[NSMutableArray arrayWithArray:array2];

? ? array3[0]=@"def";

? ? NSLog(@"%@",array3[0]); //結果:def


? ? NSDictionary *dic1=@{@"a":@123,@"b":@'c',@"c":@YES};

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

? ? /*結果:

? ? {

? ? ? ? a = 123;

? ? ? ? b = 99;

? ? ? ? c = 1;

? ? }

? ? */

? ? NSMutableDictionary *dic2=[NSMutableDictionary dictionaryWithDictionary:dic1];

? ? dic2[@"a"]=@456;

? ? NSLog(@"%@",dic2[@"a"]);//結果:456

? ? return 0;

}

反射

由于ObjC動態(tài)性购笆,在ObjC中實現(xiàn)反射可以說是相當簡單,下面代碼中演示了常用的反射操作虚循,具體作用也都在代碼中進行了注釋說明:

Account.h

//

//? Account.h

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>@interface Account : NSObject

@property (nonatomic,assign) double balance;

@end

Account.m

//

//? Account.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import "Account.h"@implementation Account

@end

Person.h

//

//? Person.h

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>@class Account;

@interface Person : NSObject

@property (nonatomic,copy) NSString *name;

@property (nonatomic,retain) Account *account;

-(Person *)initWithName:(NSString *)name;

+(Person *)personWithName:(NSString *)name;

-(void)showMessage:(NSString *)infomation;//自己實現(xiàn)對象比較方法-(NSComparisonResult)comparePerson:(Person *)person;

@end

Person.m

//

//? Person.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import "Person.h"@implementation Person

-(Person *)initWithName:(NSString *)name{

? ? if(self=[super init]){

? ? ? ? self.name=name;

? ? }

? ? return self;

}

+(Person *)personWithName:(NSString *)name{

? ? Person *person=[[Person alloc]initWithName:name];

? ? return person;

}

-(void)showMessage:(NSString *)infomation{

? ? NSLog(@"My name is %@,the infomation is \"%@\".",_name,infomation);

}//自己實現(xiàn)對象比較方法-(NSComparisonResult)comparePerson:(Person *)person{

? ? return [_name compare:person.name];

}

-(NSString *)description{

? ? return [NSString stringWithFormat:@"name=%@",_name];

}

@end

main.m

//

//? main.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>#import "Person.h"int main(int argc, const char * argv[]) {

? ? /*常用方法*/

? ? Person *person1=[Person personWithName:@"Kenshin"];

? ? NSLog(@"%i",[person1 isKindOfClass:[NSObject class]]); //判斷一個對象是否為某種類型(如果是父類也返回YES)同欠,結果:1

? ? NSLog(@"%i",[person1 isMemberOfClass:[NSObject class]]); //判斷一個對象是否是某個類的實例化對象,結果:0

? ? NSLog(@"%i",[person1 isMemberOfClass:[Person class]]); //結果:1

? ? NSLog(@"%i",[person1 conformsToProtocol:@protocol(NSCopying)]);//是否實現(xiàn)了某個協(xié)議邮丰,結果:0

? ? NSLog(@"%i",[person1 respondsToSelector:@selector(showMessage:)]);//是否存在某個方法行您,結果:1


? ? [person1 showMessage:@"Hello,world!"];//直接調用一個方法

? ? [person1 performSelector:@selector(showMessage:) withObject:@"Hello,world!"];

? ? //動態(tài)調用一個方法,注意如果有參數(shù)那么參數(shù)類型只能為ObjC對象剪廉,并且最多只能有兩個參數(shù)


? ? /*反射*/

? ? //動態(tài)生成一個類

? ? NSString *className=@"Person";

? ? Class myClass=NSClassFromString(className);//根據(jù)類名生成類

? ? Person *person2=[[myClass alloc]init]; //實例化

? ? person2.name=@"Kaoru";

? ? NSLog(@"%@",person2);//結果:name=Kaoru

? ? //類轉化為字符串

? ? NSLog(@"%@,%@",NSStringFromClass(myClass),NSStringFromClass([Person class])); //結果:Person,Person

? ? //調用方法

? ? NSString *methodName=@"showMessage:";

? ? SEL mySelector=NSSelectorFromString(methodName);

? ? Person *person3=[[myClass alloc]init];

? ? person3.name=@"Rosa";

? ? [person3 performSelector:mySelector withObject:@"Hello,world!"]; //結果:My name is Rosa,the infomation is "Hello,world!".

? ? //方法轉化為字符串

? ? NSLog(@"%@",NSStringFromSelector(mySelector)); //結果:showMessage:


? ? return 0;

}

拷貝

對象拷貝操作也比較常見娃循,在ObjC中有兩種方式的拷貝:copy和mutablecopy,這兩種方式都將產生一個新的對象斗蒋,只是后者產生的是一個可變對象捌斧。在ObjC中如果要想實現(xiàn)copy或者mutablecopy操作需要實現(xiàn)NSCopy或者NSMutableCopy協(xié)議,拷貝操作產生的新的對象默認引用計數(shù)器是1泉沾,在非ARC模式下我們應該對這個對象進行內存管理捞蚂。在熟悉這兩種操作之前我們首先需要弄清兩個概念:深復制(或深拷貝)和淺復制(或淺拷貝)。

淺復制:在執(zhí)行復制操作時跷究,對于對象中每一層(對象中包含的對象姓迅,例如說屬性是某個對象類型)復制都是指針復制(如果從引用計數(shù)器角度出發(fā),那么每層對象的引用計數(shù)器都會加1)俊马。

深復制:在執(zhí)行復制操作時丁存,至少有一個對象的復制是對象內容復制(如果從引用計數(shù)器角度出發(fā),那么除了對象內容復制的那個對象的引用計數(shù)器不變柴我,其他指針復制的對象其引用計數(shù)器都會加1)解寝。

注:

指針拷貝:拷貝的是指針本身(也就是具體對象的地址)而不是指向的對象內容本身。

對象復制:對象復制指的是復制內容是對象本身而不是對象的地址艘儒。

完全復制:上面說了深復制和淺復制聋伦,既然深復制是至少一個對象復制是對象內容復制,那么如果所有復制都是對象內容復制那么這個復制就叫完全復制界睁。

對比copy和mutablecopy其實前面我們一直還用到一個操作是retain觉增,它們之間的關系如下:

retain:始終采取淺復制,引用計數(shù)器會加1翻斟,返回的對象和被復制對象是同一個對象1(也就是說這個對象的引用多了一個抑片,或者說是指向這個對象的指針多了一個);

copy:對于不可變對象copy采用的是淺復制杨赤,引用計數(shù)器加1(其實這是編譯器進行了優(yōu)化敞斋,既然原來的對象不可變截汪,復制之后的對象也不可變那么就沒有必要再重新創(chuàng)建一個對象了)卦羡;對于可變對象copy采用的是深復制匠璧,引用計數(shù)器不變(原來的對象是可變,現(xiàn)在要產生一個不可變的當然得重新產生一個對象)令野;

mutablecopy:無論是可變對象還是不可變對象采取的都是深復制焰枢,引用計數(shù)器不變(如果從一個不可變對象產生一個可變對象自然不用說兩個對象絕對不一樣肯定是深復制蚓峦;如果從一個可變對象產生出另一個可變對象,那么當其中一個對象改變自然不希望另一個對象改變济锄,當然也是深復制)暑椰。

注:

可變對象:當值發(fā)生了改變,那么地址也隨之發(fā)生改變荐绝;

不可變對象:當值發(fā)生了改變一汽,內容首地址不發(fā)生變化;

引用計數(shù)器:用于計算一個對象有幾個指針在引用(有幾個指針變量指向同一個內存地址)低滩;


//

//? main.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>void test1(){

? ? NSString *name=@"Kenshin";

? ? NSString *str1=[NSString stringWithFormat:@"I'm %@.",name];//注意此時str1的計數(shù)器是1

? ? NSLog(@"%lu",[str1 retainCount]); //結果:1



? ? NSMutableString *str2=[str1 mutableCopy];//注意此時str2的計數(shù)器為1召夹,str1的計數(shù)器還是1

? ? //NSMutableString *str5 =CFRetain((__bridge CFTypeRef)str2);

? ? NSLog(@"retainCount(str1)=%lu,retainCount(str2)=%lu",[str1 retainCount],[str2 retainCount]);

? ? //結果:retainCount(str1)=1,retainCount(str2)=1



? ? [str2 appendString:@"def"];//改變str2,str1不變

? ? NSLog(@"%zi",str1==str2);//二者不是向同一個對象,結果:0

? ? NSLog(@"str1=%@",str1); //結果:str1=I'm Kenshin.

? ? NSLog(@"str2=%@",str2); //結果:str2=I'm Kenshin.def



? ? NSLog(@"str1's %lu",[str1 retainCount]);

? ? NSString *str3=[str1 copy];//str3不是產生的新對象而是復制了對象指針恕沫,但是str1的計數(shù)器+1(當然既然str3同樣指向同一個對象监憎,那么如果計算str3指向的對象引用計數(shù)器肯定等于str1的對象引用計數(shù)器)

? ? NSLog(@"%zi",str1==str3);//二者相等指向同一個對象,結果:1

? ? NSLog(@"retainCount(str1)=%lu,retainCount(str3)=%lu",str1.retainCount,str3.retainCount);

? ? //結果:retainCount(str1)=2,retainCount(str3)=2


? ? //需要注意的是使用copy和mutableCopy是深復制還是淺復制不是絕對,關鍵看由什么對象產生什么樣的對象

? ? NSString *str4=[str2 copy];//由NSMutableString產生了NSString婶溯,二者類型都不同肯定是深拷貝鲸阔,此時str2的計數(shù)器還是1,str4的計數(shù)器也是1

? ? [str2 appendString:@"g"];//改變原對象不影響str4

? ? NSLog(@"%zi",str2==str4); //結果:0

? ? NSLog(@"str2=%@",str2); //結果:str2=I'm Kenshin.defg

? ? NSLog(@"str4=%@",str4); //結果:str4=I'm Kenshin.def


? ? [str1 release];

? ? str1=nil;

? ? [str3 release];//其實這里也可以調用str1再次release迄委,因為他們兩個指向的是同一個對象(但是一般不建議那么做褐筛,不容易理解)

? ? str3=nil;


? ? [str2 release];

? ? str2=nil;

? ? [str4 release];

? ? str4=nil;


? ? //上面只有一種情況是淺拷貝:不可變對象調用copy方法

? ? }int main(int argc,char *argv[]){

? ? test1();

? ? return 0;

}

為了方便大家理解上面的代碼,這里以圖形畫出str1跑筝、str2死讹、str3瞒滴、str4在內存中的存儲情況:

從上面可以清楚的看到str1和str3同時指向同一個對象曲梗,因此這個對象的引用計數(shù)器是2(可以看到兩箭頭指向那個對象),str2和str4都是兩個新的對象妓忍;另外ObjC引入對象拷貝是為了改變一個對象不影響另一個對象虏两,但是我們知道NSString本身就不能改變那么即使我重新復制一個對象也沒有任何意義,因此為了性能著想如果通過copy方法產生一個NSString時ObjC不會再復制一個對象而是將新變量指向同一個對象世剖。

注意網上很多人支招在ARC模式下可以利用_objc_rootRetainCount()或者CFGetRetainCount()取得retainCount都是不準確的定罢,特別是在對象拷貝操作之后你會發(fā)現(xiàn)二者取值也是不同的,因此如果大家要查看retainCount最好還是暫時關閉ARC旁瘫。

要想支持copy或者mutablecopy操作那么對象必須實現(xiàn)NSCoping協(xié)議并實現(xiàn)-(id)copyWithZone:(NSZone*)zone方法祖凫,在Foundation中常用的可復制對象有:NSNumber琼蚯、NSString、NSMutableString惠况、NSArray遭庶、NSMutableArray、NSDictionary稠屠、NSMutableDictionary峦睡。下面看一下如何讓自定義的類支持copy操作:

Person.h

//

//? Person.h

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>@class Account;

@interface Person : NSObject

@property? NSMutableString *name;

@property (nonatomic,assign) int age;

@end

Person.m

//

//? Person.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import "Person.h"@implementation Person

-(NSString *)description{

? ? return [NSString stringWithFormat:@"name=%@,age=%i",_name,_age];

}//實現(xiàn)copy方法-(id)copyWithZone:(NSZone *)zone{

? ? //注意zone是系統(tǒng)已經分配好的用于存儲當前對象的內存

? ? //注意下面創(chuàng)建對象最好不要用[[Person allocWithZone:zone]init],因為子類如果沒有實現(xiàn)該方法copy時會調用父類的copy方法权埠,此時需要使用子類對象初始化如果此時用self就可以表示子類對象榨了,還有就是如果子類調用了父類的這個方法進行重寫copy也需要調用子類對象而不是父類Person

? ? Person *person1=[[[self class] allocWithZone:zone]init];

? ? person1.name=_name;

? ? person1.age=_age;

? ? return person1;

}

@end

main.m

//

//? main.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>#import "Account.h"#import "Person.h"void test1(){

? ? Person *person1=[[Person alloc]init];

? ? NSMutableString *str1=[NSMutableString stringWithString:@"Kenshin"];

? ? person1.name=str1;

? ? //由于name定義的時候使用屬性參數(shù)采用的是copy策略,而根據(jù)前面的知識我們知道NSMutableString的copy策略采用的是對象內容復制攘蔽,因此如果修改str1不會改變person1.name

? ? [str1 appendString:@" Cui"];

? ? NSLog(@"%@",str1);//結果:Kenshin Cui

? ? NSLog(@"%@",person1.name); //結果:Kenshin

? ? }void test2(){

? ? Person *person1=[[Person alloc]init];

? ? person1.name=[NSMutableString stringWithString:@"Kenshin"];

? ? person1.age=28;

? ? Person *person2=[person1 copy];

? ? NSLog(@"%@",person1); //結果:name=Kenshin,age=0

? ? NSLog(@"%@",person2); //結果:name=Kenshin,age=0


? ? [person2.name appendString:@" Cui"];


? ? NSLog(@"%@",person1);//結果:name=Kenshin Cui,age=28

? ? NSLog(@"%@",person2);//結果:name=Kenshin Cui,age=28}int main(int argc,char *argv[]){

? ? test1();

? ? test2();

? ? return 0;

}

在上面的代碼中重點說一下test2這個方法龙屉,在test2方法中我們發(fā)現(xiàn)當修改了person2.name屬性之后person1.name也改變了,這是為什么呢秩彤?我們可以看到在Person.m中自定義實現(xiàn)了copy方法叔扼,同時實現(xiàn)了一個淺拷貝。之所以說是淺拷貝主要是因為我們的name屬性參數(shù)是直接賦值完成的漫雷,同時由于name屬性定義時采用的是assign參數(shù)(默認為assign)瓜富,所以當通過copy創(chuàng)建了person2之后其實person2對象的name屬性和person1指向同一個NSMutableString對象。通過圖形表示如下:

上面test2的寫法純屬為了讓大家了解復制的原理和本質降盹,實際開發(fā)中我們很少會遇到這種情況与柑,首先我們一般定義name的話可能用的是NSString類型,根本也不能修改蓄坏;其次我們定義字符串類型的話一般使用(copy)參數(shù)价捧,同樣可以避免這個問題(因為NSMutableString的copy是深復制)。那么如果我們非要使用NSMutabeString同時不使用屬性的copy參數(shù)如何解決這個問題呢涡戳?答案就是使用深復制结蟋,將-(id)copyWithZone:(NSZone *)zone方法中person1.name=_name改為,person1.name=[_name copy];或person1.name=[_name mutablecopy]即可渔彰,這樣做也正好滿足我們上面對于深復制的定義嵌屎。

補充-NSString的引用計數(shù)器

在好多語言中字符串都是一個特殊的對象,在ObjC中也不例外恍涂。NSString作為一個對象類型存儲在堆中宝惰,多數(shù)情況下它跟一般的對象類型沒有區(qū)別,但是這里我們需求強調一點那就是字符串的引用計數(shù)器再沧。

//

//? main.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>int main(int argc,char *argv[]){


? ? NSString *str1=@"Kenshin";

? ? NSLog(@"retainCount(str1)=%i",(unsigned long)str1.retainCount); //結果:-1

? ? [str1 retain];

? ? NSLog(@"retainCount(str1)=%i",(unsigned long)str1.retainCount); //結果:-1


? ? NSString *str2=[NSString stringWithString:@"Kaoru"];

? ? NSLog(@"retainCount(str2)=%i",str2.retainCount); //結果:-1

? ? [str1 retain];

? ? NSLog(@"retainCount(str2)=%i",str2.retainCount); //結果:-1

? ? NSString *str2_1=[NSString stringWithString:[NSString stringWithFormat:@"Kaoru %@",@"sun"]];

? ? NSLog(@"retainCount(str2_1)=%i",str2_1.retainCount);//結果:2 [str2_1 release];? ? [str2_1 release];? ? ? ? ? ? ? ? NSString *str3=[NSString stringWithFormat:@"Rosa %@",@"Sun"];

? ? NSLog(@"retainCount(str3)=%i",str3.retainCount); //結果:1

? ? [str3 retain];

? ? NSLog(@"retainCount(str3)=%i",str3.retainCount); //結果:2

? ? [str3 release];

? ? [str3 release];


? ? NSString *str4=[NSString stringWithUTF8String:"Jack"];

? ? NSLog(@"retainCount(str4)=%i",str4.retainCount); //結果:1

? ? [str4 retain];

? ? NSLog(@"retainCount(str4)=%i",str4.retainCount); //結果:2

? ? [str4 release];

? ? [str4 release];


? ? NSString *str5=[NSString stringWithCString:"Tom" encoding:NSUTF8StringEncoding];

? ? NSLog(@"retainCount(str5)=%i",str5.retainCount); //結果:1

? ? [str5 retain];

? ? NSLog(@"retainCount(str5)=%i",str5.retainCount); //結果:2

? ? [str5 release];

? ? [str5 release];




? ? NSMutableString *str6=@"Jerry";

? ? NSLog(@"retainCount(str6)=%i",str6.retainCount); //結果:-1

? ? [str6 retain];

? ? NSLog(@"retainCount(str6)=%i",str6.retainCount); //結果:-1

? ? [str6 release];

? ? [str6 release];


? ? NSMutableArray *str7=[NSMutableString stringWithString:@"Lily"];

? ? NSLog(@"retainCount(str7)=%i",str7.retainCount); //結果:1

? ? [str7 retain];

? ? NSLog(@"retainCount(str7)=%i",str7.retainCount); //結果:2

? ? [str7 release];

? ? [str7 release];


? ? return 0;

}

看完上面的例子如果不了解NSString的處理你也許會有點奇怪(注意上面的代碼請在Xcode5下運行)尼夺?請看下面的解釋

str1是一個字符串常量,它存儲在常量區(qū),系統(tǒng)不會對它進行引用計數(shù)淤堵,因此無論是初始化還是做retain操作其引用計數(shù)器均為-1寝衫;

str3、str4拐邪、str5創(chuàng)建的對象同一般對象類似竞端,存儲在堆中,系統(tǒng)會對其進行引用計數(shù)庙睡;

采用stringWithString定義的變量有些特殊事富,當后面的字符串是字符串常量,則它本身就作為字符串常用量存儲(str2)乘陪,類似于str1统台;如果后面的參數(shù)是通過類似于str3、str4啡邑、str5的定義贱勃,那么它本身就是一個普通對象,只是后面的對象引用計數(shù)器默認為1谤逼,當給它賦值時會做一次拷貝操作(淺拷貝)贵扰,引用計數(shù)器加1,所有str2_1引用計數(shù)器為2流部;

str6其實和str1類似戚绕,雖然定義的是可變數(shù)組,但是它的本質還是字符串常量枝冀,事實上對于可變字符串只有為字符串常量時引用計數(shù)器才為-1舞丛,其他情況它的引用計數(shù)器跟一般對象完全一致;

后記:注意上面這段代碼的運行結果是在Xcode5中運行的結果果漾,事實上針對最新的Xcode6由于LLVM的優(yōu)化球切,只有str2_1和str7的引用計數(shù)器為1(str7 retain一次后第二次為2),其他均為-1绒障。

文件操作

在今天的最后一節(jié)內容中讓我們看一下Foundation中文件操作吨凑,下面將以一個例子進行說明:

//

//? main.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>/*目錄操作*/void test1(){

? ? //文件管理器是專門用于文件管理的類

? ? NSFileManager *manager=[NSFileManager defaultManager];


? ? //獲得當前程序所在目錄(當然可以改變)

? ? NSString *currentPath=[manager currentDirectoryPath];

? ? NSLog(@"current path is :%@",currentPath);

? ? //結果:/Users/kenshincui/Library/Developer/Xcode/DerivedData/FoundationFramework-awxjohcpgsqcpsanqofqogwbqgbx/Build/Products/Debug


? ? //創(chuàng)建目錄

? ? NSString *myPath=@"/Users/kenshincui/Desktop/myDocument";

? ? BOOL result= [manager createDirectoryAtPath:myPath withIntermediateDirectories:YES attributes:nil error:nil];

? ? if(result==NO){

? ? ? ? NSLog(@"Couldn't create directory!");

? ? }


? ? //目錄重命名,如果需要刪除目錄只要調用removeItemAtPath:<#(NSString *)#> error:<#(NSError **)#>

? ? NSError *error;

? ? NSString *newPath=@"/Users/kenshincui/Desktop/myNewDocument";

? ? if([manager moveItemAtPath:myPath toPath:newPath error:&error]==NO){

? ? ? ? NSLog(@"Rename directory failed!Error infomation is:%@",error);

? ? }


? ? //改變當前目錄

? ? if([manager changeCurrentDirectoryPath:newPath]==NO){

? ? ? ? NSLog(@"Change current directory failed!");

? ? }

? ? NSLog(@"current path is :%@",[manager currentDirectoryPath]);

? ? //結果:current path is :/Users/kenshincui/Desktop/myNewDocument


? ? //遍歷整個目錄

? ? NSString *path;

? ? NSDirectoryEnumerator *directoryEnumerator= [manager enumeratorAtPath:newPath];

? ? while (path=[directoryEnumerator nextObject]) {

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

? ? }

? ? /*結果:

? ? documents

? ? est.txt

? ? */


? ? //或者這樣遍歷

? ? NSArray *paths= [manager contentsOfDirectoryAtPath:newPath error:nil];

? ? NSObject *p;

? ? for (p in paths) {

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

? ? }

? ? /*結果:

? ? documents

? ? est.txt

? ? */}/*文件操作*/void test2(){

? ? NSFileManager *manager=[NSFileManager defaultManager];

? ? NSString *filePath=@"/Users/kenshincui/Desktop/myNewDocument/test.txt";

? ? NSString *filePath2=@"/Users/kenshincui/Desktop/test.txt";

? ? NSString *newPath=@"/Users/kenshincui/Desktop/myNewDocument/test2.txt";


? ? //判斷文件是否存在户辱,這個方法也可以判斷目錄是否存在鸵钝,這要后面的參數(shù)設置位YES

? ? if ([manager fileExistsAtPath:filePath isDirectory:NO]) {

? ? ? ? NSLog(@"File exists!");

? ? }


? ? //文件是否可讀

? ? if([manager isReadableFileAtPath:filePath]){

? ? ? ? NSLog(@"File is readable!");

? ? }


? ? //判斷兩個文件內容是否相等

? ? if ([manager contentsEqualAtPath:filePath andPath:filePath2]) {

? ? ? ? NSLog(@"file1 equals file2");

? ? }


? ? //文件重命名焕妙,方法類似于目錄重命名

? ? if (![manager moveItemAtPath:filePath toPath:newPath error:nil]) {

? ? ? ? NSLog(@"Rename file1 failed!");

? ? }


? ? //文件拷貝

? ? NSString *filePath3=@"/Users/kenshincui/Desktop/test3.txt";

? ? if(![manager copyItemAtPath:newPath toPath:filePath3 error:nil]){

? ? ? ? NSLog(@"Copy failed!");

? ? }


? ? //讀取文件屬性

? ? NSDictionary *attributes;

? ? if ((attributes=[manager attributesOfItemAtPath:newPath error:nil])==nil) {

? ? ? ? NSLog(@"Read attributes failed!");

? ? }

? ? for (NSObject *key in attributes) {

? ? ? ? NSLog(@"%@=%@",key,attributes[key]);

? ? }

? ? /*結果:

? ? ? ? NSFileOwnerAccountID=501

? ? ? ? NSFileHFSTypeCode=0

? ? ? ? NSFileSystemFileNumber=1781953

? ? ? ? NSFileExtensionHidden=0

? ? ? ? NSFileSystemNumber=16777218

? ? ? ? NSFileSize=27

? ? ? ? NSFileGroupOwnerAccountID=20

? ? ? ? NSFileOwnerAccountName=kenshincui

? ? ? ? NSFileCreationDate=2014-07-28 11:47:58 +0000

? ? ? ? NSFilePosixPermissions=420

? ? ? ? NSFileHFSCreatorCode=0

? ? ? ? NSFileType=NSFileTypeRegular

? ? ? ? NSFileExtendedAttributes={

? ? ? ? "com.apple.TextEncoding" = <7574662d 383b3133 34323137 393834>;

? ? ? ? }

? ? ? ? NSFileGroupOwnerAccountName=staff

? ? ? ? NSFileReferenceCount=1

? ? ? ? NSFileModificationDate=2014-07-28 11:47:58 +0000

? ? */


? ? //刪除文件

? ? [manager removeItemAtPath:newPath error:nil];


}//文件操作--文件內容操作(NSData蒋伦,非結構化字節(jié)流對象弓摘,有緩沖區(qū)管理機制焚鹊,可用于網絡傳輸)void test3(){

? ? NSFileManager *manager=[NSFileManager defaultManager];

? ? NSString *filePath=@"/Users/kenshincui/Desktop/myNewDocument/test2.txt";

? ? NSData *data=[manager contentsAtPath:filePath];

? ? NSLog(@"%@",data);//存儲的是二進制字節(jié)流

? ? //結果:<68656c6c 6f20776f 726c642c e4b896e7 958ce4bd a0e5a5bd efbc81>


? ? //NSData轉化成字符串

? ? NSString *str1=[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];

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

? ? //結果:hello world,世界你好!


? ? //字符串轉化成NSData

? ? NSString *str2=@"Kenshin";

? ? NSData *data2=[str2 dataUsingEncoding:NSUTF8StringEncoding];

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


? ? //當然一般如果僅僅是簡單讀取文件內容,直接用戶NSString方法即可

? ? NSString *content=[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];

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

? ? //結果:hello world,世界你好末患!

? ? }//文件操作--細粒度控制文件,文件操作柄void test4(){

? ? NSFileManager *manager=[NSFileManager defaultManager];

? ? NSString *filePath=@"/Users/kenshincui/Desktop/myNewDocument/test2.txt";


? ? //以只讀方式打開文件

? ? NSFileHandle *fileHandle=[NSFileHandle fileHandleForReadingAtPath:filePath];//注意這個方法返回類型為instancetype研叫,也就是說對于上面的NSFileHandle它的返回類型也是NSFileHandle

? ? NSData *data= [fileHandle readDataToEndOfFile];//完整讀取文件

? ? NSString *newPath=@"/Users/kenshincui/Desktop/test4.txt";

? ? [manager createFileAtPath:newPath contents:nil attributes:nil];

? ? NSFileHandle *fileHandle2=[NSFileHandle fileHandleForWritingAtPath:newPath];//以可寫方式打開文件

? ? [fileHandle2 writeData:data];//寫入文件內容


? ? [fileHandle2 closeFile];//關閉文件


? ? //定位到指定位置,默認在文件開頭

? ? [fileHandle seekToFileOffset:12];

? ? NSData *data2= [fileHandle readDataToEndOfFile];

? ? NSLog(@"data2=%@",[[NSString alloc]initWithData:data2 encoding:NSUTF8StringEncoding]);

? ? //結果:data2=世界你好!


? ? [fileHandle seekToFileOffset:6];

? ? NSData *data3=[fileHandle readDataOfLength:5];

? ? NSLog(@"data3=%@",[[NSString alloc]initWithData:data3 encoding:NSUTF8StringEncoding]);

? ? //結果:data3=world


? ? [fileHandle closeFile];


}//文件路徑void test5(){

? ? NSString *filePath=@"/Users/kenshincui/Desktop/myDocument";

? ? NSString *filePath2=@"/Users/kenshincui/Desktop/test.txt";

? ? //臨時文件所在目錄

? ? NSString *path=NSTemporaryDirectory();

? ? NSLog(@"temporary directory is :%@",path);

? ? //結果:/var/folders/h6/lss6gncs509c2pgzgty3wd_40000gn/T/

? ? NSString *lastComponent= [filePath lastPathComponent];

? ? NSLog(@"%@",lastComponent); //結果:myDocument


? ? NSLog(@"%@",[filePath stringByDeletingLastPathComponent]);

? ? //結果:/Users/kenshincui/Desktop

? ? NSLog(@"%@",[filePath stringByAppendingPathComponent:@"Pictrues"]);

? ? //結果:/Users/kenshincui/Desktop/myDocument/Pictrues

? ? NSLog(@"%@",[filePath2 pathExtension]);

? ? //結果:txt


? ? [[filePath pathComponents] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {

? ? ? ? NSLog(@"%i=%@",idx,obj);

? ? }];

? ? /*結果:

? ? 0=/

? ? 1=Users

? ? 2=kenshincui

? ? 3=Desktop

? ? 4=myDocument

? ? */


? ? }//文件操作--NSURLvoid test6(){

? ? NSURL *url=[NSURL URLWithString:@"http://developer.apple.com"];

? ? NSString *str1=[NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];

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

}//文件操作--NSBundle璧针,程序包,一般用于獲取Resource中的資源(當然由于當前并非IOS應用沒有程序包隧膏,只是表示當前程序運行路徑)

//在ios中經常用于讀取應用程序中的資源文件,如圖片、聲音构诚、視頻等void test7(){

? ? //在程序包所在目錄創(chuàng)建一個文件

? ? NSFileManager *manager=[NSFileManager defaultManager];

? ? NSString *currentPath=[manager currentDirectoryPath];

? ? NSLog(@"current path is :%@",currentPath);

? ? //結果:current path is :/Users/kenshincui/Library/Developer/Xcode/DerivedData/FoundationFramework-awxjohcpgsqcpsanqofqogwbqgbx/Build/Products/Debug

? ? NSString *filePath=[currentPath stringByAppendingPathComponent:@"test.txt"];

? ? [manager createFileAtPath:filePath contents:[@"Hello,world!" dataUsingEncoding:NSUTF8StringEncoding] attributes:nil];



? ? //利用NSBundle在程序包所在目錄查找對應的文件

? ? NSBundle *bundle=[NSBundle mainBundle];//主要操作程序包所在目錄

? ? //如果有test.txt則返回路徑盏阶,否則返回nil

? ? NSString *path=[bundle pathForResource:@"test" ofType:@"txt"];//也可以寫成:[bundle pathForResource:@"instructions.txt" ofType:nil];

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

? ? //結果:/Users/kenshincui/Library/Developer/Xcode/DerivedData/FoundationFramework-awxjohcpgsqcpsanqofqogwbqgbx/Build/Products/Debug/test.txt

? ? NSLog(@"%@",[NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]);

? ? //結果:Hello,world!


? ? //假設我們在程序運行創(chuàng)建一個Resources目錄,并且其中新建pic.jpg,那么用下面的方法獲得這個文件完整路徑

? ? NSString *path1= [bundle pathForResource:@"pic" ofType:@"jpg" inDirectory:@"Resources"];

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

? ? //結果:/Users/kenshincui/Library/Developer/Xcode/DerivedData/FoundationFramework-awxjohcpgsqcpsanqofqogwbqgbx/Build/Products/Debug/Resources/pic.jpg}int main(int argc,char *argv[]){

? ? test1();

? ? test2();

? ? test3();

? ? test4();

? ? test5();

? ? test6();

? ? test7();


? ? return 0;

}

歸檔

歸檔,在其他語言中又叫“序列化”抵栈,就是將對象保存到硬盤灿渴;解檔,在其他語言又叫“反序列化”就是將硬盤文件還原成對象栽烂。其實歸檔就是數(shù)據(jù)存儲的過程,在IOS中數(shù)據(jù)的存儲有五種方式:

xml屬性列表(plist歸檔)

NSUserDefaults(偏好設置)

NSKeyedArchiver歸檔(加密形式)

SQLite3(嵌入式數(shù)據(jù)庫)

Core Data(面向對象方式的嵌入式數(shù)據(jù)庫)

當然關于2恋脚、4腺办、5點不是我們今天介紹的重點,這個在IOS開發(fā)過程中我們會重點說到糟描。

xml屬性列表

首先我們先來看一下xml屬性列表怀喉,xml屬性列表進行歸檔的方式是將對象存儲在一個plist文件中,這個操作起來比較簡單船响,其實相當于xml序列化躬拢。但是同時它也有缺點:一是這種方式是明文保存的躲履;二是這種方式操作的對象有限,只有NSArray估灿、NSMutableArray、NSDictionary缤剧、NSMutableDictionary支持(歸檔時只要調用對應的writeToFile方法即可馅袁,解檔調用arrayWithContentsOfFile或dictionaryWithContentsOfFile,注意像NSString汗销、NSNumber、NSData即使有這個方法它存儲的也不是xml格式)弛针。

//

//? main.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>//xml屬性void test1(){

? ? //數(shù)組

? ? NSString *path=@"/Users/kenshincui/Desktop/arrayXml.plist";

? ? NSArray *array1=@[@"Kenshin",@"Kaoru",@"Rosa"];

? ? [array1 writeToFile:path atomically:YES];


? ? NSArray *array2=[NSArray arrayWithContentsOfFile:path];

? ? [array2 enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {

? ? ? ? NSLog(@"array2[%lu]=%@",idx,obj);

? ? }];

? ? /*結果:

? ? array1[0]=Kenshin

? ? array1[1]=Kaoru

? ? array1[2]=Rosa

? ? */



? ? //字典

? ? NSString *path2=@"/Users/kenshincui/Desktop/dicXml.plist";

? ? NSDictionary *dic1=@{@"name":@"Kenshin",@"age":@28,@"height":@172.5};

? ? [dic1 writeToFile:path2 atomically:YES];


? ? NSDictionary *dic2=[NSDictionary dictionaryWithContentsOfFile:path2];

? ? [dic2 enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {

? ? ? ? NSLog(@"dic2[%@]=%@",key,obj);

? ? }];

? ? /*結果:

? ? dic2[height]=172.5

? ? dic2[age]=28

? ? dic2[name]=Kenshin

? ? */}int main(int argc,char *argv[]){


? ? test1();


? ? return 0;

}

生成的文件如下

NSKeyedArchiver歸檔

如果要針對更多對象歸檔或者需要歸檔時能夠加密的話就需要使用NSKeyedArchiver進行歸檔和解檔,使用這種方式歸檔的范圍更廣而且歸檔內容是密文存儲。從歸檔范圍來講NSKeyedArchiver適合所有ObjC對象哪亿,但是對于自定義對象我們需要實現(xiàn)NSCoding協(xié)議粥烁;從歸檔方式來講NSKeyedArchiver分為簡單歸檔和復雜對象歸檔,簡單歸檔就是針對單個對象可以直接將對象作為根對象(不用設置key)蝇棉,復雜對象就是針對多個對象讨阻,存儲時不同對象需要設置不同的Key。

首先看一下系統(tǒng)對象兩種歸檔方式(注意由于本章主要介紹Foundation內容篡殷,下面的程序是OS X命令行程序并沒有創(chuàng)建成iOS應用变勇,如果移植到到iOS應用下運行將NSArchiver和NSUnarchiver換成NSKeyedArchiver和NSKeyedUnarchiver。雖然在Foundation部分iOS和OS X在設計上盡可能通用但是還存在著細微差別贴唇。)

//

//? main.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>//系統(tǒng)對象簡單歸檔void test1(){

? ? //NSString歸檔

? ? NSString *str1=@"Hello,world!";

? ? NSString *path1=@"/Users/kenshincui/Desktop/archiver1.arc";

? ? if(![NSArchiver archiveRootObject:str1 toFile:path1]){

? ? ? ? NSLog(@"archiver failed!");

? ? }

? ? //NSString解檔

? ? NSString *str2= [NSUnarchiver unarchiveObjectWithFile:path1];

? ? NSLog(@"str2=%@",str2);//結果:str2=Hello,world!



? ? //NSArray歸檔

? ? NSString *path2=@"/Users/kenshincui/Desktop/archiver2.arc";

? ? NSArray *array1=@[@"Kenshin",@"Kaoru",@"Rosa"];

? ? if(![NSArchiver archiveRootObject:array1 toFile:path2]){

? ? ? ? NSLog(@"archiver failed!");

? ? }

? ? //NSArray解檔

? ? NSArray *array2=[NSUnarchiver unarchiveObjectWithFile:path2];

? ? [array2 enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {

? ? ? ? NSLog(@"array2[%lu]=%@",idx,obj);

? ? }];

? ? /*結果:

? ? array2[0]=Kenshin

? ? array2[1]=Kaoru

? ? array2[2]=Rosa

? ? */}//系統(tǒng)復雜對象歸檔(多對象歸檔)void test2(){

? ? /*歸檔*/

? ? NSString *path1=@"/Users/kenshincui/Desktop/archiver3.arc";


? ? int int1=89;

? ? CGSize size1={12.5,16.8};

? ? NSNumber *number1=@60.5;

? ? NSString *str1=@"Hello,world!";

? ? NSArray *array1=@[@"Kenshin",@"Kaoru",@"Rosa"];

? ? NSDictionary *dic1=@{@"name":@"Kenshin",@"age":@28,@"height":@172.5};


? ? //同時對多個對象進行歸檔

? ? NSMutableData *data1=[[NSMutableData alloc]init];//定義一個NSMutableData用于臨時存放數(shù)據(jù)

? ? NSKeyedArchiver *archiver=[[NSKeyedArchiver alloc]initForWritingWithMutableData:data1];//定義歸檔對象

? ? [archiver encodeInt:int1 forKey:@"int"];//對int1歸檔并指定一個key以便以后讀取

? ? [archiver encodeSize:size1 forKey:@"size"];

? ? [archiver encodeObject:number1 forKey:@"number"];

? ? [archiver encodeObject:str1 forKey:@"string"];

? ? [archiver encodeObject:array1 forKey:@"array"];

? ? [archiver encodeObject:dic1 forKey:@"dic"];

? ? [archiver finishEncoding];//結束歸檔


? ? [data1 writeToFile:path1 atomically:YES];//寫入文件




? ? /*解檔*/

? ? int int2;

? ? CGSize size2;

? ? NSNumber *number2;

? ? NSString *str2;

? ? NSArray *array2;

? ? NSDictionary *dic2;


? ? NSData *data2=[[NSData alloc]initWithContentsOfFile:path1];//讀出數(shù)據(jù)到NSData

? ? NSKeyedUnarchiver *unarchiver=[[NSKeyedUnarchiver alloc]initForReadingWithData:data2];


? ? int2= [unarchiver decodeInt64ForKey:@"int"];

? ? size2=[unarchiver decodeSizeForKey:@"size"];

? ? number2=[unarchiver decodeObjectForKey:@"number"];

? ? str2=[unarchiver decodeObjectForKey:@"string"];

? ? array2=[unarchiver decodeObjectForKey:@"array"];

? ? dic2=[unarchiver decodeObjectForKey:@"dic"];


? ? [unarchiver finishDecoding];


? ? NSLog(@"int2=%i,size=%@,number2=%@,str2=%@,array2=%@,dic2=%@",int2,NSStringFromSize(size2),number2,str2,array2,dic2);

? ? /*結果:

? ? int2=89,

? ? size={12.5, 16.800000000000001},

? ? number2=60.5,

? ? str2=Hello,world!,

? ? array2=(

? ? ? ? Kenshin,

? ? ? ? Kaoru,

? ? ? ? Rosa

? ? ),

? ? dic2={

? ? ? ? age = 28;

? ? ? ? height = "172.5";

? ? ? ? name = Kenshin;

? ? }

? ? */}int main(int argc,char *argv[]){

? ? test1();

? ? test2();


? ? return 0;

}

接下來看一下自定義的對象如何歸檔搀绣,上面說了如果要對自定義對象進行歸檔那么這個對象必須實現(xiàn)NSCoding協(xié)議,在這個協(xié)議中有兩個方法都必須實現(xiàn):

-(void)encodeWithCoder:(NSCoder *)aCoder;通過給定的Archiver對消息接收者進行編碼戳气;

-(id)initWithCoder:(NSCoder *)aDecoder;從一個給定的Unarchiver的數(shù)據(jù)返回一個初始化對象链患;

這兩個方法分別在歸檔和解檔時調用。下面通過一個例子進行演示(注意對于自定義類的多對象歸檔與系統(tǒng)類多對象歸檔完全一樣瓶您,代碼中不再演示):

Person.h

//

//? Person.h

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>@interface Person : NSObject<NSCoding>

@property (nonatomic,copy) NSString *name;

@property (nonatomic,assign) int age;

@property (nonatomic,assign) float height;

@property (nonatomic,assign) NSDate *birthday;

@end

Person.m

//

//? Person.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import "Person.h"@implementation Person#pragma mark 解碼

-(id)initWithCoder:(NSCoder *)aDecoder{

? ? NSLog(@"decode...");

? ? if (self=[super init]) {

? ? ? ? self.name=[aDecoder decodeObjectForKey:@"name"];

? ? ? ? self.age=[aDecoder decodeInt64ForKey:@"age"];

? ? ? ? self.height=[aDecoder decodeFloatForKey:@"heiht"];

? ? ? ? self.birthday=[aDecoder decodeObjectForKey:@"birthday"];

? ? }

? ? return self;

}#pragma mark 編碼

-(void)encodeWithCoder:(NSCoder *)aCoder{

? ? NSLog(@"encode...");

? ? [aCoder encodeObject:_name forKey:@"name"];

? ? [aCoder encodeInt64:_age forKey:@"age" ];

? ? [aCoder encodeFloat:_height forKey:@"height"];

? ? [aCoder encodeObject:_birthday forKey:@"birthday"];

}#pragma mark 重寫描述

-(NSString *)description{

? ? NSDateFormatter *formater1=[[NSDateFormatter alloc]init];

? ? formater1.dateFormat=@"yyyy-MM-dd";

? ? return [NSString stringWithFormat:@"name=%@,age=%i,height=%.2f,birthday=%@",_name,_age,_height,[formater1 stringFromDate:_birthday]];

}

@end

main.m

//

//? main.m

//? FoundationFramework

//

//? Created by Kenshin Cui on 14-2-16.

//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.

//#import <Foundation/Foundation.h>#import "Person.h"int main(int argc,char *argv[]){

? ? //歸檔

? ? Person *person1=[[Person alloc]init];

? ? person1.name=@"Kenshin";

? ? person1.age=28;

? ? person1.height=1.72;

? ? NSDateFormatter *formater1=[[NSDateFormatter alloc]init];

? ? formater1.dateFormat=@"yyyy-MM-dd";

? ? person1.birthday=[formater1 dateFromString:@"1986-08-08"];


? ? NSString *path1=@"/Users/kenshincui/Desktop/person1.arc";


? ? [NSKeyedArchiver archiveRootObject:person1 toFile:path1];

? ? //解檔

? ? Person *person2= [NSKeyedUnarchiver unarchiveObjectWithFile:path1];

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

? ? /*結果:

? ? name=Kenshin,age=28,height=0.00,birthday=1986-08-08

? ? */


? ? return 0;

}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末麻捻,一起剝皮案震驚了整個濱河市纲仍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌贸毕,老刑警劉巖郑叠,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異明棍,居然都是意外死亡乡革,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門摊腋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來沸版,“玉大人,你說我怎么就攤上這事兴蒸∈恿福” “怎么了?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵橙凳,是天一觀的道長蕾殴。 經常有香客問我,道長岛啸,這世上最難降的妖魔是什么区宇? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮值戳,結果婚禮上议谷,老公的妹妹穿的比我還像新娘。我一直安慰自己堕虹,他們只是感情好卧晓,可當我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著赴捞,像睡著了一般逼裆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上赦政,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天胜宇,我揣著相機與錄音,去河邊找鬼恢着。 笑死桐愉,一個胖子當著我的面吹牛,可吹牛的內容都是我干的掰派。 我是一名探鬼主播从诲,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼靡羡!你這毒婦竟也來了系洛?” 一聲冷哼從身側響起俊性,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎描扯,沒想到半個月后定页,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡绽诚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年典徊,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片憔购。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡宫峦,死狀恐怖岔帽,靈堂內的尸體忽然破棺而出玫鸟,到底是詐尸還是另有隱情,我是刑警寧澤犀勒,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布屎飘,位于F島的核電站,受9級特大地震影響贾费,放射性物質發(fā)生泄漏钦购。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一褂萧、第九天 我趴在偏房一處隱蔽的房頂上張望押桃。 院中可真熱鬧,春花似錦导犹、人聲如沸唱凯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽磕昼。三九已至,卻和暖如春节猿,著一層夾襖步出監(jiān)牢的瞬間票从,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工滨嘱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留峰鄙,地道東北人。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓太雨,卻偏偏與公主長得像先馆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子躺彬,可洞房花燭夜當晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內容