OC foudation框架(上)学习

foundation框架

文章目录

字符串(NSString && NSMutableString)

功能:

  1. 创建字符串:可以用init也可以string开头的类方法最后是@""直接给字符串赋值
  2. 读取文件或网络URL初始化
  3. 将字符串的内容写入文件或URL
  4. 获取字符串长度,既可以获取字符串内包括的字符个数,也可以获取字符串包括的字节个数
  5. 连接,分割,查找字串或字符,替换字符,比较字符串,比较字符串大小,大小写转化

下面的代码给出了我们的三种不同的创建方式。

objectivec 复制代码
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        unichar data[6] = {97, 98, 99, 100, 101, 102};
        NSString* str = [[NSString alloc] initWithCharacters:data length:6];
        NSLog(@"%@", str);
        char* str1 = "Hello world";
        NSString* str2 = [NSString stringWithUTF8String:str1];
        NSLog(@"%@", str2);
        [str2 writeToFile:@"myfile.txt" atomically:YES encoding:NSUTF8StringEncoding error:nil];
        NSString* str3 = [NSString stringWithContentsOfFile:@"NSString.m"encoding:NSUTF8StringEncoding error:nil];
        NSLog(@"%@", str3);
    }
    return 0;
}

打印结果如下:(这里我们也会同时创建一个文件)

NSString的其他功能

objectivec 复制代码
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSString* str = @"hello";
        NSString* book = @"iOS";
        str = [str stringByAppendingString:@"iOS!"];
        NSLog(@"%@", str);
        const char* cstr = [str UTF8String];
        NSLog(@"%s", cstr);
        str = [str stringByAppendingFormat: @"%@是一本不错的书", book];
        NSLog(@"%@", str);
        NSLog(@"个数为%lu", [str length]);
        NSLog(@"按照UTF-8解码后的字节个数为%lu", [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
        NSString* s1 = [str substringToIndex:10];
        NSLog(@"%@", s1);
        NSString* s2 = [str substringFromIndex: 5];
        NSLog(@"%@", s2);
        NSString* s3 = [str substringWithRange: NSMakeRange(5, 10)];
        NSLog(@"%@", s3);
        NSRange pos = [str rangeOfString:@"iOS"];
        NSLog(@"iOS出现的位置%ld, 长度%ld", pos.location, pos.length);
        str = [str uppercaseString];
        NSLog(@"%@", str);
    }
    return 0;
}

上面的代码运用了一个NSRange类型的变量,这个是一个结构体而不是一个类,其包括了包括了location和length两个unsigned int整型值,分别代表起始位置和长度。

结果为:

在修改字符串的时候,由于NSString字符串不可改变,因此实际上原来的字符串对象并不改变,只是将新生成的字符串重新赋值给原来的指针变量。

str1 = [str1 stringByAppendingString: @",iOS!"];

NSMutableString

NSString字符串是不可变的字符串,即一旦NSString对象被创建,其中的字符序列就不能更改了。而NSMutableString字符串就不一样了,它的字符串序列是可更改的。而且NSMutableString是NSString的子类,NSString的方法,NSMutableString都可以直接使用。

objectivec 复制代码
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSString* book = @"疯狂iOS讲义";
        NSMutableString* str1 = [NSMutableString stringWithString: @"Hello"];
        [str1 appendString:@"iOS"];
        NSLog(@"%@", str1);
        [str1 appendFormat:@"%@,是一本很不错的书籍", book];
        NSLog(@"%@", str1);
        [str1 insertString:@"fkit.org" atIndex:6];
        NSLog(@"%@", str1);
        [str1 deleteCharactersInRange:NSMakeRange(6, 12)];
        NSLog(@"%@", str1);
        [str1 replaceCharactersInRange:NSMakeRange(6, 12) withString:@"Objective-C"];
        NSLog(@"%@", str1);
    }
    return 0;
}

程序运行结果:

