Objective-C Foundation框架全解 – Objective-C开发教程

上一章Objective-C开发教程请查看:Objective-C复合对象解析?
从之前的章节我们已经学完所有的Objective-C语言基础了,后面开始进入高级开发的内容,包括运行时、内存管理等。
这一章主要讨论Foundation框架,说到Foundation框架,又不得不说一下Cocoa和Cocoa Touch。首先这两个都是工具集,前者用于macOS开发,后者用于iOS开发,这两个工具集有一些相同的框架,Foundation框架就是其中之一,另外Core Data也是。
另外,macOS的UI开发使用的框架是AppKit,而iOS开发使用的UI框架是UIKit。
Foundation框架是OC编程的基本框架,除了提供OOP的基本特性之外,主要还提供了:

  • 一些列基本工具类,以及一些常用的数据结构。
  • 支持Unicode字符串、对象持久化和对象分发。
  • 日期和时间处理。
  • 异常和错误处理。
  • 文件处理。
  • URL加载系统。
当然还有其它功能,其它的后面还是详细讨论。顺便说下,Foundation框架提供的数据结构太少了,起码再提供个堆(heap)也好。
下面我们详细介绍以上提供的功能,其中一般来说,使用指定的类,只需要导入指定的头文件即可,例如使用NSString,那么就导入Foundation/NSString.h头文件,但是为了避免导入太多的类,直接导入Foundation/Foundation.h头文件即可。
数据结构Foundation框架中提供的数据结构也就是两种:数组和散列表,数组又包括NSArray和NSMutableArray,散列表包括NSDictionary和NSMutableDictionary,以及NSSet和NSMutableSet。
使用的时候要注意其时间复杂度,否则有可能影响APP运行效率,关于数据结构和算法更多内容,可以在srcmini中搜索获取完整的教程。

NSArray和NSMutableArray
NSArray和它的子类NSMutableArray都是有序数组,前者是静态数组,元素不可变,后者是动态数组,元素可变。因而一般来说,NSArray是直接初始化,然后使用,除非有特殊的更改元素的需求(有提供方法),否则数组都是静态不变的。而NSMutableArray在之后的使用中可能会频繁更改元素,建议使用的时候考虑元素可变和不可变的因素。
初始化NSArray可以使用initWithObjects这样的静态方法,但是更简单的方式是使用数组字面量,如下:
NSArray *array = @[someObject, @"Hello, World!", @42];

@操作符将后面的东西转为对象,这也是常见的一种用法。
方法数组元素可以使用objectAtIndex:,更简单的方式是使用下标访问法:
id value = http://www.srcmini.com/array[3];

下面是NSArray主要提供的功能说明:
  • 创建一个数组:使用静态方法,如array,arrayWtihArray,arrayWithObject等。
  • 初始化数组:init,initWithObjects等。
  • 数组查询:containsObject,objectAtIndex,ObjectAtIndexes等。
  • 查找元素索引:indexOfObject,indexOfObject:inRange等。
  • 给元素发送消息(每个元素执行指定操作):makeObjectsPerformSelector等。
  • 比较数组:isEqualToArray等。
  • 派生新的数组:如arrayByAddingObject。
  • 数组排序:如sortedArrayUsingFunction:context。
NSMutableArray是NSArray的子类,所以以上对NSMutableArray也适用。NSMutableArray额外提供对元素操作的功能:
  • removeAllObjects ——清空数组。
  • addObject——在数组的末尾插入一个给定的对象。
  • removeObjectAtIndex—用于删除特定索引中的对象
  • exchangeObjectAtIndex:withObjectAtIndex – 交换数组中的对象。
  • replaceObjectAtIndex:withObject – 用对象替换索引处的对象。
下面是一个使用示例:
#import < Foundation/Foundation.h>int main() { NSArray *array = [[NSArray alloc] initWithObjects:@"string1", @"string2",@"string3",nil]; NSString *string1 = [array objectAtIndex:0]; NSLog(@"数组中下标0处的对象为 %@",string1); NSMutableArray *mutableArray = [[NSMutableArray alloc]init]; [mutableArray addObject: @"string"]; string1 = [mutableArray objectAtIndex:0]; NSLog(@"mutableArray中下标0处的对象为 %@",string1); return 0; }

另外最新OC提供了泛型编程,可以在数组后面添加< Type>指定集合中的元素类型,类似于Java中的泛型。
遍历数组建议使用for-in循环,比block效率好。
NSDictionary和NSMutableDictionary
类似于上面的数组,字典也分可变和不可变两种,字典的元素的无序的,但是所有操作的时间效率都是最好的,当面向的数据不需要顺序的时候,优先使用字典。
字典中的每个元素为key-value键值对,使用方法初始化字典有点麻烦,如dictionaryWithObjects:forKeys:count,类似数组字面量使用@[],字典字面量使用@{},如:
NSDictionary *dictionary = @{ @"anObject" : someObject, @"helloString" : @"Hello, World!", @"magicNumber" : @42, @"aValue" : someValue };

