OC基础学习-06

168 阅读13分钟

Foundation

  • NSString

    //1.通过字符串常量创建,字符串对象存储在常量区
    NSString *str1 = @"";
    //2.通过allo init创建,存储在堆区
    NSString *str2 = [[NSString alloc] initWithFormat:@""];
    //3.通过类工厂方法创建,存储在堆区
    NSString *str3 = [NSString stringWithFormat:@""]
    
    • 如果是通过字符串常量创建字符串对象,那么如果创建多个对象且值相同,只是局部变量名称不同,多个对象指向同一块存储空间(会先去常量区寻找是否有这个值,如果找到就将这个地址返回给一个局部变量);通过alloc创建的不存在这种情况,只要出现alloc,就会在堆空间开辟空间
    • 不同平台的存储方式不一样,不同编译器存储的方式也不一样,mac平台会自动对字符串对象进行优化在堆区的显示是一个对象,但是如果是IOS平台就是两个对象;在xcode6一下并且是iOS平台,那么每次alloc都会创建一个新的对象,如果是Xcode6以上那么多次alloc指向同一块存储空间
    • 一般情况下,只要通过alloc获取类工厂方法创建的对象,每次都会在堆内存中开辟一块新的存储空间,但是如果是alloc的initWithString方法除外外,因为这个方法是通过浅copy返回一个字符串对象
    • copy分为深拷贝和浅拷贝,如果是深拷贝会创建一个新的对象,如果是浅拷贝不会创建新的对象,而是直接返回被拷贝的对象的地址,和平台无关
    • atomically:如果传入YES,字符串写入文件的过程中如果没有写完,那么不会生成文件,如果传入NO,字符串写入文件的过程中如果没有写完,会生成文件
    • 文件读取和写入
    //读取
    NSString *str = [NSString stringWithContentsOfFile:@"文件的绝对路径" encoding:NSSUTF8StringEncoding error:&error]
    //写入
    BOOL flag= [str writeToFile:@"文件绝对路径" atomically:YES encoding:NSSUTF8StringEncoding error:nil]
    //通过URL来读写,如果加载的资源是本机资源,那么URL中的主机地址可以省略,虽然主机地址可以省略,单文件路径最前面的/不能省略,文件路径最前面的/代表国根路径
    NSString *path = @"file://127.0.0.1/文件路径";
    NSString *url = [NSURL URLWithString:path];
     NSString *str = [NSString stringWithContentsOfURL:url encoding:NSSUTF8StringEncoding error:&error];
     
     [str writeToURL:@"文件绝对路径" atomically:YES encoding:NSSUTF8StringEncoding error:nil]
    
    
    • 如果是通过fileURLWithPath创建URL,那么系统会自动给我传入的字符串添加协议头,所以字符串中不需要在写file://,开发中,如果使用本机资源,创建URL时,建议使用fileURLWithPath,因为url不支持中文,如果URL中包含中文,将无法访问,但是fileURLWithPath可以对URL中包含的中文进行处理
    • 如果想URL包含中文,又不通过fileURLWithPath,可以通过stringByAddingPercentEscaspesUsingEncoding进行处理
    • 比较字符串
    //比较两个字符串的内容
    BOOL flag= [str isEqualToString:str2];
    //比较两个字符串的地址
    flag2 = (str1== str2);
    //字符串大小,比较 阿斯克码
    [str1 compare:str2];
    //忽略大小写比较
    [str2 caseInsensitiveCompare:str2];
    
    • 字符串搜索
    //是否以特定字符开头,本质就是从字符串第一个字符开始匹配
    [str hasPrefix:@""]
     //判断是否以什么结尾
     [str hasSuffix:@""]
     //判断是否包含,
     //只要str包含该字符串,就会返回该字符串在str中的起始位置和长度
     //如果不存在,返回的length为0,location返回为NSNotFound
     [str rangeOfString:@""]
    
    • 字符串截取
    NSRange range = {6,3}; //位置、长度
    NSRange range2 = NSMakeRange(6,3);
    [str substringWithRange:range];
    //rangeOfString 是从左至右开始查找,可以使用options: NSBackwradsSearch 反转查找
    //从某个位置开始
    [str substringFromIndex:]
    //从开始到某个位置
    [str substringToIndex:]
    
    • 字符串替换
    [str stringByReplaceingOccrurencesOfString:@"老字段" withString:@"新字段"]
    //去除首尾空格
    NSCharacterSet *set = [NSCharacterSet whitespaceCharacterSet];
    NSString *newStr = [str stringByTrimmingCharactersInSet:set]
    
    • 字符串和路径
    //1.判断是否是绝对路径,判断字符串是否以/开头
    BOOL flag = [str isAbsolutePath];
    //2.获取文件路径中的最后一个目录,获取路径中最后一个/后的内容
    NSString *newStr = [str lastPathComponent];
    //3.给路径添加目录,本质是在字符串的末尾加上一个/和指定的内容,注意:如果路径后面已经有了/,那么久不会添加/了,如果路径后面有多个/,那么会自动删除多余的
    NSString *newStr = [str stringByAppendingPathComponent:@"xx"];
    //4.删除文件路径中的最后一个目录,本质是删除最后一个/和后面的内容
     NSString *newStr = [str stringByDeletingLatPathComponent]
     //5.获取路径中文件的拓展名,本质是从字符串的末尾开始查找,截取第一个.后面的内容
     NSString *newStr = [str pathExtension]
     
     //6.删除路径中文件的拓展名
     NSString *newStr = [str stringByDeletingPathExtension]
     //6.给文件路径添加一个拓展名
     NSString *newStr = [str stringByAppendingPathExtension:@"jpg"]
    
    • 字符串转换
    //将字符串转为大写
     NSString *newStr = [str uppercaseString];
     //将字符串转为小写
      NSString *newStr = [str lowercaseString];
      //将首字母转为大写
      NSString *newStr = [str capitalizedString];
      //字符串与基本数据类型的转换
      NSString *str1 = @"110";
      int calue1 = [str1 intValue];
      //C语言字符串和OC字符串转换
      char *cStr = "dsa";
      NSString *str = [NSString stringWithUTF8String:cStr];
      //OC字符串转C语言字符串
      char *c = [str UTF8String];
    
  • NSMutalbleString

    • NSString是不可变的,里面的文字内容是不能进行修改的,NSMutalbleString是可变的,里面的文字内容可以随时更改
    //1.创建空的
    NSMutalbleString *STR = [NSMutalbleString string];
    //2.字符串添加
    NSMutalbleString *str = [NSMutalbleString stringWithFormat:@"123"];
    [str appendString:@"456"];
    //3.字符串删除
    NSRange range = [str rangeOfString:@"50"];
    [str deleteCharactersInRange:range];
    //4.字符串插入
    NSSrange *range2 = [str rangeOfString:@"50"];
    [str insertString:@"999" atIndex:range2.location];
    //5.字符串替换
    [str replaceOccurrencesOfString:@"老数据" withString:@"新数据" options:0 range NSMakeRange(0,str.length)]
    
    • OC中的枚举,如果不想使用任何一个的话,就使用0;一般情况下OC方法要求传入一个参数如果没有*,大部分是枚举
    • 开发中如果字符串经常发生变化,尽量不使用不可变的字符串
  • NSArray

    • 只能存放任意OC对象,并且是由顺序的
    • 不能存储非OC对象,如int、float等
    • 是不可变的,它里面的内容就是永远是固定的,不能删除里面的元素,也不能添加
    //单个元素
    NSArrat *array = [NSArray arrayWithObject:@"xxx"];
    //多个参数、nil是结束符
     NSArrat *array = [NSArray arrayWithObjects:@"xxx",@"kkk",@"ooo",nil];
    
    • NSArray遍历
    //常规循环
    for(int i = 0;i<arr.count;++1){
    }
    //for增强
    for(NSObject *obj in arr){
    }
    //使用OC数组的迭代器
    //每次调用block都会将当前取出的元素和元素对应的索引传递给我们,obj就是取出的元素,idx是索引,stop控制什么时候停止遍历
    [arr enumerateObjectsUsingBlock:^(id obj,NSUInteger idx,BOOL *stop){}]
    
    • 如果使用OC数组存储对象,可以调用OC数组的方法让数组中所有的元素都执行特定的方法;如果数组中保存的不是相同类型数据,并且没有相同方法,那么会报错
    //没有参数
    [arr makeObjectsPerformSelector:@selector(say)];
    //有一个参数,超出一个参数无法使用
    [arr makeObjectsPerformSelector:@selector(sayWithName:) withObject:@"xxx"];
    
    • NSArray排序
    NSArray *arr = @[@10,@11,@16];
    //默认升序,想使用compare方法对数组中元素排序,那么数组中的元素必须是Foundation框架中的对象,也就是说不能是自定义的对象
     NSArray *arr2 =[arr sortedArrayUsingSelector:@selectorr(compare:)]
     //对自定义的数据对象进行排序
     NSArray *newArr = [arr sortedArrayWithOptions:NSSortStableusingComparator:@NSComparisonResult(Person *obj1,Person *obj2){
         return obj1.age>obj2.age; 
     }]
    
    • NSArray和NSString转换
    NSString * str = [arr componentsJoinedByString:@"-"];
    NSArray *array = [str componentsSeparatedByString:@"-"];
    
    • NSArray文件读写
    //如果将一个数据写入到文件中之后,本质是写入了一个XML文件
    //再iOS开发中一般情况下我们将XML文件的拓展名保存为plist
    [arr writeToFile:@"/dsadasd/da.text",atomically:YES];
    //writeToFile 只能写入数据中保存的元素都是Foundation框架中的类创建的对象,如果保存的是自定义的对象将无法写入
    NSArray *arr = [NSArray arrayWithContentsofFile:@"/sadasda/abc.plist"];
    
  • NSMutableArray