而在NSMutableString的代码中,由于NSMutableString的字符串是可变的,因此修改字符串的时候,字符串所包含的字符序列本身就发生了改变,因此无需重新赋值。

日期与时间 (NSDate)

2.1 日期与时间(NSDate)

OC提供了NSDate、NSCalendar对象。

其中,NSDate对象代表日期与时间,OC既提供了类方法来创建NSDate对象,也提供了大量init开头的方法来初始化NSDate对象。创建NSDate的类方法和实例方法基本相似,只是类方法以date开头,实例方法以init开头。

objectivec 复制代码
#import <Foundation/Foundation.h>
 
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSDate *date1 = [NSDate date]; //获取当前时间
        NSLog(@"%@",date1);
        NSDate *date2 = [[NSDate alloc] initWithTimeIntervalSinceNow:3600 * 24];//获取从当前时间开始的后一天的时间
        NSLog(@"%@",date2);
        NSDate *date3 = [[NSDate alloc] initWithTimeIntervalSinceNow:-3 * 3600 * 24];//获取从现在开始三天前的时间
        NSLog(@"%@",date3);
        NSDate *date4 = [[NSDate alloc] initWithTimeIntervalSince1970:3600 * 24 * 366 * 20];//获取从1970年1月1日开始往后20年的时间
        NSLog(@"%@",date4);
        NSLocale *cn = [NSLocale currentLocale];//NSLocale代表一个语言,这里表示中文
        NSLog(@"%@",[date1 descriptionWithLocale: cn]);//用中文输出date1的时间
        NSDate *earlier = [date1 earlierDate: date2];
        NSLog(@"%@",earlier);//获取两个时间中较早的时间
        NSDate *later = [date1 laterDate: date2];
        NSLog(@"%@",later);//获取两个时间中较晚的时间
        //比较两个日期用:compare:方法,它包括如下三个值
        //三个值分别代表调用compare的日期位于被比较日期之前、相同、之后
        switch([date1 compare: date3]) {
            case NSOrderedAscending: 
            		NSLog(@"date1在date3之前");
                break;
            case NSOrderedSame: 
            		NSLog(@"date1和date3时间想相同");
                break;
            case NSOrderedDescending: 
            		NSLog(@"date1在date3时间之后");
                break;
        }
        NSLog(@"date1和date3的时间差是%g秒",[date1 timeIntervalSinceDate: date3]);//获取两个时间的时间差
        NSLog(@"date2与现在的时间差%g秒",[date2 timeIntervalSinceNow]);//获取指定时间和现在的时间差
    }
    return 0;
}

这里就表现出了创建NSDate的方法,一种是类方法,一种是实例方法,我们重点记忆类方法以date开头,实例方法以init开头这一句话

2.2日期格式器

NSDateFormatter代表一个日期格式器,它的作用就是完成NSDate和NSString之间的转换。在进行转换时,我们首先需要创建一个NSDateFormatter对象,然后调用该对象的setDateStyle:、setTimeStyle:方法设置格式化日期、时间的风格。其中日期、时间风格支持以下几个枚举值:

  • NSDateFormatterNoStyle: 不显示日期,时间的风格
  • NSDateFormatterShortStyle:显示短日期,时间风格。
  • NSDateFormatterMediumStyle:显示中等日期,时间风格。
  • NSDateFormatterLongStyle:显示长日期,时间风格
  • NSDateFormatterFullStyle:显示完整日期,时间风格

如果打算用自己的模版就调用setDateFormate:方法来设置日期,时间的模版

如果需要将NSDate转化为NSString,就是stringFromDate:相反则就是调用dateFromString:

