第六章、oc的语言高级阶段

1,copy内存管理

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    /*
     内存管理原则:
     一次alloc 对应一次release
     一次retain 对应一次 release
     一次copy 对应一次release
     有加就有减
     
     总结:
     如果是浅拷贝:不会生成新的对象,但是系统就会对原来的对象进行retain,
     所以需要对原来的对象进行一次
     
     如果是深拷贝:会生成新的对象,系统不会对原来的对象进行retain,但是因为生成了新的对象,所以我们需要对新的对象进行release
     */

    //1.创建一个不可变字符串
    NSString *str = [[NSString alloc]initWithFormat:@"lnj"];
    NSLog(@"str = %lu", [str retainCount]);
    
    /*
  
     */
    //不会生成新的对象,但是需要注意,正是因为不会生产新的对象,所以系统会对以前的对象进行一次retain
    //如果是浅拷贝,那么系统就会对原来的对象进行retain
    
    NSString *str2 = [str copy];
    NSLog(@"str = %lu", [str retainCount]);
    
    [str release];
    [str release];
    
    //深拷贝:会生成新的对象,正是因为会生成新的对象,所以系统不会对以前的对象进行retain,但是因为生成了新的对象,所以我们需要对新的对象进行release
    NSMutableString *strM = [str mutableCopy];
    NSLog(@"str = %lu", [str retainCount]);
    NSLog(@"strM = %lu", [strM retainCount]);
    
    NSLog(@"%p - %p", str, strM);
    [strM release];
    [str release];
    
}
@end

数组NSArray和MutableArray

1,NSArray的基本概念

#import 
#import "Person.h"

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

    /*
//    NSArray *arr = [[NSArray alloc] init];
//    NSArray *arr = [NSArray arrayWithObject:@"lnj"];
    // 数组中的nil是结束符
    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]);
     */
    
    /*
    NSArray *arr = [NSArray arrayWithObjects:@"lnj", @"lmj", @"jjj", @"zs", nil];
    if([arr containsObject:@"zs"])
    {
        NSLog(@"arr中包含zs");
    }else
    {
        NSLog(@"arr中不包含zs");
    }
     */
    
    // 创建数组简写
    NSString *str = @"lnj";
//    NSArray *arr = [NSArray arrayWithObjects:@"lnj", @"lmj", @"jjj", nil];
    NSArray *arr = @[@"lnj", @"lmj", @"jjj"];
    // 获取数组元素的简写
    NSLog(@"%@", [arr objectAtIndex:0]);
    NSLog(@"%@", arr[0]);
    return 0;
}

2,NSArray遍历

#import 
#import "Person.h"

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

//    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"];
    return 0;
}


Person类
#import 

@interface Person : NSObject

- (void)say;

- (void)sayWithName:(NSString *)name;
@end

Person.m类
#import "Person.h"

@implementation Person

-(void)say
{
    NSLog(@"加油");
}

- (void)sayWithName:(NSString *)name
{
    NSLog(@"%@ 加油", name);
}
@end

3,NSArray排序

#import 
#import "Person.h"

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

    /*
    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);
    return 0;
}

4,NSArray和NSString之间转换

#import 

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

//    NSArray *arr = @[@"lnj", @"lmj", @"jjj"];
    // 需求: 用-将所有的姓名连接起来生成一个字符串
    
    /*
    // 1.定义一个可变字符串保存拼接之后的结果
    NSMutableString *strM = [NSMutableString string];
    // 2.遍历数组, 取出数组中的每一个元素, 将元素添加到可变字符串中
    for (NSString *str in arr) {
        [strM appendString:str];
        // 3.每次添加完毕之后再添加一个-
        [strM appendString:@"-"];
    }
    [strM deleteCharactersInRange:NSMakeRange(strM.length - 1, 1)];
    NSLog(@"strM = %@", strM);
     */
    
    /*
    NSString *str = [arr componentsJoinedByString:@"**"];
    NSLog(@"str = %@", str);
     
    */
    
    // 通过一个字符串生成一个数组
    // 也叫做字符串切割
    NSString *str = @"lnj**lmj**jjj";
    NSArray *arr = [str componentsSeparatedByString:@"**"];
    NSLog(@"arr = %@", arr);
    
//    打印出来的效果
//    arr = (
//           lnj,
//           lmj,
//           jjj
//           )
    return 0;
}

5,NSArray文件读写

#import 
#import "Person.h"

int main(int argc, const char * argv[]) {
    // 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);
    */
    
    return 0;
}

6,NSMutableArray的基本概念

#import 

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

    // 创建一个空的数组
    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);