//创建空数组
NSMutableArray *arr = [NSMutableArray arrat]; 
//添加元素
[arr addObject:@"dsa"];
//将指定数组的元素都取出来,放到arr中,并不是将整个数组作为一个元素添加到arr中
[arr addObjectsFromArray:@[@"das",@"dasdas"]];
//注意:将整个数组作为一个元素添加
[arr addObjects:@[@"sda",@"789"]];
//插入元素
[arr insertObject:@"2sad" atIndex:1];
//插入一组数据,指定需要插入的位置和插入多少个
NSRange range = NSMakeRange(2,2);
NSIndexSet *set = [NSIndexSet indexSetWithIndexsInRange:range];
[arr insertObjects:@[@"2sad",@"ppp"] atIndexs:set];
//删除
[arr removeObjectAtIndex:0];
//替换
[arr replaceobjectAtIndex:1,withObject:@"dsa"];
//获取
[arr objectAtIndex:0];
//注意不能通过@[@"12",@"DSAD"]来直接创建NSMutableArray,如果把一个不可变数组当作一个可变数据来使用,会引发运行时错误
  • NSDirectory
//遍历
[dist enumerateKeysAndObjectsUsingBlock:^(id key,id obj,BOOL *stop){

}]
  • NSMutableDirectory:NSDirectory有重复key的话保留第一个,NSMutableDirectory有重复的话保存的是最后一个

  • 常用结构体

    • CGpoint:表示二维平面上的一个点
    • CGSize:表示长宽
    • CGRect:表示宽高已经左上角的点的坐标
  • NSNumber

    • 将基本数据类型转为对象类型
     NSNumber *age1 = [NSNumber numberWithInt:age];
     NSNumber *number1 = [NSNumber numberWithDouble:number];
     //简写,如果传入的是变量必须写@(),如果传入的是常量()可以省略
     NSNumber *nn = @(age)
    
    • 将对象类型转为基本数据类型
    int value = [age intValue];
    
  • NSValue

    • NSNumber是NSValue的子类,但是NSNumber只能包装数字类型
    //1.包装常用结构体
    CGPoint point = NSMakePoint(10,20);
    NSVlaue *value = [NSValue valueWithPoint:point];
    NSArray *arr = @[value];
    //2.包装自定义结构体
    typedef struct{
        int age;
        char *name;
    }Person;
    Person *p = {1,"sd"};
    //valueWithBytes:接收一个指针,需要传递需要包装的结构体的变量的地址
    objCType:需要传递需要包装的数据类型
    NSValue *value = [NSValue valueWithBytes:&p,objCType:@encode(Person)];
    NSArray *arr = @[value];
    Pserson res;
    [value getValue:&res];
    
  • NSDate

    //当前时间
    NSDate *now = [NSDate date];
    //获取当前所处的时区
    NSTimeZone *zone = [NSTimeZone systemTimeZone];
    //获取当前时区喝指定时间的时间差
    NSInteger seconds = [zone secondsFromGMTForDate:now];
    NSDate *newDate = [now dateByAddingTimeInterval:seconds];
    //格式化对象
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    //HH 24小时 hh12小时 Z时区
    formatter.dataFormat = @"yyyy年MM月dd日 HH时mm分ss秒"
    NSString *res = [formatter stringFromDate:now];
    //NSString ->NSDate
    NSDate * = [formatter dateFromString:str];
     
    
  • NSCalender

    NSDate *now = [NSDate date];
    NSCalenar *calendar = [NSCalender currentCalendar];
    //获取年
    NSDateComponents *cmps = [calendar components:NScalenderUnitYear fromDate:now];
    NSLog(@"year= %ld",cmps.year);
    
    • 一般情况下如果一个方法接收一个参数,这个参数如果时一个枚举,那么可以通过|符号,连接多个枚举值
    • 比较两个时间
    NSDateComponents *cmps = [calendar components:NScalenderUnitYear fromDate:now,toDate:date,options:0];
    
  • NSFileManager

  NSFileManager *manager = [NSFileManager defaultManager];
    // 可以判断文件
    BOOL flag = [manager fileExistsAtPath:@"/Users/LNJ/Desktop/lnj.txt"];
    NSLog(@"flag = %i", flag);
    // 可以判断文件夹
    BOOL dir = NO;
    flag = [manager fileExistsAtPath:@"/Users/LNJ/Desktop/未命名文件夹",isDirectory:&dir];
    NSLog(@"flag = %i,%i", flag,dir);
  • NSFileManager的文件访问
  • - (NSDictionary *)attributesOfItemAtPath:(NSString *)path error:(NSError **)error;
    • 获得path这个文件\文件夹的属性,通过这个获取到的文件夹的大小不准确,要想实现计算一个文件夹中所有文件的大小必须先拿到所有的文件,然后再获取所有文件的大小,然后再加
    NSFileManager *manager = [NSFileManager defaultManager];
    NSDictionary *dict = [manager attributesOfItemAtPath:@"/Users/LNJ/Desktop/lnj.txt" error:nil];
    NSLog(@"dit = %@", dict);
  • (NSArray *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError **)error;

    • 获取文件夹中所有文件,只能获取当前文件夹下所有文件,不能获取子文件夹下的问题
  • - (NSData *)contentsAtPath:(NSString *)path;

    • 获得文件内容
    NSFileManager *manager = [NSFileManager defaultManager];
    NSArray *paths = [manager contentsOfDirectoryAtPath:@"/Users/LNJ/Desktop/" error:nil];
    NSLog(@"paths = %@", paths);
  • (NSArray *)subpathsAtPath:(NSString *)path; 获取文件夹下所有的文件
  • (NSArray *)subpathsOfDirectoryAtPath:(NSString *)path error:(NSError **)error;
    • 获取文件夹下所有的文件
    NSFileManager *manager = [NSFileManager defaultManager];
    NSArray *paths = [manager subpathsAtPath:@"/Users/LNJ/Desktop/"];
    NSLog(@"paths = %@", paths);