objectivec 复制代码
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSDate* date = [NSDate dateWithTimeIntervalSince1970:3600 * 24 * 366 * 20];
        NSLocale* locals[] = {[[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"], [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]};
        NSDateFormatter* df[8];
        for (int i = 0; i < 2; i++) {
            df[i * 4] = [[NSDateFormatter alloc] init];
            [df[i * 4] setDateStyle: NSDateFormatterShortStyle];
            [df[i * 4] setDateStyle: NSDateFormatterShortStyle];
            [df[i * 4] setLocale: locals[i]];
            df[i * 4 + 1] = [[NSDateFormatter alloc] init];
            [df[i * 4 + 1] setDateStyle: NSDateFormatterMediumStyle];
            [df[i * 4 + 1] setDateStyle: NSDateFormatterMediumStyle];
            [df[i * 4 + 1] setLocale: locals[i]];
            df[i * 4 + 2] = [[NSDateFormatter alloc] init];
            [df[i * 4 + 2] setDateStyle: NSDateFormatterLongStyle];
            [df[i * 4 + 2] setDateStyle: NSDateFormatterLongStyle];
            [df[i * 4 + 2] setLocale: locals[i]];
            df[i * 4 + 3] = [[NSDateFormatter alloc] init];
            [df[i * 4 + 3] setDateStyle: NSDateFormatterFullStyle];
            [df[i * 4 + 3] setDateStyle: NSDateFormatterFullStyle];
            [df[i * 4 + 3] setLocale: locals[i]];
        }
        for (int i = 0; i < 2; i++) {
            switch (i) {
                case 0:
                    NSLog(@"中国各式");
                    break;
                case 1:
                    NSLog(@"美国格式");
                    break;
           

            }
            NSLog(@"SHORT: %@", [df[i * 4] stringFromDate: date]);
            NSLog(@"MEDIUM: %@", [df[i * 4 + 1] stringFromDate: date]);
            NSLog(@"LONG: %@", [df[i * 4 + 2] stringFromDate: date]);
            NSLog(@"FULL: %@", [df[i * 4 + 3] stringFromDate: date]);
        }
        NSDateFormatter* df2 = [[NSDateFormatter alloc] init];
        [df2 setDateFormat:@"公元yyyy年MM月DD日HH时mm分"];
        NSLog(@"%@", [df2 stringFromDate:date]);
        NSString* datestr = @"2013-03-02";
        NSDateFormatter* df3 = [[NSDateFormatter alloc] init];
        [df3 setDateFormat:@"yyyy-MM-dd"];
        NSData* date2 = [df3 dateFromString: datestr];
        NSLog(@"%@", date2);
        
    }
    return 0;
}

日历与日期组件

为了方便分开处理NSDate所包含的三个数据:年份,月份,日期。OC引入了NSCalendar对象。该方法包含如下两个常用方法:

  • (NSDateComponents*)components:formDate:从NSDate提取年,月,日,时,分,秒各时间字段的信息。
  • dateFromComponents:(NSDateComponents*) comps:使用comps对象包含的年,月,分,秒的各时间字段的信息。

NSDateComponents对象,该对象封装年月日时分秒各时间字段的信息。

我们从NSDate对象中分开获取各时间字段的数值的步骤如下:

  • 创建NSCalendar对象
  • 调用NSCalendar的方法来获取NSDate对象中各时间字段的数值,该方法返回一个NSDateComponents对象。
  • 调用NSDateComponents对象的getter方法。

很容易理解如果我们通过各个时间段的数值来初始化NSDate对象的步骤与上面差别不大,我们只需要把getter方法改成setter方法。

objectivec 复制代码
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSCalendar* gregorian = [[NSCalendar alloc] initWithCalendarIdentifier: NSCalendarIdentifierGregorian];
        NSDate* dt = [NSDate date];
        unsigned unitFlags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond | NSCalendarUnitWeekday;
        NSDateComponents* comp=[gregorian components: unitFlags fromDate:dt];
        NSLog(@"%ld年", comp.year);
        NSLog(@"%ld月", comp.month);
        NSLog(@"%ld日", comp.day);
        NSLog(@"%ld分", comp.minute);
        NSLog(@"%ld秒", comp.second);
        NSDateComponents* comp2 = [[NSDateComponents alloc] init];
        comp2.year = 2024;
        comp2.month = 5;
        comp2.day = 6;
        comp.hour = 18;
        comp.minute = 34;
        NSDate* date = [gregorian dateFromComponents: comp2];
        NSLog(@"%@", date);
    }
    return 0;
}