要注意,字典中的key关键字也是一个对象,通常使用的是NSString对象。
在数组中访问一个元素可以使用下标,如array[1]。在字典中访问元素可以使用objectForKey方法,同样也可以使用简单的方式:
id value = http://www.srcmini.com/dictionary[@"helloString"];

字典遍历同样建议使用for-in循环,其中每个元素为key。
NSDictionary的重要方法如下:
  • alloc/initWithObjectsAndKeys——使用从指定的值和键集构造的条目初始化新分配的字典。
  • valueForKey——返回与给定键关联的值。
  • count——返回字典中的条目数。
NSMutableDictionary的重要方法如下:
  • removeAllObjects——清空其词条的字典。
  • removeObjectForKey——从字典中删除给定的键及其关联值。
  • setValue:forKey——将给定的键值对添加到字典中。
下面是字典的一个简单例子:
#import < Foundation/Foundation.h>int main() { NSDictionary *dictionary = [[NSDictionary alloc] initWithObjectsAndKeys: @"string1",@"key1", @"string2",@"key2",@"string3",@"key3",nil]; NSString *string1 = [dictionary objectForKey:@"key1"]; NSLog(@"key的对象,字典中的key1是 %@",string1); NSMutableDictionary *mutableDictionary = [[NSMutableDictionary alloc]init]; [mutableDictionary setValue:@"string" forKey:@"key1"]; string1 = [mutableDictionary objectForKey:@"key1"]; NSLog(@"key的对象,mutableDictionary 中的key1是 %@",string1); [pool drain]; return 0; }

NSSet和NSMutableSet
集合和字典是类似的,同样是基于散列,也是无序的,只不过不需要额外提供键值,并且集合中的元素唯一。
NSSet的重要方法如下:
  • alloc/initWithObjects——用指定对象列表中的成员初始化一个新分配的集合。
  • allObjects——返回一个包含集合成员的数组,如果集合没有成员,则返回一个空数组。
  • count——返回集合中成员的数目。
NSMutableSet继承自NSSet,因此NSSet的所有实例方法在NSMutableSet中都是可用的。
NSMutableSet的重要方法如下:
  • removeAllObjects——清空其所有成员的集合。
  • addObject – 如果该对象还不是集合的一个成员,则添加到集合。
  • removeObject——从集合中移除给定的对象。
下面是集合的一个简单例子:
#import < Foundation/Foundation.h>int main() { NSSet *set = [[NSSet alloc] initWithObjects:@"string1", @"string2",@"string3",nil]; NSArray *setArray = [set allObjects]; NSLog(@"集合对象 %@",setArray); NSMutableSet *mutableSet = [[NSMutableSet alloc]init]; [mutableSet addObject:@"string1"]; setArray = [mutableSet allObjects]; NSLog(@"mutableSet的对象 %@",setArray); return 0; }

文本和字符串Foundation支持创建和处理Unicode字符字符串,使用正则表达式查找模式,并执行文本的自然语言分析。
在这一部分,Foundation提供的功能主要是以字符为基本单位进行相关的操作,提供的主要类如下:
字符串
  • NSString:一个静态的纯文本Unicode字符串对象。
  • NSMutableString:一个动态的纯文本Unicode字符串对象。
字符串和元数据
  • NSAttributedString:具有与其文本部分相关属性(如视觉样式、超链接或可访问性数据)的字符串。
  • NSMutableAttributedString:一个可变的字符串对象,它还包含与其文本内容的各个部分相关联的属性(如可视样式、超链接或可访问性数据)。
字符
  • NSCharacterSet:表示用于搜索操作的一组固定的Unicode字符值的对象。
  • NSMutableCharacterSet:表示用于搜索操作的一组可变Unicode字符值的对象。
  • unichar:类型为UTF-16代码单元。
自然语言处理
  • NSLinguisticTagger:分析自然语言文本,标记部分语音和词汇类,识别名称,进行词化,确定语言和脚本。
模式匹配
  • NSScanner:一种字符串解析器,用于扫描字符集中的子字符串或字符,以及十进制、十六进制和浮点表示法中的数值。
  • NSRegularExpression:应用于Unicode字符串的已编译正则表达式的不可变表示。
本地化
  • NSLocale:有关用于格式化数据以供表示的语言、文化和技术约定的信息。
  • NSLocalizedStringFromTable:返回指定表中字符串的本地化版本。
时间和日期NSDate和NSDateFormatter类提供了日期和时间的特性。
NSDateFormatter是一个帮助类,它支持将NSDate轻松地转换为NSString,反之亦然。
下面是一个简单的示例,演示如何将NSDate转换为NSString并返回到NSDate。
#import < Foundation/Foundation.h>int main() { NSDate *date= [NSDate date]; NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init]; [dateFormatter setDateFormat:@"yyyy-MM-dd"]; NSString *dateString = [dateFormatter stringFromDate:date]; NSLog(@"Current date is %@",dateString); NSDate *newDate = [dateFormatter dateFromString:dateString]; NSLog(@"NewDate: %@",newDate); return 0; }

