NSArray基本概念
1.NSArray的基本概念
-
什么是NSArray?
-
NSArray是OC中的数组类,开发中建议尽量使用NSArray替代C语言中的数组
-
C语言中数组的弊端
- int array[4] = {10, 89, 27, 76};
- 只能存放一种类型的数据.(类型必须一致)
- 不能很方便地动态添加数组元素、不能很方便地动态删除数组元素(长度固定)
-
-
NSArray的使用注意
- 只能存放任意OC对象, 并且是有顺序的
- 不能存储非OC对象, 比如int\float\double\char\enum\struct等
- 它是不可变的,一旦初始化完毕后,它里面的内容就永远是固定的, 不能删除里面的元素, 也不能再往里面添加元素
2.NSArray的创建方式
-
- (instancetype)array;
-
- (instancetype)arrayWithObject:(id)anObject;
-
- (instancetype)arrayWithObjects:(id)firstObj, ...;
-
- (instancetype)arrayWithArray:(NSArray *)array;
-
- (id)arrayWithContentsOfFile:(NSString *)path;
-
- (id)arrayWithContentsOfURL:(NSURL *)url;
3.NSArray 的使用注意事项
- NSArray直接使用NSLog()作为字符串输出时是小括号括起来的形式。
- NSArray中不能存储nil,因为NSArray认为nil是数组的结束(nil是数组元素结束的标记)。nil就是0。0也是基本数据类型,不能存放到NSArray中。
NSArray *arr = [NSArray arrayWithObjects:@"lnj", nil ,@"lmj",@"jjj", nil];
NSLog(@"%@", arr);
输出结果:
(
lnj
)
4.NSArray的常用方法
-
-
(NSUInteger)count;
-
获取集合元素个数
-
-
-
(id)objectAtIndex:(NSUInteger)index;
-
获得index位置的元素
-
-
-
(BOOL)containsObject:(id)anObject;
-
是否包含某一个元素
-
-
-
(id)lastObject;
-
返回最后一个元素
-
-
-
(id)firstObject;
-
返回最后一个元素
-
-
-
(NSUInteger)indexOfObject:(id)anObject;
-
查找anObject元素在数组中的位置(如果找不到,返回-1)
-
-
-
(NSUInteger)indexOfObject:(id)anObject inRange:(NSRange)range;
-
在range范围内查找anObject元素在数组中的位置
-
5.NSArray的简写形式
-
自从2012年开始, Xcode的编译器多了很多自动生成代码的功能, 使得OC代码更加精简
-
数组的创建
-
之前
[NSArray arrayWithObjects:@"Jack", @"Rose", @"Jim", nil]; -
现在
@[@"Jack", @"Rose", @"Jim"];
-
-
数组元素的访问
-
之前
[array objectAtIndex:0]; -
现在
array[0];
-
NSArray 遍历
1.NSArray的下标遍历
NSArray *arr = @[p1, p2, p3, p4, p5];
for (int i = 0; i < arr.count; ++i) {
Person *p = arr[i];
[p say];
}
2.NSArray的快速遍历
NSArray *arr = @[p1, p2, p3, p4, p5];
for (Person *p in arr) {
[p say];
}
3.NSArray 使用block进行遍历
[arr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(@"obj = %@, idx = %lu", obj, idx);
Person *p = obj;
[p say];
}];
4.NSArray给所有元素发消息
-
让集合里面的所有元素都执行aSelector这个方法
-
- (void)makeObjectsPerformSelector:(SEL)aSelector;
-
- (void)makeObjectsPerformSelector:(SEL)aSelector withObject:(id)argument;
-
// 让数组中所有对象执行这个方法
// 注意: 如果数组中的对象没有这个方法会报错
// [arr makeObjectsPerformSelector:@selector(say)];
[arr makeObjectsPerformSelector:@selector(eat:) withObject:@"bread"];
NSArray排序
1.NSArray排序
- Foundation自带类排序
NSArray *arr = @[@(1), @(9), @(5), @(2)];
NSArray *newArr = [arr sortedArrayUsingSelector:@selector(compare:)];
- 自定义类排序
NSArray *arr = @[p1, p2, p3, p4, p5];
// 默认按照升序排序
NSArray *newArr = [arr sortedArrayWithOptions:NSSortConcurrent usingComparator:^NSComparisonResult(Person *obj1, Person *obj2) {
return obj1.age > obj2.age;
}];
NSLog(@"%@", newArr);
NSArray文件读写
1.NSArray数据写入到文件中
NSArray *arr = @[@"lnj", @"lmj", @"jjj", @"xcq"];
BOOL flag = [arr writeToFile:@"/Users/LNJ/Desktop/persons.plist" atomically:YES];
NSLog(@"%i", flag);
2.从文件中读取数据到NSArray中
NSArray *newArr = [NSArray arrayWithContentsOfFile:@"/Users/LNJ/Desktop/persons.xml"];
NSLog(@"%@", newArr);
NSArray 与字符串
1.把数组元素链接成字符串
-
-
(NSString )componentsJoinedByString:(NSString )separator;
-
这是NSArray的方法, 用separator作拼接符将数组元素拼接成一个字符串
-
NSArray *arr = @[@"lnj", @"lmj", @"jjj", @"xcq"];
NSString *res = [arr componentsJoinedByString:@"*"];
NSLog(@"res = %@", res);
输出结果:
lnj*lmj*jjj*xcq
2.字符串分割方法
-
-
(NSArray )componentsSeparatedByString:(NSString )separator;
-
这是NSString的方法,将字符串用separator作为分隔符切割成数组元素
-
NSString *str = @"lnj-lmj-jjj-xcq";
NSArray *arr = [str componentsSeparatedByString:@"-"];
NSLog(@"%@", arr);
输出结果:
(
lnj,
lmj,
jjj,
xcq
)
NSMutableArray基本概念
1.NSMutableArray介绍
-
什么是NSMutableArray
- NSMutableArray是NSArray的子类
- NSArray是不可变的,一旦初始化完毕后,它里面的内容就永远是固定的, 不能删除里面的元素, 也不能再往里面添加元素
- NSMutableArray是可变的,随时可以往里面添加\更改\删除元素
2.NSMutableArray基本用法
- 创建空数组
NSMutableArray *arr = [NSMutableArray array];
- 创建数组,并且指定长度为5,此时也是空数组
NSMutableArray *arr2 = [[NSMutableArray alloc] initWithCapacity:5];
- 创建一个数组,包含两个元素
NSMutableArray *arr3 = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
- 调用对象方法创建数组
NSMutableArray *arr4 = [[NSMutableArray alloc] initWithObjects:@"1",@"2", nil];
-
-
(void)addObject:(id)object;
-
添加一个元素
-
-
-
(void)addObjectsFromArray:(NSArray *)array;
-
添加otherArray的全部元素到当前数组中
-
-
-
(void)insertObject:(id)anObject atIndex:(NSUInteger)index;
-
在index位置插入一个元素
-
-
-
(void)removeLastObject;
-
删除最后一个元素
-
-
-
(void)removeAllObjects;
-
删除所有的元素
-
-
-
(void)removeObjectAtIndex:(NSUInteger)index;
-
删除index位置的元素
-
-
-
(void)removeObject:(id)object;
-
删除特定的元素
-
-
-
(void)removeObjectsInRange:(NSRange)range;
-
删除range范围内的所有元素
-
-
-
(void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;
-
用anObject替换index位置对应的元素
-
-
-
(void)exchangeObjectAtIndex:(NSUInteger)idx1 withObjectAtIndex:(NSUInteger)idx2;
-
交换idx1和idx2位置的元素
-
3.NSMutableArray 错误用法
- 不可以使用@[]创建可变数组
NSMutableArray *array = @[@"lnj", @"lmj", @"jjj"];
// 报错, 本质还是不可变数组
[array addObject:@“Peter”];
NSDictionary基本概念
1.NSDictionar基本概念
-
什么是NSDictionary
- NSDictionary翻译过来叫做”字典”
- 日常生活中,“字典”的作用:通过一个拼音或者汉字,就能找到对应的详细解释
- NSDictionary的作用类似:通过一个key,就能找到对应的value
- NSDictionary是不可变的, 一旦初始化完毕, 里面的内容就无法修改
2.NSDictionary的创建
+ (instancetype)dictionary;
+ (instancetype)dictionaryWithObject:(id)object forKey:(id <NSCopying>)key;
+ (instancetype)dictionaryWithObjectsAndKeys:(id)firstObject, ...;
+ (id)dictionaryWithContentsOfFile:(NSString *)path;
+ (id)dictionaryWithContentsOfURL:(NSURL *)url;
-
NSDictionary创建简写
-
以前
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:@"lnj", @"name", @"12345678", @"phone", @"天朝", @"address", nil]; -
现在
NSDictionary *dict = @{@"name":@"lnj", @"phone":@"12345678", @"address":@"天朝"};
-
-
NSDictionary获取元素简写
-
以前
[dict objectForKey:@"name”]; -
现在
dict[@"name”];
-
-
键值对集合的特点
- 字典存储的时候,必须是"键值对"的方式来存储(同时键不要重复)
- 键值对中存储的数据是"无序的".
- 键值对集合可以根据键, 快速获取数据.
3.NSDictionary的遍历
-
-
(NSUInteger)count;
-
返回字典的键值对数目
-
-
-
(id)objectForKey:(id)aKey;
-
根据key取出value
-
- 快速遍历
NSDictionary *dict = @{@"name":@"lnj", @"phone":@"12345678", @"address":@"天朝"};
for (NSString *key in dict) {
NSLog(@"key = %@, value = %@", key, dict[key]);
}
- Block遍历
[dict enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL *stop) {
NSLog(@"key = %@, value = %@", key, obj);
}];
4.NSDictionary文件操作
-
将字典写入文件中
-
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile;
-
- (BOOL)writeToURL:(NSURL *)url atomically:(BOOL)atomically;
- 存结果是xml文件格式,但苹果官方推荐为plist后缀。
-
-
示例
NSDictionary *dict = @{@"name":@"lnj", @"phone":@"12345678", @"address":@"天朝"};
BOOL flag = [dict writeToFile:@"/Users/LNJ/Desktop/dict.plist" atomically:YES];
NSLog(@"flag = %i", flag);
- 从文件中读取字典
NSDictionary *newDict = [NSDictionary dictionaryWithContentsOfFile:@"/Users/LNJ/Desktop/dict.plist"];
NSLog(@"newDict = %@", newDict);
NSMutableDictionary基本概念
1.NSMutableDictionary 基本概念
-
什么是NSMutableDictionary
- NSMutableDictionary是NSDictionary的子类
- NSDictionary是不可变的,一旦初始化完毕后,它里面的内容就永远是固定的,不能删除里面的元素, 也不能再往里面添加元素
- NSMutableDictionary是可变的,随时可以往里面添加\更改\删除元素
2.NSMutableDictionary的常见操作
-
-
(void)setObject:(id)anObject forKey:(id )aKey;
-
添加一个键值对(会把aKey之前对应的值给替换掉)
-
-
-
(void)removeObjectForKey:(id)aKey;
-
通过aKey删除对应的value
-
-
-
(void)removeAllObjects;
-
删除所有的键值对
-
3.NSMutableDictionary的简写
-
设置键值对
-
以前
[dict setObject:@"Jack" forKey:@"name”]; -
现在
dict[@"name"] = @"Jack";
-
4.NSDictionary和NSArray对比
-
NSArray和NSDictionary的区别
- NSArray是有序的,NSDictionary是无序的
- NSArray是通过下标访问元素,NSDictionary是通过key访问元素
-
NSArray的用法
-
创建
@[@"Jack", @"Rose"] (返回是不可变数组) -
访问
id d = array[1]; -
赋值
array[1] = @"jack";
-
-
NSDictionary的用法 +创建
@{ @"name" : @"Jack", @"phone" : @"10086" } (返回是不可变字典)-
访问
id d = dict[@"name"]; -
赋值
dict[@"name"] = @"jack";
-
常见的结构体
1.NSPoint和CGPoint
- CGPoint和NSPoint是同义的
typedef CGPoint NSPoint;
CGPoint的定义
struct CGPoint {
CGFloat x;
CGFloat y;
};
typedef struct CGPoint CGPoint;
typedef double CGFloat;
-
CGPoint代表的是二维平面中的一个点
- 可以使用CGPointMake和NSMakePoint函数创建CGPoint
2.NSSize和CGSize
- CGSize和NSSize是同义的
typedef CGSize NSSize;
CGSize的定义
struct CGSize {
CGFloat width;
CGFloat height;
};
typedef struct CGSize CGSize;
-
CGSize代表的是二维平面中的某个物体的尺寸(宽度和高度)
- 可以使用CGSizeMake和NSMakeSize函数创建CGSize
3.NSRect和CGRect
- CGRect和NSRect是同义的
typedef CGRect NSRect;
CGRect的定义
struct CGRect {
CGPoint origin;
CGSize size;
};
typedef struct CGRect CGRect;
-
CGRect代表的是二维平面中的某个物体的位置和尺寸
- 可以使用CGRectMake和NSMakeRect函数创建CGRect
4.常见的结构体使用注意
-
苹果官方推荐使用CG开头的:
- CGPoint
- CGSize
- CGRect
NSNumber
1.NSNumber基本概念
-
NSArray\NSDictionary中只能存放OC对象,不能存放int\float\double等基本数据类
-
如果真想把基本数据(比如int)放进数组或字典中,需要先将基本数据类型包装成OC对象
-
NSNumber可以将基本数据类型包装成对象,这样就可以间接将基本数据类型存进NSArray\NSDictionary中
2.NSNumber的创建
- 以前
+ (NSNumber *)numberWithInt:(int)value;
+ (NSNumber *)numberWithDouble:(double)value;
+ (NSNumber *)numberWithBool:(BOOL)value;
-
现在
@10; @10.5; @YES; @(num);
3.从NSNumber对象中的到基本类型数据
- (char)charValue;
- (int)intValue;
- (long)longValue;
- (double)doubleValue;
- (BOOL)boolValue;
- (NSString *)stringValue;
- (NSComparisonResult)compare:(NSNumber *)otherNumber;
- (BOOL)isEqualToNumber:(NSNumber *)number;
NSValue
1.NSValue基本概念
-
NSNumber是NSValue的子类, 但NSNumber只能包装数字类型
-
NSValue可以包装任意值
- 因此, 可以用NSValue将结构体包装后,加入NSArray\NSDictionary中
2. 常见结构体的包装
- 为了方便 结构体 和NSValue的转换,Foundation提供了以下方法
- 将结构体包装成NSValue对象
+ (NSValue *)valueWithPoint:(NSPoint)point;
+ (NSValue *)valueWithSize:(NSSize)size;
+ (NSValue *)valueWithRect:(NSRect)rect;
- 从NSValue对象取出之前包装的结构体
- (NSPoint)pointValue;
- (NSSize)sizeValue;
- (NSRect)rectValue;
3.任意数据的包装
- NSValue提供了下列方法来包装任意数据
+ (NSValue *)valueWithBytes:(const void *)value objCType:(const char *)type;
+ value参数 : 所包装数据的地址
+ type参数 : 用来描述这个数据类型的字符串, 用@encode指令来生成
- 从NSValue中取出所包装的数据
- (void)getValue:(void *)value;
NSDate
1.NSDate基本概念
- NSDate可以用来表示时间, 可以进行一些常见的日期\时间处理
- 一个NSDate对象就代表一个时间
- [NSDate date]返回的就是当前时间
NSDate *now = [NSDate date];
NSLog(@"now = %@", now);
// 设置转换后的目标日期时区
NSTimeZone *zone = [NSTimeZone systemTimeZone];
// 得到源日期与世界标准时间的偏移量
NSInteger interval = [zone secondsFromGMTForDate: date];
NSLog(@"interval = %lu", interval);
// 在当前时间基础上追加时区差值
now = [now dateByAddingTimeInterval:interval];
NSLog(@"%@", date);
2.格式化日期
- NSDate -> NSString
// 1.创建时间
NSDate *now = [NSDate date];
// 2.创建时间格式化
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
// 3.指定格式
formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
// 4.格式化时间
NSString *str = [formatter stringFromDate:now];
NSLog(@"%@", str);
- NSString -> NSDate
NSString *str = @"2015-06-28 19:53:24";
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
NSDate *date = [formatter dateFromString:str];
NSLog(@"%@", date);
3.日期时间对象
- 结合NSCalendar和NSDate能做更多的日期\时间处理
- 获得NSCalendar对象
NSCalendar *calendar = [NSCalendar currentCalendar];
- 获得年月日
- (NSDateComponents *)components:(NSCalendarUnit)unitFlags fromDate:(NSDate *)date;
NSDate *date = [NSDate date];
// 1.创建日历对象
NSCalendar *calendar = [NSCalendar currentCalendar];
// 2.利用日历对象获取年月日时分秒
NSCalendarUnit type = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
NSDateComponents *cmps =[calendar components:type fromDate:date];
NSLog(@"year = %lu", cmps.year);
NSLog(@"month = %lu", cmps.month);
NSLog(@"day = %lu", cmps.day);
NSLog(@"hour = %lu", cmps.hour);
NSLog(@"minute = %lu", cmps.minute);
NSLog(@"second = %lu", cmps.second);
NSLog(@"date = %@", date);
- 比较两个日期的差距
- (NSDateComponents *)components:(NSCalendarUnit)unitFlags fromDate:(NSDate *)startingDate toDate:(NSDate *)resultDate options:(NSCalendarOptions)opts;
// 1.确定时间
NSString *time1 = @"2015-06-23 12:18:15";
NSString *time2 = @"2015-06-28 10:10:10";
// 2.将时间转换为date
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
NSDate *date1 = [formatter dateFromString:time1];
NSDate *date2 = [formatter dateFromString:time2];
// 3.创建日历
NSCalendar *calendar = [NSCalendar currentCalendar];
NSCalendarUnit type = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
// 4.利用日历对象比较两个时间的差值
NSDateComponents *cmps = [calendar components:type fromDate:date1 toDate:date2 options:0];
// 5.输出结果
NSLog(@"两个时间相差%ld年%ld月%ld日%ld小时%ld分钟%ld秒", cmps.year, cmps.month, cmps.day, cmps.hour, cmps.minute, cmps.second);
NSFileManager
1.NSFileManager介绍
-
什么是NSFileManager
- 顾名思义, NSFileManager是用来管理文件系统的
- 它可以用来进行常见的文件\文件夹操作
-
NSFileManager使用了单例模式
-
使用defaultManager方法可以获得那个单例对象
[NSFileManager defaultManager]
-
2.NSFileManager用法
-
-
(BOOL)fileExistsAtPath:(NSString *)path;
-
path这个文件\文件夹是否存在
-
NSFileManager *manager = [NSFileManager defaultManager];
// 可以判断文件
BOOL flag = [manager fileExistsAtPath:@"/Users/LNJ/Desktop/lnj.txt"];
NSLog(@"flag = %i", flag);
// 可以判断文件夹
flag = [manager fileExistsAtPath:@"/Users/LNJ/Desktop/未命名文件夹"];
NSLog(@"flag = %i", flag);
-
-
(BOOL)fileExistsAtPath:(NSString )path isDirectory:(BOOL )isDirectory;
-
path这个文件\文件夹是否存在, isDirectory代表是否为文件夹
-
NSFileManager *manager = [NSFileManager defaultManager];
BOOL directory = NO;
BOOL flag = [manager fileExistsAtPath:@"/Users/LNJ/Desktop/未命名文件夹" isDirectory:&directory];
NSLog(@"flag = %i, directory = %i", flag, directory);
-
-
(BOOL)isReadableFileAtPath:(NSString *)path;
-
path这个文件\文件夹是否可读
-
-
-
(BOOL)isWritableFileAtPath:(NSString *)path;
-
path这个文件\文件夹是否可写
-
系统目录不允许写入
-
-
-
(BOOL)isDeletableFileAtPath:(NSString *)path;
-
path这个文件\文件夹是否可删除
-
系统目录不允许删除
-
3.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;
-
获得path的当前子路径
-
-
-
(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;
-
获得path的所有子路径
-
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);
集合对象的内存管理
1.集合对象的内存管理
- 当一个对象加入到集合中,那么该对象的引用计数会+1
- 当集合被销毁的时候,集合会向集合中的元素发送release消息
NSMutableArray *arr = [[NSMutableArray alloc] init];
Person *p = [[Person alloc] init];
NSLog(@"retainCount = %lu", [p retainCount]);
[arr addObject:p];
NSLog(@"retainCount = %lu", [p retainCount]);
[p release];
NSLog(@"retainCount = %lu", [p retainCount]);
[arr release];
- 当一个对象加入到集合中,那么该对象的引用计数会+1
- 当把一个对象从集合中移除时,会向移除的元素发送release消息
NSMutableArray *arr = [[NSMutableArray alloc] init];
Person *p = [[Person alloc] init];
NSLog(@"retainCount = %lu", [p retainCount]);
[arr addObject:p];
NSLog(@"retainCount = %lu", [p retainCount]);
[arr removeObject:p];
NSLog(@"retainCount = %lu", [p retainCount]);
[p release];
[arr release];
2.集合对象内存管理总结
-
1.官方内存管理原则
- 1> 当调用alloc、new、copy(mutableCopy)方法产生一个新对象的时候,就必须在最后调用一次release或者autorelease
- 2> 当调用retain方法让对象的计数器+1,就必须在最后调用一次release或者autorelease
-
2.集合的内存管理细节
- 1> 当把一个对象添加到集合中时,这个对象会做了一次retain操作,计数器会+1
- 2> 当一个集合被销毁时,会对集合里面的所有对象做一次release操作,计数器会-1
- 3> 当一个对象从集合中移除时,这个对象会一次release操作,计数器会-1
-
3.普遍规律
- 1> 如果方法名是add\insert开头,那么被添加的对象,计数器会+1
- 2> 如果方法名是remove\delete开头,那么被移除的对象,计数器-1
Copy
1.copy基本概念
-
什么是copy
- Copy的字面意思是“复制”、“拷贝”,是一个产生副本的过程
-
常见的复制有:文件复制
- 作用:利用一个源文件产生一个副本文件
-
特点:
- 修改源文件的内容,不会影响副本文件
- 修改副本文件的内容,不会影响源文件
-
OC中的copy
- 作用:利用一个源对象产生一个副本对象
-
特点:
- 修改源对象的属性和行为,不会影响副本对象
- 修改副本对象的属性和行为,不会影响源对象
2.Copy的使用
-
如何使用copy功能
- 一个对象可以调用copy或mutableCopy方法来创建一个副本对象
- copy : 创建的是不可变副本(如NSString、NSArray、NSDictionary)
- mutableCopy :创建的是可变副本(如NSMutableString、NSMutableArray、NSMutableDictionary)
-
使用copy功能的前提
- copy : 需要遵守NSCopying协议,实现copyWithZone:方法
@protocol NSCopying
- (id)copyWithZone:(NSZone *)zone;
@end
-
使用mutableCopy的前提
- 需要遵守NSMutableCopying协议,实现mutableCopyWithZone:方法
@protocol NSMutableCopying
- (id)mutableCopyWithZone:(NSZone *)zone;
@end
2.深复制和浅复制
-
浅复制(浅拷贝,指针拷贝,shallow copy)
- 源对象和副本对象是同一个对象
- 源对象(副本对象)引用计数器+1,相当于做一次retain操作
- 本质是:没有产生新的对象
NSString *srcStr = @"lnj";
NSString *copyStr = [srcStr copy];
NSLog(@"src = %p, copy = %p", srcStr, copyStr);
-
深复制(深拷贝,内容拷贝,deep copy)
- 源对象和副本对象是不同的两个对象
- 源对象引用计数器不变,副本对象计数器为1(因为是新产生的)
- 本质是:产生了新的对象
NSString *srcStr = @"lnj";
NSMutableString *copyStr = [srcStr mutableCopy];
NSLog(@"src = %p, copy = %p", srcStr, copyStr);
NSLog(@"src = %@, copy = %@", srcStr, copyStr);
[copyStr appendString:@" cool"];
NSLog(@"src = %@, copy = %@", srcStr, copyStr);
NSMutableString *srcStr = [NSMutableString stringWithFormat:@"lnj"];
NSString *copyStr = [srcStr copy];
[srcStr appendString:@" cool"];
NSLog(@"src = %p, copy = %p", srcStr, copyStr);
NSLog(@"src = %@, copy = %@", srcStr, copyStr);
NSMutableString *srcStr = [NSMutableString stringWithFormat:@"lnj"];
NSMutableString *copyStr = [srcStr mutableCopy];
[srcStr appendString:@" cool"];
[copyStr appendString:@" 520it"];
NSLog(@"src = %p, copy = %p", srcStr, copyStr);
NSLog(@"src = %@, copy = %@", srcStr, copyStr);
- 只有源对象和副本对象都不可变时,才是浅复制,其它都是深复制
copy与内存管理
1.copy与内存管理
-
浅拷贝
-
原对象引用计数器+1
-
必须对原对象进行释放
char *cstr = "this is a c string"; NSString *str1 = [[NSString alloc] initWithUTF8String:cstr]; NSLog(@"str = %lu", [str1 retainCount]); NSString *str2 = [str1 copy]; NSLog(@"str = %lu", [str1 retainCount]); [str2 release];必须做一次release
-
-
深拷贝
-
必须释放新对象
char *cstr = "this is a c string"; NSString *str1 = [[NSString alloc] initWithUTF8String:cstr]; NSLog(@"str = %lu", [str1 retainCount]); NSMutableString *str2 = [str1 mutableCopy]; NSLog(@"str = %lu", [str1 retainCount]); [str2 release]; // 必须做一次release
-
@property中的copy关键字
1.@property中的copy的作用
- 防止外界修改内部的值
@interface Person : NSObject
@property (nonatomic, retain) NSString *name;
@end
NSMutableString *str = [NSMutableString stringWithFormat:@"lnj"];
Person *p = [[Person alloc] init];
p.name = str;
// person中的属性会被修改
[str appendString:@" cool"];
NSLog(@"name = %@", p.name);
-
防止访问对象对象已经释放
-
不用copy情况
Person *p = [[Person alloc] init]; p.name = @"lnj"; Dog *d = [[Dog alloc] init]; d.age = 10; NSLog(@"retainCount = %lu", [d retainCount]); // 1 p.pBlock = ^{ // 报错, 调用之前就销毁了 NSLog(@"age = %d", d.age); }; [d release]; // 0 p.pBlock(); [p release]; -
用copy情况
-
Person *p = [[Person alloc] init];
p.name = @"lnj";
Dog *d = [[Dog alloc] init];
d.age = 10;
NSLog(@"retainCount = %lu", [d retainCount]); // 1
p.pBlock = ^{
// 会对使用到的外界对象进行一次retain
NSLog(@"age = %d", d.age);
NSLog(@"retainCount = %lu", [d retainCount]); // 1
};
[d release]; // 1
p.pBlock();
[p release];
2.@property内存管理策略选择
-
非ARC
- 1> copy : 只用于NSString\block
- 2> retain : 除NSString\block以外的OC对象
- 3> assign :基本数据类型、枚举、结构体(非OC对象),当2个对象相互引用,一端用retain,一端用assign
-
ARC
- 1> copy : 只用于NSString\block
- 2> strong : 除NSString\block以外的OC对象
- 3> weak : 当2个对象相互引用,一端用strong,一端用weak
- 4> assgin : 基本数据类型、枚举、结构体(非OC对象)
自定义的类实现copy操作
1.自定义类实现copy操作
-
让类遵守NSCopying协议
-
实现 copyWithZone:方法,在该方法中返回一个对象的副本即可。
-
在copyWithZone方法中,创建一个新的对象,并设置该对象的数据与现有对象一致, 并返回该对象.
zone: 表示空间,分配对象是需要内存空间的,如果指定了zone,就可以指定 新建对象对应的内存空间。但是:zone是一个非常古老的技术,为了避免在堆中出现内存碎片而使用的。在今天的开发中,zone几乎可以忽略
-
无父类实现
-(id)copyWithZone(NSZone *)zone{
CustomMode *custom = [[[self class] copyWithZone:zone] init];
Custom ->_a = [_a copyWithZone:zone];
Custom -> _c = _c;//不是对象的 直接赋值
Return custom;
}
-
有父类实现
-
不调用父类方法, 无法拷贝父类中继承的属性
-
不重新父类copyWithZone, 无法拷贝本来中的特有属性
-(id)copyWithZone(NSZone *)zone{ CustomModel *custom = [super copyWithZone:zone]; …. Return custom; }
-
单例设计模式
1.单例模式概念
-
什么是单例模式:(Singleton)
- 单例模式的意图是是的类的对象成为系统中唯一的实例,供一个访问点,供客户类 共享资源。
-
什么情况下使用单例?
- 1、类只能有一个实例,而且必须从一个为人熟知的访问点对其进行访问,比如工厂方 法。
- 2、这个唯一的实例只能通过子类化进行扩展,而且扩展的对象不会破坏客户端代码。
-
单例设计模式的要点:
-
- 某个类只能有一个实例。
- 2)他必须自行创建这个对象
- 3)必须自行向整个系统供这个实例;
- 4)为了保证实例的唯一性,我们必须将
- 5)这个方法必须是一个静态类
-
2.简单的单例模式实现
#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