OC中的NSFoundation框架

        OC基础学习阶段,除了OC基本语法的学习,很长一段时间要学习NSFoundation。话说,冰冻三尺非一日之寒,对这块知识的学习确实需要时间一点点积累,不可急于求成。        

        其实学习任何语言的道理都一样,首先要学习语言的基础语法,了解了这种语言语法的独特之处。之后就要学习针对这种语言开发出来的框架,正如牛顿曾讲过的,“我正是站在巨人的肩膀上,才能有现在的成就”。如果什么都从基础做起,那搞IT的岂不是都要学习怎么写机器码了,说不定搞硬件都要去中国大西北的稀土矿中挖矿去了。所以在软件开发中能够很好的利用框架的作用,显然可以降低你开发的难度,提高开发的质量。因为这些框架毕竟都是经过千锤百炼出来的。学习cocoa的Foundation框架中的类,最好的途径就是查阅apple的开发文档,这些东西都是靠平时用来积累的,并不是你看了几篇文章就能熟练掌握其中的用法的,所以我对这些类的常见的用法就不加说明,只是针对某些类那种用法容易出错,来一一说明,

NSString 、NSMutableString类

    cocoa把字符串处理封装到NSString类中了,这个类提供了字符串处理的常用方法,详细请见apple develop document。在这里你要了解到NSString类的那些特征那,下面请看一段代码:

    NSString *temp =[[NSString alloc] initWithString:@"test"];
    //NSLog(@"%d",[temp retainCount]);
    [temp release];

    //NSLog(@"%d",[temp retainCount]);

    NSMutableString *str=[[NSMutableString alloc] initWithString:temp];
    [str appendFormat:@"NSString"];
    NSLog(@"%@",str);

你猜NSLog会输出什么,是我们想要的testNSString么?有兴趣的可以在xcode中创建一个控制台程序,把上面的代码复制进去,可以看到确实是我们预期的结果。学习过objective c内存管理的肯定就会有疑问了,temp已经释放了,为什么str中还能和temp中的字符串拼接那?

为了揭开NSString这个类的秘密,首先把代码中被注释的那两句话,在程序中一同运行。那么retainCount又是什么那?引用计数,这个你不会没有听过吧,那你就要再好好学习下objective c的内存管理了。程序再次运行时,你会发现控制台中会输出两个-1,也就是alloc和release根本就没有起到任何作用,难道NSString类不适用于objective c的内存管理么?显然不是,原因就在于NSString类中的字符串都定义在常量区,常量区并不是程序员为对象在内存中申请的空间,而是编译起给一些变量分配的空间,所以说你根本无法取得它的引用计数,因为这个字符串的分配,根本不受你的控制,完全是编译起去控制的。所以你看到的都是-1。那么如果给NSMutableString类型的str取retainCount那答案会是什么哪?有兴趣的可以实验下,应该是1。因为NSMutableString虽说它初始化的字符串在常量区,但是NSMutalbeString不像NSString简单的存取字符串的信息,它还要存取字符串的容量以及一些其他信息供字符串扩展,所以自然和NSString就不太一样了。具体NSMutableString是怎么实现的那就要问问apple的工程师了。

NSArray NSMutableString类

    首先还是先看段代码,眼见为实,代码可以说明一切。假如你有两个类Person和Animal那么你就可以写如下的代码,来访问你定义再array对象中的实例了:

    Person *aMan = [[Person alloc] init];

    Animal *dog   = [[Animal alloc] init];

    NSArray *aryMammal = [[NSArray arrayWithObjects:aMan,dog]];

    [aMan release];

    [dog release];

    for(id someThing for aryMammal)

    {

         NSLog(@"%@", [someThing getName]];  //假如说你定义的类有这个方法可以获得对象的名字

    }

按照我们学过的objective c内存管理的理论分析的话,程序会在for循环中,抛出一个不能识别的selector发向一个实例的异常。但是你可以运行一下,程序非常的正常,没有任何的异常。这又是什么原因哪?先不急着看下面的分析,你可以用上面的策略retainCount来看下对象aMan和dog的变化。之后你就很容易发现问题在哪里了。下面还是为一些不愿动手的同志,分析一下结果吧。如果照上面所说的在NSArray后面用retainCount来获取aMan和dog的的引用计数的,你会发现这两个实例的retainCount都变成了2,自然release之后对象还是没有销毁的。所以由这里可以推测出我们向一个容器里面放对象的时候,对象的引用计数会自动加1,apple工程师这样设计就可以大大减少程序崩溃的可能性。

    下面来看下NSMutableArray的一些特征。当你想要遍历一个NSMutableArray的时候,然后找到某个特殊的对象,用另外一个对象替代这个对象的时候,代码如下:

   int i = 0;

    for(id temp in mutableAry)

   {

        if([temp equal:aObject])

            [mutableAry replaceObjectAtIndex:i withObject:aObject];

        i++;

   }