##4.NSFileManager的文件操作

  • - (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error;

    • 拷贝
  • - (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error;

    • 移动(剪切)
  • - (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error;

    • 删除
  • (BOOL)createDirectoryAtPath:(NSString *)path withIntermediateDirectories:(BOOL)createIntermediates attributes:(NSDictionary *)attributes error:(NSError **)error;

    • 创建文件夹(createIntermediates为YES代表自动创建中间的文件夹),不能创建文件
    NSFileManager *manager = [NSFileManager defaultManager];
    BOOL flag = [manager createDirectoryAtPath:@"/Users/LNJ/Desktop/test" withIntermediateDirectories:YES attributes:nil error:nil];
    NSLog(@"flag = %i", flag);
  • (BOOL)createFileAtPath:(NSString *)path contents:(NSData *)data attributes:(NSDictionary *)attr;
    • 创建文件(NSData是用来存储二进制字节数据的),不能创建文件夹
    NSString *str = @"lnj";
    NSData  *data = [str dataUsingEncoding:NSUTF8StringEncoding];
    NSFileManager *manager = [NSFileManager defaultManager];
    BOOL flag = [manager createFileAtPath:@"/Users/LNJ/Desktop/abc.txt" contents:data attributes:nil];
    NSLog(@"flag = %i", flag);

  • 集合中对象的内存管理
    • 如果将一个对象添加到一个数组中,那么数组会对对象进行一次retain
    • 当数组对象释放之后,会给数组中所有的对象发送一条release消息
    • 当数组移除一个对象之后,会给这个对象发送一条release消息

Copy

  • 作用:利用一个源对象产生一个副本对象
  • 使用
    • 一个对象可以调用copy或mutableCopy方法来创建一个副本对象
    • copy:创建的时不可变副本
    • mutableCopy:创建的是可变副本
    • 前提:需要遵守NSCopying/NSMutableCopying,实现copyWithZone/copymutableWithZone
    • 一般情况下拷贝会生成一个新的对象
    • 如果是通过不可变对象巧用了copy,那么不会生成一个新的对象,因为原对象是不能修改的,拷贝出来的对象也是不能修改的,所以不会相互修改,OC为了对内存进行优化,所以不会生成新的对象
    • 如果没有生成系的呢对象,我们称之为浅拷贝,本质就是指针拷贝,系统会对原来的对象retain,需要对以前的对象进行release
    • 如果生成了新的对象,我们称之为深拷贝,本质就是会创建新的休对象,因为生成了新的对象,所以系统不会对以前的对象进行retain,但是因为生成了新的对象,我们需要对新的对象进行release
  • copy和property
    • copy的第一个用处:防止外界修改内部的数据 以后字符串属性都使用copy
    • copy的第二个用处:可以使用copy保存block,这样可以保住block中使用的外界对象的命,避免以后巧用block时,里面的外界对象已经释放
    • block默认在栈中,栈中的block访问到了外界的对象,不会对外界对象进行retain
    • block可以通过Block_copy()将block放入堆中,如果这时访问了外界对象,会对外界对象进行一次reatain
    • 如果是block使用copy并不是拷贝,而是转移
    • 只要给block发送一条release消息,block中是以哦那个的对象也会收到该消息
    • 如果对象中的block又用到了对象自己,那么为了避免内存泄漏,应该将对象修饰为——block

自定义实现copy

//1.想让自定义的对象能够被copy只需要遵守NSCopying协议
//2.实现协议中的-(id) copyWithZone:(NSZone *)zone
//3.在方法中创建一个副本对象,然后将当前对象的值赋值给副本并返回
- (id) copyWithZone:(NSZone *)zone
{
    //1.创建一个新的对象
    Person *p = [[[self clasee ] allocWithZone:zone] init];
    p.age - _age;
    return p;
}
//如果想让子类在copy时保留子类的属性,那么必须重写copyWithZone方法,在该方法中先调用父类创建副本设置值,然后再设置子类特有的值
//d单例模式下copy的实现
static Tools *_instance = nil;
+(instancetype) allocWithZone:(struce _NSZone *)zone
{
    //当前代码再多线程中有问题
    if(_instance==nil){
        _instance = [[super alloocWithZone:zone]init];
    }
    return _isntance;
    //多线程写法
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken,^{
         _instance = [[super alloocWithZone:zone]init];
    })
    return _isntance;
}
- (id) copyWithZone:(NSZone *)zone
{
    return instance;
}
//如果在MRC中要使用单例,还需要重写,release(什么都不做),retain(直接返回 _instance),retainCount(固定返回一,为了方便沟通,一般返回很大的值而不是1)方法
  • 判断项目是ARC还是MRC
 #if __has_feature(objc_arc)
     NSLog(@"ARC");
 #else
     NSLog(@"MRC");
 #endif

  • 使用宏抽取单例
```objc
#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