//    打印的结果是:
//    NSMutableArray[1221:29098] (
//                                lnj,
//                                xcq,
//                                A,
//                                B,
//                                lmj,
//                                jjj
//                                )
    /*
    // 如何删除
    [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"];*/
    
    return 0;
}

7,NSDictionary

#import 

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

    // 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 *dict1 = @{@"name":@"lnj", @"age":@"30", @"height":@"1.75"};
    // 2.1如何获取字典中key和value的个数, 在字典中key称之为键, value称之为值
//    打印的值为:3
    NSLog(@"count = %lu", [dict1 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];
    
    return 0;
}

8,NSMutableDictionary

#import 

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

    /*
    // 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);
    
    return 0;
    
//    打印的结果:
//    2018-03-15 10:25:36.905934+0800 NSMutableDictionary[1757:46366] dict = {
//        name = lmj;
//    }
//    2018-03-15 10:25:36.906294+0800 NSMutableDictionary[1757:46366] dict = {
//        name = lnj;
//    }
}

9,OC中的常用结构体

#import 

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

    // 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开头的结构体一般不用
    
    return 0;
}

10,NSNumber

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

    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);
    
    return 0;
}

11,NSValue

#import 

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

//    typedef struct{
//        int age;
//        char *name;
//        double _height;
//    }Person;
//
//    Person p = {30, "lnj", 1.75};
////    结构体不包装不能存入数组,所以要用NSValue
//    NSArray *arr = @[p];
    
    // 1.利用NSValue包装常用的结构体
    
//    CGPoint point = NSMakePoint(10, 20);
//    NSValue *value = [NSValue valueWithPoint:point];
//    NSArray *arr1 = @[value];
//    NSLog(@"%@", arr1);
    
    // 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);
    
    return 0;
}

12,NSDate

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

    // 1.NSDate创建和基本概念
    
    // 只要是通过date方法创建的时间对象, 对象中就保存了当前的时间
    NSDate *now = [NSDate date];
    NSLog(@"now = %@", now);
    // 在now的基础上追加多少秒
    NSDate *date1 = [now dateByAddingTimeInterval:10];
    NSLog(@"date = %@", date1);
//    打印的结果是:
//    2018-03-15 10:44:49.276294+0800 NSDate[1994:55461] now = Thu Mar 15 10:44:49 2018
//    2018-03-15 10:44:49.276358+0800 NSDate[1994:55461] date = Thu Mar 15 10:44:59 2018

    
    // 1.获取当前所处的时区
    NSTimeZone *zone = [NSTimeZone systemTimeZone];
    // 2.获取当前时区和指定时区的时间差
    NSInteger seconds = [zone secondsFromGMTForDate:now];
    NSLog(@"seconds = %lu", seconds);
//    打印结果:28800 也就是8个小时
    NSDate *newDate = [now dateByAddingTimeInterval:seconds];
    NSLog(@"newDate = %@", newDate);
//    现在的时间8个小时

    // 2.时间格式化  NSDate --> NSString
 
    // xxxx年xx月xx日 xx小时xx分钟xx秒
    // xxxx/xx/xx  xx/xx/xx
    // xx/xx/xxxx  xx/xx/xx
    NSDate *now1 = [NSDate date];
    
    // 创建一个时间格式化对象
    NSDateFormatter *formatter1 = [[NSDateFormatter alloc] init];
    // 告诉时间格式化对象, 按照什么样的格式来格式化时间
    // yyyy 年
    // MM 月
    // dd 日
    // HH 24小时  hh 12小时
    // mm 分钟
    // ss 秒钟
    // Z 时区
//    formatter.dateFormat = @"yyyy年MM月dd日 HH时mm分ss秒 Z";
    formatter1.dateFormat = @"MM-dd-yyyy HH-mm-ss";
    
    // 利用时间格式化对象对时间进行格式化
    NSString *res = [formatter1 stringFromDate:now1];
    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);

    return 0;
}

13,NSCalendar

#import 

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

    // 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);
    
    打印的结果:
    2018-03-15 10:56:07.682445+0800 NSCalendar[2158:61248] now = Thu Mar 15 10:56:07 2018
    2018-03-15 10:56:07.683580+0800 NSCalendar[2158:61248] year = 2018
    2018-03-15 10:56:07.683630+0800 NSCalendar[2158:61248] month = 3
    2018-03-15 10:56:07.683652+0800 NSCalendar[2158:61248] day = 15
    2018-03-15 10:56:07.683664+0800 NSCalendar[2158:61248] hour = 10
    2018-03-15 10:56:07.683674+0800 NSCalendar[2158:61248] minute = 56
    2018-03-15 10:56:07.683684+0800 NSCalendar[2158:61248] second = 7
    */

    // 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);