这样的代码可以运行么?答案是只要你的程序进到了if语句里面,下次执行for循环的时候程序就会崩溃,报出的错误是bad access,在之后的博客里我会和大家一块分享下,xcode程序运行中的常见错误。下面就来分析下这种错误的原因吧。 用for(id temp in mutableArray)的时候,切记不能在for循环中为mutableArray添加任何东西,因为for循环在开始的时候,会把array中的所有数据都放到一个stack中,stack中存着array中对象的指针。如果你改变了array中的对象,原来对象的指针就销毁了,而stack中仍然存着这个指针,访问的时候程序会bad access从而crash。

    下面再来看下NSArray的一个特殊的用法,就是在数组中放置dictionary的时候,数组同样可以用valueForKey的方法来访问dictionary中的value。代码演示如下:

    NSDictionary *dic1 = [NSDictionary dictionaryWithObjectsAndKeys:@"1",@"sky",nil];
    NSDictionary *dic2 = [NSDictionary dictionaryWithObjectsAndKeys:@"2",@"zhiwei",nil];

    NSDictionary *dic3 = [NSDictionary dictionaryWithObjectsAndKeys:@"1",@"mengtnt",nil];
    NSMutableArray *ary = [[NSMutableArray alloc] init];
    [ary insertObject:dic1 atIndex:0];
    [ary insertObject:dic2 atIndex:0];
    [ary insertObject:dic3 atIndex:0];
    NSString *str = [[ary valueForKey:@"1"] objectAtIndex:1];
    NSLog(@"%@",str);

在从array中取得dictionary的value的时候,切记要指定index因为数组中的dictionary可能有好多key为1的数据,它会根据index位置获得正确的数据,如果上面的程序这样写NSString *str = [[ary valueForKey:@"1"] objectAtIndex:0];那么str中应该存取的就是mengtnt,而如果objectAtIndex:2 那结果会输出一个Null。默认情况下如果在相应的位置没有这个dictionary的key,编译器会自动给这个位置加一个NSNull的对象,大家可不要搞混了,NSNull也是个对象,也是继承自NSObject,千万不能把它当作nil处理。只不过apple为了在集合类中表示空对象而定义的一个类,因为不可能在集合中随便插入nil对象。这样就可以避免程序随便的进入nil对象,而造成程序异常。。

 首先来看下一个操作文件以及目录的重要类NSFileManager。下面还是看一段代码,来一一说明此类的用法

    NSFileManager *fm;
    NSDirectoryEnumerator *dirEnum;
    NSArray *dirArray;
     // 需要创建一个file manager的实例
    fm = [NSFileManager defaultManager];
    
    // 获取当前工作目录的路径
    path = [fm currentDirectoryPath];
    
    // 遍历这个目录
    dirEnum = [fm enumeratorAtPath: path];

    NSLog(@"Contents of %@:", path);

    while ((path = [dirEnum nextObject]) != nil)

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

    }

    // 另一种遍历目录的方法
    
    dirArray = [fm contentsOfDirectoryAtPath: [fm currentDirectoryPath] error: nil];

    NSLog(@"Contents using contentsOfDirectoryAtPath:");

    for (path in dirArray)

        NSLog(@"%@", path);

相信仔细看了这些代码之后,会对NSFileManager这个类如何遍历目录,有了一定的认识吧。其实这些代码都非常简单,只要查看apple developer文档都能找到的。虽说理解起来简单,但是有时候一动手写代码还是会遇到很多问题的。例如下面

        NSArray *aryProvsioning = [FileManager contentsOfDirectoryAtPath:@"/vault/provisioning" error:nil];
        for(NSString* provisioningFile in aryProvsioning)
        {
            NSLog(@"delete file:%@",provisioningFile);
            [FileManager removeItemAtPath:[NSString stringWithFormat:@"/vault/provisioning/%@",provisioningFile]
                                    error:nil];
        }

