IOS开发学习笔记Day3-OC基础三
NSArray
常用方法
//创建的两种方式:
// 数组中的nil是结束符
NSArray *arr = [[NSArray alloc] init];
NSArray *arr = [NSArray arrayWithObject:@"lnj"];
NSArray *arr = [NSArray arrayWithObjects:@"lnj", @"lmj" ,@"jjj", @"cp", nil];
NSLog(@"%@", arr);
//常用方法:
Person *p = [Person new];
NSObject *obj = [NSObject new];
NSArray *arr = [NSArray arrayWithObjects:p, obj, @"lnj", nil];
NSLog(@"arr = %@", arr);
NSLog(@"count = %lu", [arr count]);
NSLog(@"last = %@", [arr lastObject]);
NSLog(@"first = %@", [arr firstObject]);
NSLog(@"arr[1] = %@", [arr objectAtIndex:1]);
if([arr containsObject:@"zs"]) {
NSLog(@"arr中包含zs");
}else {
NSLog(@"arr中不包含zs");
}
// 创建数组简写
// NSArray *arr = [NSArray arrayWithObjects:@"lnj", @"lmj", @"jjj", nil];
NSArray *arr = @[@"lnj", @"lmj", @"jjj"];
// 获取数组元素的简写
NSLog(@"%@", [arr objectAtIndex:0]);
NSLog(@"%@", arr[0]);
数组的遍历
NSArray *arr= @[@"lnj", @"lmj", @"jjj"];
/*
// 常规遍历
for (int i = 0; i < arr.count; ++i) {
NSLog(@"arr[%i] = %@", i, arr[i]);
}
*/
// 如果是OC数组可以使用OC中的增强for循环来遍历
// 逐个取出arr中的元素, 将取出的元素赋值给obj
// 注意: obj的类型可以根据数组中元素的类型来写, 不一定要写NSObject
for (NSString *obj in arr) {
NSLog(@"obj = %@", obj);
}
/*
// 使用OC数组的迭代器来遍历
// 每取出一个元素就会调用一次block
// 每次调用block都会将当前取出的元素和元素对应的索引传递给我们
// obj就是当前取出的元素, idx就是当前元素对应的索引
// stop用于控制什么时候停止遍历
[arr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if (idx == 1) {
*stop = YES;
}
NSLog(@"obj = %@, idx = %lu", obj, idx);
}];
*/
Person *p1 = [Person new];
Person *p2 = [Person new];
Person *p3 = [Person new];
Person *p4 = [Person new];
NSArray *arr = @[p1, p2, p3, p4];
/*
[arr enumerateObjectsUsingBlock:^(Person *obj, NSUInteger idx, BOOL *stop) {
[obj say];
}];
*/
// 如果使用OC数组存储对象, 可以调用OC数组的方法让数组中所有的元素都执行指定的方法
// 注意点: 如果数组中保存的不是相同类型的数据, 并且没有相同的方法, 那么会报错
// [arr makeObjectsPerformSelector:@selector(say)];
// withObject: 需要传递给调用方法的参数
[arr makeObjectsPerformSelector:@selector(sayWithName:) withObject:@"lnj"];
数组的排序
/*
NSArray *arr = @[@10, @20, @5, @7, @15];
NSLog(@"排序前: %@", arr);
// 注意: 想使用compare方法对数组中的元素进行排序, 那么数组中的元素必须是Foundation框架中的对象, 也就是说不能是自定义对象
NSArray *newArr = [arr sortedArrayUsingSelector:@selector(compare:)];
NSLog(@"排序后: %@", newArr);
*/
Person *p1 = [Person new];
p1.age = 10;
Person *p2 = [Person new];
p2.age = 20;
Person *p3 = [Person new];
p3.age = 5;
Person *p4 = [Person new];
p4.age = 7;
NSArray *arr = @[p1, p2, p3, p4];
NSLog(@"排序前: %@", arr);
// 按照人的年龄进行排序
// 不能使用compare:方法对自定义对象进行排序
// NSArray *newArr = [arr sortedArrayUsingSelector:@selector(compare:)];
// 该方法默认会按照升序排序
NSArray *newArr = [arr sortedArrayWithOptions:NSSortStable usingComparator:^NSComparisonResult(Person *obj1, Person *obj2) {
// 每次调用该block都会取出数组中的两个元素给我们
// NSLog(@"obj1 = %@, obj2 = %@", obj1, obj2);
return obj1.age > obj2.age;
// return obj1.age < obj2.age;
/*
if (obj1.age > obj2.age) {
// 5 4
return NSOrderedDescending;
}else if(obj1.age < obj2.age) {
// 4 5
return NSOrderedAscending;
}else {
return NSOrderedSame;
}
*/
}];
NSLog(@"排序后: %@", newArr);
array和String之间的转换
/*
NSArray *arr = @[@10, @20, @5, @7, @15];
NSLog(@"排序前: %@", arr);
// 注意: 想使用compare方法对数组中的元素进行排序, 那么数组中的元素必须是Foundation框架中的对象, 也就是说不能是自定义对象
NSArray *newArr = [arr sortedArrayUsingSelector:@selector(compare:)];
NSLog(@"排序后: %@", newArr);
*/
Person *p1 = [Person new];
p1.age = 10;
Person *p2 = [Person new];
p2.age = 20;
Person *p3 = [Person new];
p3.age = 5;
Person *p4 = [Person new];
p4.age = 7;
NSArray *arr = @[p1, p2, p3, p4];
NSLog(@"排序前: %@", arr);
// 按照人的年龄进行排序
// 不能使用compare:方法对自定义对象进行排序
// NSArray *newArr = [arr sortedArrayUsingSelector:@selector(compare:)];
// 该方法默认会按照升序排序
NSArray *newArr = [arr sortedArrayWithOptions:NSSortStable usingComparator:^NSComparisonResult(Person *obj1, Person *obj2) {
// 每次调用该block都会取出数组中的两个元素给我们
// 二分
// NSLog(@"obj1 = %@, obj2 = %@", obj1, obj2);
return obj1.age > obj2.age;
// return obj1.age < obj2.age;
/*
if (obj1.age > obj2.age) {
// 5 4
return NSOrderedDescending;
}else if(obj1.age < obj2.age){
// 4 5
return NSOrderedAscending;
}else{
return NSOrderedSame;
}
*/
}];
NSLog(@"排序后: %@", newArr);
将数组写入到文件中
// 1.将数组写入到文件中
/*
NSArray *arr = @[@"lnj", @"lmj", @"jjj"];
// 其实如果将一个数组写入到文件中之后, 本质是写入了一个XML文件
// 在iOS开发中一般情况下我们会将XML文件的扩展名保存为plist
BOOL flag = [arr writeToFile:@"/Users/xiaomage/Desktop/abc.plist" atomically:YES];
NSLog(@"flag = %i", flag);
*/
Person *p1 = [Person new];
p1.age = 10;
Person *p2 = [Person new];
p2.age = 20;
Person *p3 = [Person new];
p3.age = 5;
Person *p4 = [Person new];
p4.age = 7;
NSArray *arr = @[p1, p2, p3, p4];
// 注意:writeToFile只能写入数组中保存的元素都是Foundation框架中的类创建的对象, 如果保存的是自定义对象那么不能写入
BOOL flag = [arr writeToFile:@"/Users/xiaomage/Desktop/person.plist" atomically:YES];
NSLog(@"flag = %i", flag);
// 2.从文件中读取一个数组
/*
NSArray *newArray = [NSArray arrayWithContentsOfFile:@"/Users/xiaomage/Desktop/abc.plist"];
NSLog(@"%@", newArray);
*/
NSMutableArray(可变数组)
// 创建一个空的数组
NSMutableArray *arrM = [NSMutableArray array];
NSLog(@"%@", arrM);
// 如何添加
[arrM addObject:@"lnj"];
// 将指定数组中的元素都取出来, 放到arrM中
// 并不是将整个数组作为一个元素添加到arrM中
[arrM addObjectsFromArray:@[@"lmj", @"jjj"]];
// 注意: 以下是将整个数组作为一个元素添加
// [arrM addObject:@[@"lmj", @"jjj"]];
NSLog(@"%@", arrM);
// 如何插入
[arrM insertObject:@"xcq" atIndex:1];
NSLog(@"%@", arrM);
NSRange range = NSMakeRange(2, 2);
NSIndexSet *set = [NSIndexSet indexSetWithIndexesInRange:range];
// 插入一组数据, 指定数组需要插入的位置, 和插入多少个
[arrM insertObjects:@[@"A", @"B"] atIndexes:set];
NSLog(@"%@", arrM);
/*
// 如何删除
[arrM removeObjectAtIndex:0];
NSLog(@"%@", arrM);
[arrM removeLastObject];
NSLog(@"%@", arrM);
[arrM removeObject:@"A"];
NSLog(@"%@", arrM);
// 如何替换
[arrM replaceObjectAtIndex:1 withObject:@"M"];
NSLog(@"%@", arrM);
// 如何获取
NSLog(@"%@", [arrM objectAtIndex:0]);
NSLog(@"%@", arrM[0]);
// 替换
arrM[0] = @"ZS";
NSLog(@"%@", arrM);
// 注意: 不能通过@[]来创建一个可变数组, 因为@[]创建出来的是一个不可变的数组
// 如果把一个不可变数组当做一个可变数组来使用, 会引发一个运行时的错误
NSMutableArray *arrM = @[@"lnj", @"lmj"];
[arrM addObject:@"JJJ"];*/
NSDictionary(字典)
// 1.如何创建
// NSDictionary *dict = [NSDictionary dictionaryWithObject:@"lnj" forKey:@"name"];
// NSString *name = [dict objectForKey:@"name"];
// NSLog(@"name = %@", name);
// 注意: key和value 是一一对应
// NSDictionary *dict = [NSDictionary dictionaryWithObjects:@[@"lnj", @"30", @"1.75"] forKeys:@[@"name", @"age", @"height"]];
// NSLog(@"%@ %@ %@", [dict objectForKey:@"name"], [dict objectForKey:@"age"], [dict objectForKey:@"height"]);
// NSDictionary *dict = @{key:value};
// NSDictionary *dict = @{@"name": @"lnj"};
// NSLog(@"%@", dict[@"name"]);
// NSDictionary *dict = @{@"name":@"lnj", @"age":@"30", @"height":@"1.75"};
// NSLog(@"%@ %@ %@", dict[@"name"], dict[@"age"], dict[@"height"]);
// 2.字典的遍历
// NSDictionary *dict = @{@"name":@"lnj", @"age":@"30", @"height":@"1.75"};
// 2.1如何获取字典中key和value的个数, 在字典中key称之为键, value称之为值
// NSLog(@"count = %lu", [dict count]);
/*
for (int i = 0; i < dict.count; ++i) {
// 获取字典中所有的key
NSArray *keys = [dict allKeys];
// 取出当前位置对应的key
// NSLog(@"%@", keys[i]);
NSString *key = keys[i];
NSString *value = dict[key];
NSLog(@"key = %@, value = %@", key, value);
}
*/
/*
// 如何通过forin遍历字典, 会将所有的key赋值给前面的obj
for (NSString *key in dict) {
// NSLog(@"%@", key);
NSString *value = dict[key];
NSLog(@"key = %@, value = %@", key, value);
}
*/
/*
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
NSLog(@"key = %@, value = %@", key, obj);
}];
*/
// 3.字典文件读写
NSDictionary *dict = @{@"name":@"lnj", @"age":@"30", @"height":@"1.75"};
// XML 扩展名plist
[dict writeToFile:@"/Users/xiaomage/Desktop/info.plist" atomically:YES];
// 注意: 字典和数组不同, 字典中保存的数据是无序的
NSDictionary *newDict = [NSDictionary dictionaryWithContentsOfFile:@"/Users/xiaomage/Desktop/info.plist"];
NSLog(@"%@", newDict);
NSArray *arr = @[@10, @20, @30, @5];
[arr writeToFile:@"/Users/xiaomage/Desktop/abc.plist" atomically:YES];
可变字典
/*
// 1.创建一个空的字典
NSMutableDictionary *dictM = [NSMutableDictionary dictionary];
NSLog(@"%@", dictM);
// 2.如何添加
[dictM setObject:@"lnj" forKey:@"name"];
NSLog(@"%@", dictM);
// 会将传入字典中所有的键值对取出来添加到dictM中
[dictM setValuesForKeysWithDictionary:@{@"age":@"30", @"height":@"1.75"}];
NSLog(@"%@", dictM);
// 3.如何获取
NSLog(@"name = %@", dictM[@"name"]);
// 4.如何删除
[dictM removeObjectForKey:@"name"];
NSLog(@"%@", dictM);
// [dictM removeObjectsForKeys:@[@"age", @"height"]];
// NSLog(@"%@", dictM);
// 5.如何修改
// 如果利用setObject方法给同名的key赋值, 那么新值会覆盖旧值
// [dictM setObject:@"88" forKey:@"age"];
dictM[@"age"] = @"88";
NSLog(@"%@", dictM);
*/
// 1.不能使用@{}来创建一个可变的字典
// NSMutableDictionary *dictM = @{@"name":@"lnj"};
// [dictM setObject:@"30" forKey:@"age"];
// 2.如果是不可变数组, 那么key不能相同
// 如果是不可变字典出现了同名的key, 那么后面的key对应的值不会被保存
// 如果是在可变数组中, 后面的会覆盖前面的
NSDictionary *dict = @{@"name":@"lmj", @"name":@"lnj"};
NSLog(@"dict = %@", dict);
NSMutableDictionary *dictM = [NSMutableDictionary dictionaryWithObjects:@[@"lmj", @"lnj"] forKeys:@[@"name", @"name"]];
NSLog(@"dict = %@", dictM);
常用的结构体
// 1.保存坐标的
// NSPoint;
// CGPoint point = NSMakePoint(10, 20);
// 2.保存尺寸的
// NSSize;
// CGSize size = NSMakeSize(100, 50);
// 3.保存坐标和尺寸
NSRect;
CGRect rect = NSMakeRect(10, 20, 100, 50);
// 4.在开发中苹果推荐我们使用CG开头的结构体, 也就是说NS开头的结构体一般不用
NSNumber
int age = 10;
double number= 5.1;
int value = 6;
// NSArray *arr = @[age, number, value];
// 1.将基本数据类型转换为对象类型
NSNumber *ageN = [NSNumber numberWithInt:age];
NSNumber *numberN = [NSNumber numberWithDouble:number];
NSNumber *valueN = [NSNumber numberWithInt:value];
NSArray *arr = @[ageN, numberN, valueN];
NSLog(@"arr = %@", arr);
// 2.将对象类型转换为基本数据类型
// int temp = [ageN intValue];
// double temp = [numberN doubleValue];
// NSLog(@"%f", temp);
// 3.基本数据类型转换对象类型简写
// 注意: 如果传入的是变量那么必须在@后面写上(), 如果传入的常量, 那么@后面的()可以省略
// NSNumber *temp = @(number);
NSNumber *temp [email protected];
NSLog(@"%@", temp);
NSValue
/*
typedef struct{
int age;
char *name;
double _height;
}Person;
Person p = {30, "lnj", 1.75};
// NSArray *arr = @[p];
*/
// 1.利用NSValue包装常用的结构体
/*
CGPoint point = NSMakePoint(10, 20);
NSValue *value = [NSValue valueWithPoint:point];
NSArray *arr = @[value];
NSLog(@"%@", arr);
*/
// 2.利用NSValue包装自定义的结构体
typedef struct{
int age;
char *name;
double height;
}Person;
Person p = {30, "lnj", 1.75};
// valueWithBytes: 接收一个指针, 需要传递需要包装的结构体的变量的地址
// objCType: 需要传递需要包装的数据类型
NSValue *pValue = [NSValue valueWithBytes:&p objCType:@encode(Person)];
NSArray *arr = @[pValue];
NSLog(@"%@", arr);
// 从NSValue中取出自定义的结构体变量
Person res;
[pValue getValue:&res];
NSLog(@"age = %i, name = %s, height = %f", res.age, res.name, res.height);
NSDate
// 1.NSDate创建和基本概念
/*
// 只要是通过date方法创建的时间对象, 对象中就保存了当前的时间
NSDate *now = [NSDate date];
NSLog(@"now = %@", now);
// 在now的基础上追加多少秒
// NSDate *date = [now dateByAddingTimeInterval:10];
// NSLog(@"date = %@", date);
// 1.获取当前所处的时区
NSTimeZone *zone = [NSTimeZone systemTimeZone];
// 2.获取当前时区和指定时区的时间差
NSInteger seconds = [zone secondsFromGMTForDate:now];
// NSLog(@"seconds = %lu", seconds);
NSDate *newDate = [now dateByAddingTimeInterval:seconds];
NSLog(@"newDate = %@", newDate);
*/
// 2.时间格式化 NSDate --> NSString
/*
// xxxx年xx月xx日 xx小时xx分钟xx秒
// xxxx/xx/xx xx/xx/xx
// xx/xx/xxxx xx/xx/xx
NSDate *now = [NSDate date];
// 创建一个时间格式化对象
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
// 告诉时间格式化对象, 按照什么样的格式来格式化时间
// yyyy 年
// MM 月
// dd 日
// HH 24小时 hh 12小时
// mm 分钟
// ss 秒钟
// Z 时区
// formatter.dateFormat = @"yyyy年MM月dd日 HH时mm分ss秒 Z";
formatter.dateFormat = @"MM-dd-yyyy HH-mm-ss";
// 利用时间格式化对象对时间进行格式化
NSString *res = [formatter stringFromDate:now];
NSLog(@"res = %@", res);
*/
// NSString --> NSDate
NSString *str = @"2015-06-29 07:05:26 +0000";
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
// 注意: 如果是从NSString格式化为NSDate, 那么dateFormat的格式, 必须和字符串中的时间格式一致, 否则可能转换失败
formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss Z";
NSDate *date = [formatter dateFromString:str];
NSLog(@"%@", date);
NSCalendar
// 1.获取当前时间的年月日时分秒
/*
// 获取当前时间
NSDate *now = [NSDate date];
NSLog(@"now = %@", now);
// 日历
NSCalendar *calendar1 = [NSCalendar currentCalendar];
// 利用日历类从当前时间对象中获取 年月日时分秒(单独获取出来)
// components: 参数的含义是, 问你需要获取什么?
// 一般情况下如果一个方法接收一个参数, 这个参数是是一个枚举 , 那么可以通过|符号, 连接多个枚举值
NSCalendarUnit type = NSCalendarUnitYear |
NSCalendarUnitMonth |
NSCalendarUnitDay |
NSCalendarUnitHour |
NSCalendarUnitMinute |
NSCalendarUnitSecond;
NSDateComponents *cmps = [calendar1 components:type fromDate:now];
NSLog(@"year = %ld", cmps.year);
NSLog(@"month = %ld", cmps.month);
NSLog(@"day = %ld", cmps.day);
NSLog(@"hour = %ld", cmps.hour);
NSLog(@"minute = %ld", cmps.minute);
NSLog(@"second = %ld", cmps.second);
*/
// 2.比较两个时间之间的差值, 比较相差多少年多少月多少日多少小时多少分钟多少秒
// 2.1过去的一个时间
NSString *str = @"2015-06-29 07:05:26 +0000";
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss Z";
NSDate *date = [formatter dateFromString:str];
// 2.2当前的时间
NSDate *now = [NSDate date];
NSLog(@"date = %@", date);
NSLog(@"now = %@", now);
// 2.3比较两个时间
NSCalendar *calendar = [NSCalendar currentCalendar];
NSCalendarUnit type = NSCalendarUnitYear |
NSCalendarUnitMonth |
NSCalendarUnitDay |
NSCalendarUnitHour |
NSCalendarUnitMinute |
NSCalendarUnitSecond;
NSDateComponents *cmps = [calendar components:type fromDate:date toDate:now options:0];
NSLog(@"%ld年%ld月%ld日%ld小时%ld分钟%ld秒钟", cmps.year, cmps.month, cmps.day, cmps.hour, cmps.minute, cmps.second);
NSFileManager
NSFileManager *manager = [NSFileManager defaultManager];
// 1.判断一个文件或者文件夹是否存在
/*
BOOL flag = [manager fileExistsAtPath:@"/Users/xiaomage/Desktop/video/01-NSArray基本概念.mp4"];
NSLog(@"flag = %i", flag);
*/
// 2.判断一个文件是否存在, 并且判断它是否是一个文件夹
/*
// 注意: 该方法的返回值是说明传入的路径对应的文件或文件夹是否存在
// 第二个参数是用于保存判断结果的, 如果是一个目录, 那么就会赋值为YES, 如果不是就赋值为NO
BOOL dir = NO;
BOOL flag = [manager fileExistsAtPath:@"/Users/xiaomage/Desktop/video/01-NSArray基本概念.mp4" isDirectory:&dir];
NSLog(@"flag = %i, dir = %i", flag, dir);
*/
// 3.获取文件或文件夹的属性
/*
NSDictionary *info = [manager attributesOfItemAtPath:@"/Users/xiaomage/Desktop/video/01-NSArray基本概念.mp4" error:nil];
NSLog(@"info = %@", info);
*/
// 4.获取文件夹中所有的文件
/*
// 注意:contentsOfDirectoryAtPath方法有一个弊端, 只能获取当前文件夹下所有的文件, 不能获取子文件夹下面的文件
NSArray *res = [manager contentsOfDirectoryAtPath:@"/Users/xiaomage/Desktop/video" error:nil];
NSLog(@"res = %@", res);
*/
/*
// NSArray *res = [manager subpathsAtPath:@"/Users/xiaomage/Desktop/video"];
NSArray *res = [manager subpathsOfDirectoryAtPath:@"/Users/xiaomage/Desktop/video" error:nil];
NSLog(@"res = %@", res);
// 作业: 要求计算一个文件夹中所有文件的大小
// 注意: 如果通过attributesOfItemAtPath方法直接获取, 那么获取到的文件夹的大小不准确
// 要想实现计算一个文件夹中所有文件的大小必须先拿到所有的文件, 然后再获取所有文件的大小, 然后再相加
*/
// 5.创建文件夹
/*
// createDirectoryAtPath: 告诉系统文件夹需要创建到什么位置
// withIntermediateDirectories: 如果指定的文件中有一些文件夹不存在, 是否自动创建不存在的文件夹
// attributes: 指定创建出来的文件夹的属性
// error: 是否创建成功, 如果失败会给传入的参数赋值
// 注意: 该方法只能用于创建文件夹, 不能用于创建文件
BOOL flag = [manager createDirectoryAtPath:@"/Users/xiaomage/Desktop/abc/lnj" withIntermediateDirectories:YES attributes:nil error:nil];
NSLog(@"%i", flag);
*/
// 6.创建文件
// createFileAtPath: 指定文件创建出来的位置
// contents : 文件中的内容
// attributes: 创建出来的文件的属性
// NSData : 二进制数据
// 注意: 该方法只能用于创建文件, 不能用于创建文件夹
NSString *str = @"江哥真帅";
NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
[manager createFileAtPath:@"/Users/xiaomage/Desktop/abc.txt" contents:data attributes:nil];
对象的copy
/*
// 会生成一个新的对象
NSString *srcStr = @"lnj";
// 只要是拷贝出来的对象, 拷贝出来的对象中的内容和以前对象中的内容一致
// "一般"情况下拷贝会生成一个新的对象
// 为什么会产生一个新的对象 1.因为拷贝要求修改原来的对象不能影响到拷贝出来得对象 \
修改拷贝出来的对象也不能影响到原来的对象, 所以需要生成一个新的对象 \
2.由于以前的对象是一个不可变的对象, 而通过mutableCopy拷贝出来的对象必须是一个可变的对象, 所以必须生成一个新的对象
NSMutableString *copyStr = [srcStr mutableCopy];
NSLog(@"srcStr = %@, copyStr = %@", srcStr, copyStr);
NSLog(@"srcStr = %p, copyStr = %p", srcStr, copyStr);
*/
/*
// 会生成一个新的对象
NSMutableString *srcStr = [NSMutableString stringWithFormat:@"lnj"];
NSMutableString *copyStr = [srcStr mutableCopy];
[srcStr appendString:@" cool"];
NSLog(@"srcStr = %@, copyStr = %@", srcStr, copyStr);
NSLog(@"srcStr = %p, copyStr = %p", srcStr, copyStr);
*/
/*
// 会生成一个新的对象
NSMutableString *srcStr = [NSMutableString stringWithFormat:@"lnj"];
NSString *copyStr = [srcStr copy];
[srcStr appendString:@" cool"];
NSLog(@"srcStr = %@, copyStr = %@", srcStr, copyStr);
NSLog(@"srcStr = %p, copyStr = %p", srcStr, copyStr);
*/
// 如果是通过不可变对象调用了copy方法, 那么不会生成一个新的对象
// 原因: 因为原来的对象是不能修改的, 拷贝出来的对象也是不能修改的
// 既然两个都不能修改, 所以永远不能影响到另外一个对象, 那么已经符合需求
// 所以: OC为了对内存进行优化, 就不会生成一个新的对象
NSString *srcStr = @"lnj";
NSString *copyStr = [srcStr copy];
NSLog(@"srcStr = %p, copyStr = %p", srcStr, copyStr);
/*
正是因为调用copy方法有时候会生成一个新的对象, 有时候不会生成一个新的对象
所以: 如果没有生成新的对象, 我们称之为浅拷贝, 本质就是指针拷贝
如果生成了新的对象, 我们称之为深拷贝, 本质就是会创建一个新的对象
*/
Person.h
@interface Person : NSObject<NSCopying, NSMutableCopying>
@property (nonatomic, assign) int age;
@property (nonatomic, copy) NSString *name;
@end
Person.m
#import "Person.h"
@implementation Person
- (id)copyWithZone:(NSZone *)zone
{
// 1.创建一个新的对象
Person *p = [[[self class] allocWithZone:zone] init];
// 2.设置当前对象的内容给新的对象
p.age = _age;
p.name = _name;
// 3.返回新的对象
return p;
}
- (id)mutableCopyWithZone:(NSZone *)zone
{
// 1.创建一个新的对象
Person *p = [[[self class] allocWithZone:zone] init];
// 2.设置当前对象的内容给新的对象
p.age = _age;
p.name = _name;
// 3.返回新的对象
return p;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"name = %@, age = %i", _name, _age];
}
@end
/*
1.以后想让自定义的对象能够被copy只需要遵守NSCopying协议
2.实现协议中的- (id)copyWithZone:(NSZone *)zone
3.在- (id)copyWithZone:(NSZone *)zone方法中创建一个副本对象, 然后将当前对象的值赋值给副本对象即可
*/
/*
Person *p = [[Person alloc] init];
p.age = 30;
p.name = @"lnj";
NSLog(@"%@", p);
// Person *p2 = [p copy];
Person *p2 = [p mutableCopy];
NSLog(@"%@", p2);
*/
Student *stu = [[Student alloc] init];
stu.age = 30;
stu.height = 1.75;
stu.name = @"lnj";
NSLog(@"stu = %@", stu);
// 如果想让子类在copy的时候保留子类的属性, 那么必须重写copyWithZone方法, 在该方法中先调用父类创建副本设置值, 然后再设置子类特有的值
Student *stu2 = [stu copy];
NSLog(@"stu2 = %@", stu2);
宏的高级使用(生成单例代码)
// 以后就可以使用interfaceSingleton来替代后面的方法声明
#define interfaceSingleton(name) +(instancetype)share##name
#if __has_feature(objc_arc)
// ARC
#define implementationSingleton(name) \
+ (instancetype)share##name \
{ \
name *instance = [[self alloc] init]; \
return instance; \
} \
static name *_instance = nil; \
+ (instancetype)allocWithZone:(struct _NSZone *)zone \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [[super allocWithZone:zone] init]; \
}); \
return _instance; \
} \
- (id)copyWithZone:(NSZone *)zone{ \
return _instance; \
} \
- (id)mutableCopyWithZone:(NSZone *)zone \
{ \
return _instance; \
}
#else
// MRC
#define implementationSingleton(name) \
+ (instancetype)share##name \
{ \
name *instance = [[self alloc] init]; \
return instance; \
} \
static name *_instance = nil; \
+ (instancetype)allocWithZone:(struct _NSZone *)zone \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [[super allocWithZone:zone] init]; \
}); \
return _instance; \
} \
- (id)copyWithZone:(NSZone *)zone{ \
return _instance; \
} \
- (id)mutableCopyWithZone:(NSZone *)zone \
{ \
return _instance; \
} \
- (oneway void)release \
{ \
} \
- (instancetype)retain \
{ \
return _instance; \
} \
- (NSUInteger)retainCount \
{ \
return MAXFLOAT; \
}
#endif
使用反斜杠代表在一行也是宏定义。,宏名称后面传递参数可以用## (双井号)+名称可以使用。例如:
#define 名称(参数名)
后面如果需要使用宏里面的参数,##参数名