//    打印结果:
//    2018-03-15 10:59:20.710963+0800 NSCalendar[2195:62513] date = Mon Jun 29 15:05:26 2015
//    2018-03-15 10:59:20.711031+0800 NSCalendar[2195:62513] now = Thu Mar 15 10:59:20 2018
//    2018-03-15 10:59:20.711341+0800 NSCalendar[2195:62513] 2年8月14日19小时53分钟54秒钟
    return 0;
}

14,NSFileManager文件管理

#import 

int main(int argc, const char * argv[]) {
    
    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];
    
    return 0;
}

15,数组集合对象的内存管理

#import 
#import "Person.h"

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

    @autoreleasepool {
        
        /*
        // 1. 如果将一个对象添加到一个数组中, 那么数组会对对象进行一个retain
        Person *p = [Person new];
        NSLog(@"reatinCount = %lu", [p retainCount]);
        NSMutableArray *arrM = [[NSMutableArray alloc] init];
        
        [arrM addObject:p];
        NSLog(@"reatinCount = %lu", [p retainCount]);
        
        [p release];
        NSLog(@"reatinCount = %lu", [p retainCount]);
        // 当数组对象释放之后, 会给数组中所有的对象发送一条release消息
        [arrM release];
         */
        
        Person *p = [Person new]; // 1
        NSLog(@"reatinCount = %lu", [p retainCount]);
        NSMutableArray *arrM = [[NSMutableArray alloc] init];
        [arrM addObject:p]; // 2
        NSLog(@"reatinCount = %lu", [p retainCount]);
        
        [p release]; // 1
        // 当数组移除一个对象之后, 会给这个对象发送一条release消息
        [arrM removeObject:p];
    }
    return 0;
}

16,Copy基本使用

#import 

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

    /*
     // 会生成一个新的对象
    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方法有时候会生成一个新的对象, 有时候不会生成一个新的对象
     所以: 如果没有生成新的对象, 我们称之为浅拷贝, 本质就是指针拷贝
          如果生成了新的对象, 我们称之为深拷贝, 本质就是会创建一个新的对象
     */
    return 0;
}

17,Copy内存管理

#import 

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

    @autoreleasepool {
        /*
        //NSString *str1 = @"lnj";
        char *cstr = "this is a c string";
        NSString *str1 = [[NSString alloc] initWithUTF8String:cstr];
        NSLog(@"str = %lu", [str1 retainCount]); // 1
        // 不会产生新对象, 会对原有对象进行一次retain
        NSString *str2 = [str1 copy]; // 2
        NSLog(@"str = %lu", [str1 retainCount]);
        
        // 注意点: 如果是浅拷贝, 那么会对拷贝的对象进行一次retain, 那么我们就需要对拷贝出来的对象进行一次release
        [str2 release]; // 1
        [str1 release]; // 0
         */
        
        char *cstr = "this is a c string";
        NSString *str1 = [[NSString alloc] initWithUTF8String:cstr];
        NSLog(@"str1 = %lu", [str1 retainCount]); // 1
        
        // 会生成一个新的对象
        NSMutableString *str2 = [str1 mutableCopy];
        NSLog(@"%p %p", str1, str2);
        NSLog(@"str2 = %lu", [str2 retainCount]); // 1
        
        [str1 release];
        [str2 release];
        
        /*
         内存管理的原则, 有加就有减
         一次alloc/retain/copy 对应一次release
         
         */
    }
    return 0;
}

18,Copy与Property

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

    
    // 1.copy的第一个用途, 防止外界修改内部的数据
    
    /*
    NSMutableString *temp = [NSMutableString stringWithFormat:@"lnj"];
    
    Person *p = [[Person alloc] init];
    p.name = temp;
    
    // 问题: 修改了外面的变量, 影响到了对象中的属性
    [temp appendString:@" cool"];
    
    NSLog(@"name = %@", p.name);
    
    // 记住: 以后字符串属性都用copy
     */
    
    /*
    __block int num = 10;
    void (^myBlock)() = ^{
        num = 20;
        NSLog(@"%i", num);
    };
    myBlock();
     */
    /*
    // block默认存储在栈中, 栈中的block访问到了外界的对象, 不会对对象进行retain
    // block如果在堆中, 如果在block中访问了外界的对象, 会对外界的对象进行一次retain
    Person *p = [[Person alloc] init];
    NSLog(@"retainCount = %lu", [p retainCount]);
    void (^myBlock)() = ^{
        NSLog(@"%@", p);
        NSLog(@"retainCount = %lu", [p retainCount]);
    };
    Block_copy(myBlock); // 将block转移到堆中
    myBlock();
     */
    
    // 2.可以使用copy保存block, 这样可以保住block中使用的外界对象的命
    /*
    // 避免以后调用block的时候, 外界的对象已经释放了
    Dog *d = [[Dog alloc] init]; // 1
    NSLog(@"retainCount = %lu", [d retainCount]);
    Person *p = [[Person alloc] init];
    p.pBlock = ^{
        // 2
        NSLog(@"%@", d);
    };
    NSLog(@"retainCount = %lu", [d retainCount]); // 2

    // 如果狗在调用block之前释放了, 那么程序就会崩溃
    [d release]; // 1
    
    p.pBlock();
    
    [p release];
     */
    
    // 3.注意点: copy block之后引发循环引用
    // 如果对象中的block又用到了对象自己, 那么为了避免内存泄露, 应该将对象修饰为__block
    __block Person *p = [[Person alloc] init]; // 1
    p.name = @"lnj";
    NSLog(@"retainCount = %lu", [p retainCount]);
    p.pBlock = ^{
        NSLog(@"name = %@", p.name); // 2
    };
    NSLog(@"retainCount = %lu", [p retainCount]);
    p.pBlock();
    
    [p release]; // 1