这段代码的功能是删除/vault/provisioning下的所有文件,有兴趣的可以亲自实验以下,确实可以删除该文件夹下面的所有文件。但是有种情况不行,就是该文件夹下又存在文件夹的情况下,该方法removeitemAtPath就不能删除,也就是该方法只能删除文件不能删除目录,所以在用的时候要格外小心,一定要认真阅读开发文档。所以上面如果想要删除该文件夹下面的所有文件及目录,就要写成函数来递归调用。

void deleteAllAtPath(NSString *path)

{

        NSArray *aryProvsioning = [FileManager contentsOfDirectoryAtPath:path error:nil];

        if (aryProvsioning == nil)

            return;
        for(NSString* provisioningFile in aryProvsioning)
        {
            deleteAllAtPath(provisioningFile);

            NSLog(@"delete file:%@",provisioningFile);
            [FileManager removeItemAtPath:[NSString stringWithFormat:@"/vault/provisioning/%@",provisioningFile]
                                    error:nil];
        }

}

    下面再来看一个读写文件的类NSFileHandle,其实它的实现就是调用c语言种文件流的操作。通过取得文件流的指针,然后定位指针的位置从而读取出文件中的内容,同样在文件中写入东西也是同样的道理,下面来看一小短代码。

    NSFileHandle *TXTFileHandle;

    NSString    *szTXTFileTemp = @"111.txt";
    TXTFileHandle = [NSFileHandle fileHandleForWritingAtPath:szTXTFileTemp];

    //防止该文件不存在,如果不存在,新建该文件并写入空数据。
    if(!TXTFileHandle)
    {
        [@"" writeToFile:szTXTFileTemp atomically:NO encoding:1 error:nil];
        TXTFileHandle = [NSFileHandle fileHandleForWritingAtPath:szTXTFileTemp];
    }

    [TXTFileHandle seekToEndOfFile];

    char * conTemp = "mengtnt";

    [TXTFileHandle  writeData:[NSData dataWithBytes:conTemp length:8];

    [TXTFileHandle closeFile];

    //下面是读取

    TXTFileHandle = [NSFileHandle fileHandleForReadingAtPath:szTXTFileTemp];

    NSData *fileData = [TXTFileHandle readDataToEndOfFile];

    NSlog(@"%@",[[NSString alloc] initWithData:fileData  encoding:NSUTF8StringEncoding]);

    [TXTFileHandle closeFile];

  以上就是cocoa编程中文件操作的常见用法,下面再看两个类,虽说里面的方法都很简单,但是不注意一些细节还是会出问题的。

    NSThread类,开启一个线程的方法在简单不过了,[NSThread detachNewThreadSelector:@selector(StartThreadTest:) toTarget:self withObject:nil];其中selector就是你要调用的线程方法。

- (void)StartThreadTest:(id)idThread
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    //一些UI事件以及其他事件处理
    [pool release];
}

不知道大家注意了没有,为什么线程中要加release pool那。特别是线程中有处理UI事件的时候一定要加上pool,否则你会发现应用程序运行异常的慢,甚至有的时候会crash掉。可能是线程的资源在使用时,很难判断何时使用完毕,所以编译器也就不方便自动释放这些资源,这样就要手动加上pool,使线程运行到此处的时候释放掉某些资源。究其深层的原因,自己也是一知半解。这里仅仅是自己的一些想法,还望那位高手能指点下分析的是否正确。

    最后跟大家提以下NSTimer的用法,NSTimer说白了就是一个定时器,就是用来每隔一段时间运行某段代码,    [NSTimer scheduledTimerWithTimeInterval:0.2
                                             target:self
                                           selector:@selector(DetectSN:)
                                           userInfo:nil
                                            repeats:YES];

当你相停止timer的时候就调用invalide方法。需要注意的一点是当NSTimer被设置无效的时候,要想使用这个timer对象,必须重新开辟一个NSTimer的对象。

    那么关于NSFoundation一些类的用法就先介绍到这里吧,这些都是自己平时整理一些资料,这些类都是比较容易用到,同时也容易出错的类。希望能给大家一些帮助,同时在调试中也提高了自己的能力。以后我就来简单的分享下自己cocoa编程中ApplicationKit框架的一些简单用法,不过我一直在UI设计方面存在先天不足,也希望在介绍ApplicationKit框架,各位老鸟看到我设计的一些UI的时候,能给点建设性的意见,以让自己摆脱这方面的苦恼。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值