这部分是一个从NSDate对象中分开获取各时间字段的数值的方式,以及通过各项数值重新获取到我们的NSDate数据。

定时器(NSTimer)

当程序需要让某个方法重复执行,可以借助OC中的定时器来完成。

通过调用NSTimer的scheduledTimerWithTimeInterval: invocation: repeats:或scheduledTimerWithTimeInterval: targe:selector: userInfo: repeats:类方法来创建NSTimer对象。调用该方法时需要传入以下参数:

1、timeInterval:指定每隔多少秒执行一次任务

2、invocation或target与selector:指定重复执行的任务。如果指定target和selector参数,则指定用某个对象的特定方法作为重复执行的任务;如果指定invocation参数,该参数需要传入一个NSInvocation对象,该对象也是封装target和selector的,其实也是指定用某个对象的特定方法作为重复执行的任务。

3、userInfo:该参数用于传入额外的附加信息。

4、repeats:该参数需要指定一个BOOL值,该参数控制是否需要重复执行任务。

笔者这里还不会使用,之后会进行一个补充。

对象复制

copy与mutabCopy方法

  • copy方法返回的是对象不可修改的副本,即使该对象本身是可修改的。

  • mutableCopy方法用于复制对象的可变副本。通常来说,mutableCopy方法总是返回该对象可修改的副本,即使被复制的对象本身是不可修改的。

无论如何,copy和mutabCopy返回的总是原对象的副本,当程序对于复制的副本进行修改的时候,原对象通常不会受到影响。

objectivec 复制代码
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSMutableString* book = [NSMutableString stringWithString:@"疯狂iOS讲义"];
        NSMutableString* bookCopy = [book mutableCopy];
        [bookCopy replaceCharactersInRange:NSMakeRange(2, 3) withString:@"Android"]; // range中第一个是替换的位置,第二个是替换的长度
        NSLog(@"book的值为:%@", book);
        NSLog(@"bookCopy为:%@", bookCopy);
        NSString* str = @"fkit";
        NSMutableString* strCopy = [str mutableCopy];
        [strCopy appendString:@".org"];
        NSLog(@"%@", strCopy);
        NSMutableString* bookCopy2 = [book copy];//程序将返回一个不可修改的副本
      	[bookCopy2 appendString:@"aa"]//这段代码因为我们的bookCopy方法是不可变副本所以会报错。
    }
    return 0;
}

这是上面代码的一个结果:

tips : 副本的可不可变和他的复制方式有关,而与他的复制对象无关

NSCopying 与 NSMutableCopy 协议

objectivec 复制代码
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        FKDog* dog1 = [[FKDog alloc] init];
        dog1.name = [NSMutableString stringWithString:@"旺财"];
        dog1.age = 20;
        FKDog* dog2 = [dog1 copy];
    }
    return 0;
}

这段代码可以通过编译但是却不能够运行,所以我们为了保证一个对象可以调用copy和mutableCopy方法,我们就要通过实现它的NSCopying协议和重写copyWithZone 方法。在这里我们调用了allocWithZone因为这个方法可以指定新对象分配内存的位置,如果没有指定分配内存的位置,allocWithZone 方法就会使用默认的分配器来分配内存。

objectivec 复制代码
@implementation FKDog
- (id)copyWithZone:(NSZone *)zone {
    NSLog(@"执行copy");
    FKDog* dog = [[[self class] allocWithZone:zone] init];
    dog.name = self.name;
    dog.age = self.age;
    return dog;
}
- (NSString*) description {
    return [NSString stringWithFormat:@"<dog[name = %@, age = %d]>", self.name, self.age];
}
@end