//    [p release]; // 2B
    
    return 0;
}


Person.h
#import 


typedef void (^myBlock)();

@interface Person : NSObject

//@property (nonatomic, retain) NSString *name;
@property (nonatomic, copy) NSString *name;

// 注意: 如果是block使用copy并不是拷贝, 而是转移
@property (nonatomic, copy) myBlock pBlock;
@end

19,自定义类实现copy

  • main.m类
#import 
#import "Person.h"
#import "Student.h"

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

    /*
     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", p,&p);
    Person *p2 = [p copy];
    Person *p3 = [p mutableCopy];
    
    NSLog(@"%@,%@,%p,%p", p2,p3,&p2,&p3);
//    打印结果:
//    2018-03-15 15:50:29.669328+0800 自定义类实现copy[4622:150414] name = lnj, age = 30,0x7fff5fbff6a8
//    2018-03-15 15:50:29.669579+0800 自定义类实现copy[4622:150414] name = lnj, age = 30,name = lnj, age = 30,0x7fff5fbff6a0,0x7fff5fbff698
//    2018-03-15 15:50:29.669684+0800 自定义类实现copy[4622:150414] stu = name = lnj, age = 30, height = 1.750000
//    2018-03-15 15:50:29.669734+0800 自定义类实现copy[4622:150414] stu2 = name = lnj, age = 30, height = 1.750000
    
    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);
    
    return 0;
}
  • Person类
#import 

@interface Person : NSObject

@property (nonatomic, assign) int age;

@property (nonatomic, copy) NSString *name;
@end




#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
  • Student.h
#import "Person.h"

@interface Student : Person

@property (nonatomic, assign) double height;
@end




#import "Student.h"

@implementation Student

- (id)copyWithZone:(NSZone *)zone
{
    // 1.创建副本
//    id obj = [[self class] allocWithZone:zone];
    id obj = [super copyWithZone:zone];
    // 2.设置数据给副本
//    [obj setAge:[self age]];
//    [obj setName:[self name]];
    [obj setHeight:_height];
    
    // 3.返回副本
    return obj;
}

- (NSString *)description
{
    return [NSString stringWithFormat:@"name = %@, age = %i, height = %f", [self name], [self age], _height];
}

@end

20,单例(宏定义抽取单例)

int main(int argc, const char * argv[]) {
    Tools *t1 = [[Tools alloc] init]; //内部会调用allocWithZone
    Tools *t2 = [Tools new];// [[alloc] init]  allocWithZone
    Tools *t3 = [Tools shareTools];
    
    Tools *t4 = [t3 copy];
    Tools *t5 = [t3 mutableCopy];
    
    NSLog(@"%p, %p, %p, %p, %p", t1, t2, t3, t4, t5);
    
    Person *p1 = [Person sharePerson];
    Person *p2 = [Person sharePerson];
    Person *p3 = [Person sharePerson];
    
    NSLog(@"%p, %p, %p", p1 , p2, p3);
    // 如何判断当前是ARC还是MRC?
    // 可以在编译的时候判断当前是否是ARC
#if __has_feature(objc_arc)
    NSLog(@"ARC");
#else
    NSLog(@"MRC");
#endif
    return 0;
}
  • Singleton.h核心类

// 以后就可以使用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

  • Person.h类
#import 
#import "Singleton.h"

@interface Person : NSObject

interfaceSingleton(Person);

@end



#import "Person.h"

@implementation Person


implementationSingleton(Person)

@end

你可能感兴趣的:(第六章、oc的语言高级阶段)