正如我们在上面的程序中看到的,我们在NSDate的帮助下得到了当前的时间。
NSDateFormatter是负责转换格式的类。
可以根据可用的数据更改日期格式。例如,当我们想要在上面的例子中添加时间时,日期格式可以更改为@“yyyy-MM-dd:hh:mm:ss”
异常处理异常处理在Objective-C中是可用的,带有基础类NSException。
异常处理通过以下块实现:
  • @try — 这个块尝试执行一组语句。
  • @catch — 这个块试图在try块中捕获异常。
  • @finally — 这个块包含一组总是执行的语句。
#import < Foundation/Foundation.h>int main() { NSMutableArray *array = [[NSMutableArray alloc]init]; @try{ NSString *string = [array objectAtIndex:10]; } @catch (NSException *exception) { NSLog(@"%@ ",exception.name); NSLog(@"Reason: %@ ",exception.reason); }@finally{ NSLog(@"@@finaly Always Executes"); } return 0; }

文件处理文件处理在NSFileManager类的帮助下可用。这些例子不适用于在线编译器。
用于文件处理的方法
用于访问和操作文件的方法列表如下所示。在这里,我们必须将FilePath1、FilePath2和FilePath字符串替换为所需的完整文件路径,以获得所需的操作。
// 1、检查某个路径上是否存在文件 NSFileManager *fileManager = [NSFileManager defaultManager]; // 获取文件目录 NSArray *directoryPaths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectoryPath = [directoryPaths objectAtIndex:0]; if ([fileManager fileExistsAtPath:@""] == YES) { NSLog(@"File exists"); }// 2、比较两个文件内容 if ([fileManager contentsEqualAtPath:@"FilePath1" andPath:@" FilePath2"]) { NSLog(@"Same content"); }// 3、检查是否可写、可读和可执行 if ([fileManager isWritableFileAtPath:@"FilePath"]) { NSLog(@"isWritable"); }if ([fileManager isReadableFileAtPath:@"FilePath"]) { NSLog(@"isReadable"); }if ( [fileManager isExecutableFileAtPath:@"FilePath"]) { NSLog(@"is Executable"); }// 4、移动文件 if([fileManager moveItemAtPath:@"FilePath1" toPath:@"FilePath2" error:NULL]) { NSLog(@"Moved successfully"); }// 5、复制文件 if ([fileManager copyItemAtPath:@"FilePath1" toPath:@"FilePath2"error:NULL]) { NSLog(@"Copied successfully"); }// 6、删除文件 if ([fileManager removeItemAtPath:@"FilePath" error:NULL]) { NSLog(@"Removed successfully"); }// 7、读取文件 NSData *data = http://www.srcmini.com/[fileManager contentsAtPath:@"Path"]; // 8、写入文件 [fileManager createFileAtPath:@"" contents:data attributes:nil];

URL加载系统在访问URL时,URL加载是很有用的,从互联网上获得的信息。它在下列各类的帮助下得到:
  • NSMutableURLRequest
  • NSURLConnection
  • NSURLCache
  • NSURLAuthenticationChallenge
  • NSURLCredential
  • NSURLProtectionSpace
  • NSURLResponse
  • NSURLDownload
  • NSURLSession
下面是一个简单的url加载示例,这不能在命令行上运行,我们需要创建Cocoa应用程序。
这可以通过在XCode中选择New,然后在出现的窗口的OS X应用程序部分中Project和select Cocoa Application来实现。
通过单击next完成步骤的顺序,你将被要求提供一个项目名称,你可以给它一个名称。
h头文件如下所示:
#import < Cocoa/Cocoa.h>@interface AppDelegate : NSObject < NSApplicationDelegate>@property (assign) IBOutlet NSWindow *window; @end

AppDelegate.m更新文件如下:
#import "AppDelegate.h"@interface SampleClass:NSObject< NSURLConnectionDelegate> { NSMutableData *_responseData; }- (void)initiateURLConnection; @end@implementation SampleClass - (void)initiateURLConnection {// 创建请求. NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://date.jsontest.com"]]; // 创建url连接和fire请求 NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self]; [conn start]; }#pragma mark NSURLConnection Delegate Methods - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { // 已收到响应,这是我们初始化你创建的实例变量的地方 // 我们可以在didReceiveData方法中向它添加数据 // 而且,每次重定向时都要调用此方法,以便重新初始化它 // 也可以用来清理 _responseData = [[NSMutableData alloc] init]; }- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { // 将新数据附加到已声明的实例变量 [_responseData appendData:data]; }- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse*)cachedResponse { // 返回nil,表示不需要为此连接存储缓存的响应 return nil; }- (void)connectionDidFinishLoading:(NSURLConnection *)connection { // 请求已经完成,数据已经收到 // 现在可以解析实例变量中的内容了 NSLog(@"%@",[[NSString alloc]initWithData:_responseData encoding:NSUTF8StringEncoding]); }- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { // 由于某种原因,请求失败了! // 检查error } @end@implementation AppDelegate - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { SampleClass *sampleClass = [[SampleClass alloc]init]; [sampleClass initiateURLConnection]; // 在这里插入代码来初始化你的应用程序 } @end

【Objective-C Foundation框架全解 – Objective-C开发教程】在上面的程序中,我们创建了一个简单的URL连接,它是JSON格式的时间,并显示时间。

    推荐阅读