然后我们在主函数增加一条语句:dog2.name = [NSMutableString stringWithString:@"snoopy"];结果为:

按照我们的一个常理来说,我们的copy方法按照要求是不可以修改的,但是这里却可以修改,这是因为我们没有给我们的FKDog类提供相应的不可变类,如果提供了不可变类,当然还是应该让FKDog的copyWithZone:返回不可变的FKDog对象。

需要指出的是,如果重写copyWithZone:方法时,他的父类也是已经实现了NSCopying协议,并且重写该方法,那么子类重写该方法的时候应该先调用父类的copy方法复制从父类继承的到成员变量,然后对子类中定义的成员变量进行赋值。

objectivec 复制代码
- (id) copyWithZone:(NSZone*)zone {
  id obj = [super copy];
  //对子类定义的成员变量赋值
  return obj;
}

深拷贝与浅拷贝

首先我们来看一下我们的代码来帮助我们理解这部分内容。

objectivec 复制代码
#import <Foundation/Foundation.h>
#import "FKDog.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        FKDog* dog1 = [[FKDog alloc] init];
        dog1.name = [NSMutableString stringWithString:@"旺财"];
        dog1.age = 20;
        FKDog* dog2 = [dog1 copy];
        NSLog(@"%@", dog1);
        [dog2.name replaceCharactersInRange:NSMakeRange(0, 2) withString:@"anocpy"];
        NSLog(@"%@", dog2);
        NSLog(@"%@", dog1);
    }
    return 0;
}

打印结果如下:

这里发现我们同时修改了dog1,dog2两个对象的name属性值,这里的主要问题就是在于我们自己写的**copyWithZone:**方法。dog.name = self.name这个语句实际上是将两个指针指向了同一块内存。

原因是:FKDog的对象的name是一个指针变量,该变量中村方法的只是字符串的地址,而不是字符串本身,所以当我们的对象的实例变量是指针变量时候,如果程序只是赋值该指针的地址,而不是真正复制指针所指向的对象,这种方式就被称为一个"浅拷贝"

而深拷贝则不一样,它不仅会复制对象本身,而且会递归的复制每一个指针变量的实例变量,直到两个对象没有任何公用的部分。我们只需要修改一条语句就可以了dog.name = [self.name mutableCopy];dog.name = dog,name两条语句替换就可以实现一个深拷贝

一般来说,深拷贝的实现难度会比较大,所以Foudation框架中的类大部分只实现了浅拷贝。

setter方法的复制选项

在之前的学习中我们学习了copy这个指示符,该指示符就是指定当程序调用setter方法复制时,实际上是将传入的参数的副本赋值给程序的实例变量。

我们实现一个我们的接口部分的代码,用一个copy指示符来定义一个我们的属性。

objectivec 复制代码
@interface FKItem : NSObject
@property (nonatomic, copy) NSMutableString* name;
@end

主函数部分:

objectivec 复制代码
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        FKItem* item = [[FKItem alloc] init];
        item.name = [NSMutableString stringWithString:@"疯狂iOS讲义"];
        [item.name appendString:@"fkit"]; //此处代码会报错。
    }
    return 0;
}

这里我们的代码会报错,但是我们定义的一个接口部分的代码定义的明明是NSMutableString 这个可变类型,但是为什么我们会出现无法复制的原因是我们的setName方法的缘故。

objectivec 复制代码
- (void) setName: (NSMutableString*) aname {
  name = [aname copy];
}

这个部分我们的copy方法默认是复制该对象的不可变副本,虽然设置的是一个NSMutableString但是实际上我们得到的却是不可变字符串。

相关推荐
pe7er3 天前
macOS 应用无法打开(权限问题)解决方案
macos·mac
西岸行者5 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意5 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码5 天前
嵌入式学习路线
学习
毛小茛5 天前
计算机系统概论——校验码
学习
babe小鑫5 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms5 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下5 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。5 天前
2026.2.25监控学习
学习
im_AMBER5 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode