一 IOS的内存管理
1.再IOS中对于每一个对象都会有一个对象计数器
2.IOS内存管理分为三种(MRC:手动释放内存 autorelaese:自动释放内存 ARC:自动引用计数)
(1)MRC 黄金法则: 一旦对象被创建(new alloc init copy retain),那么就由创建者释放。总结:谁创建,谁释放,谁retain,谁release。其中 retain 计数器加一 release 计数器减一。
使用方式:Target-> 搜索auto -> 找到Automatic Reference Counting 将其修改成NO
如情况一
//实例化Person对象 此时计数器为 1
Person *person1 = [[Person alloc]init];
//计数器加一
[person1 retain];
//计数器减一
[person1 release];
//打印当前计数器的值
NSLog(@"%li",[person1 retainCount]);
如情况二
//若只是创建一个person的指针 则计数器为 0
Person *person;
// 因为计数器的release是针对于对象计数器而言的,此时没有对象,所以即使[person retain] 打印出的计数器的值永远为0
如情况三
//创建了一个Dog对象
Dog *dog = [[Dog alloc]init];
//给人设置狗对象后,狗被引用一次 , 此时dog的retainCount为2
[person1 setDog:dog];
//类的类属性,设置和获取,set和get,现在可以简便化:
//@property \ @synthesize
//noatomic 高性能 atomic 低性能
//retain 代表针对于这个对象计数器+1
//readonly 代表对象只读
//setter 、getter 更改这个方法的名字
//assign 赋值通过针对于简单的数据类型
//3.释放内存用dealloc,一般在.m文件中实现 [super dealloc],只要调用对象的release方法,就会进入dealloc;
//4.类的类属性,set和get,现在可以简化为:@property @synthesize
//noatomic 高性能 atomic 低性能 readonly 代表对象只读
//setter 、getter 更改这个方法的名字
//5.有内存管理,用retain OC语言的数据类型可内存管理
// 如 @property( nonatomic , retain ) Dog *dog;
无内存管理 用assign c语言的基础数据类型无内存管理
//如 @property( nonatomic , assign ) int age;
3.关于retainCount为1,-1,0,无穷大的条件
(1)为1时 是对象计数 当创建对象时或者对象即将释放时为1,即对象计数器最小为1.
(2)为0时 当一个指针的值为空时。
(3)为-1或无穷大时 当一个数据类型被赋值时。
不可变对象通常被称为静态常量,这类对象一般不能用于计数,而可变的对象是可以用来计数的
二.浅拷贝和深拷贝
1.浅拷贝 copy 通常指针对于指针进行拷贝,特点为:通过多个指针指向同一片内存地址。
如 NSString *string = @"123";
NSString *stringI = [string copy];
这时string和stringI输出的内存地址相同
2.深拷贝 mutableCopy 指的是赋值内容并且重新创建一块内存地址
如 NSString *stringII = [string mutableCopy];
这时string和stringII输出的内存地址不相同
三.自动释放池(autorelease)
1.autorelease通常有作用域,当在作用域范围内的创建特定的对象时,出去作用域计数器就会-1
如 @autorelease{
Person *personP = [[Person alloc]autorelease];
//特定的autorelease创建的对象才在出去作用域的时候release 计数器 -1
此时也可以在作用域里面对person retain或release,但是不管怎样,出作用域时计数器只会减一
}
四.ARC(自动引用计数)
1.ARC会追踪你的对象并决定哪一个仍会使用而哪一个不 会再用到
• 如果启用了ARC,编译器会自动插入retain和release 语句
• 通过Xcode启用或禁用可
2.ARC的特点
(1)不允许调用release,retain,retainCount
(2)允许重写dealloc,但是不允许调用[super dealloc]
(3)@property的参数:
• strong:相当于原来的retain(适用于OC对象),成员变量是强指针
• weak:相当于原来的assign,(适用于OC对象类型),成员变量是弱指针
• assign:适用于非OC对象类型(基础类型)
相互引用时注意的问题:
1.在一方的.h文件中要导入另一方的头文件,另一方的.h文件要引用@class name
如 #import <Foundation/Foundation.h>
#import "MicroBlog.h"
@interface Person : NSObject
#import <Foundation/Foundation.h>
@class Person;
@interface MicroBlog : NSObject
2.一方使用强指针,另一方使用弱指针
3.引用@class的一方若要在.m文件中实现相应的方法,要在.m文件中再次导入另一方的头文件
如 #import "MicroBlog.h"
#import "Person.h"
@implementation MicroBlog
ARC实例:
person.h
#import <Foundation/Foundation.h>
#import "MicroBlog.h"
@interface Person : NSObject
@property (nonatomic ,strong)MicroBlog *microBlog;
@property (nonatomic,strong) NSString *name;
@property (nonatomic,weak) NSString *comment;
-(void)printf;
@end
person.m
#import "Person.h"
@implementation Person
-(void)printf
{
NSLog(@"%@在%@发布了第%i条微博,内容是:%@",_name, [_microBlog time],[_microBlog second],[_microBlog content]);
}
@end
microBlog.h
#import <Foundation/Foundation.h>
@class Person;
@interface MicroBlog : NSObject
@property(nonatomic,weak) Person *person;
@property(nonatomic,weak) Person *commentPerson;
@property(nonatomic,weak) NSString *content;
@property(nonatomic,weak) NSString *time;
@property(nonatomic,assign) int second;
-(void)printComment;
@end
microBlog.m
#import "MicroBlog.h"
#import "Person.h"
@implementation MicroBlog
-(void)printComment
{
NSLog(@"%@评论了你的微博:%@",[_commentPerson name],[_commentPerson comment]);
}
@end
main.m
#import <Foundation/Foundation.h>
#import "MicroBlog.h"
#import "Person.h"
int main(int argc, const char * argv[]) {
Person *person = [[Person alloc]init];
person.name = @"小明";
Person *personI = [[Person alloc]init];
personI.name = @"小花";
personI.comment = @"赞";
MicroBlog *microBlog = [[MicroBlog alloc]init];
microBlog.content = @"暑假去旅游";
microBlog.second = 2;
NSDate *date = [NSDate date];
NSDateFormatter *dateformatter = [[NSDateFormatter alloc]init];
[dateformatter setDateFormat:@"yyyy-MM-dd HH-mm-ss"];
NSString *nowTime = [dateformatter stringFromDate:date];
microBlog.time = nowTime;
microBlog.commentPerson = personI;
//这是容易忘记的一步,要给person对象设置microBlog
person.microBlog = microBlog;
[person printf];
[microBlog printComment];
return 0;
}
五 KVC: key value coding 键值编码。
可以将对象属性变为一个键从而去设置属性的值
一.KVC的四种方法
1.setValue:forKey: 设置类属性的值
如: [hunter setValue:"光头强" forKey:"name"];
2.valueForKey: 取相对应的属性的值
如: [person valueForKey:@"name"]
3.setValue:forKeyPath: 根据键路径设置类属性的值
4.valueForKeyPath: 根据键路径取相对应的属性的值(对象属性是另一个的类属性)
如: [person valueForKeyPath:@"hunterPig.weight"]
二.KVC中常用的几种计算方式
如:
//创建森林对象
Forest *forest = [[Forest alloc]init];
//把三只猪放到同一个数组中
NSArray *array = [boarI,boarIII,boarII,boarII];
//将存放猪的数组放入到森林数组(是森里类的一个属性)里 即完成了
[forest setValue:array forKey:"forestArray"];
1.count: 计算数量
//求猪的个数
NSLog("猪的个数是:%",[forest valueForKeyPath:"forestArray.@count"])
2.max: 计算最大值
//求猪重量的最大值
NSLog("猪的重量的最大值是:%",[forest valueForKeyPath:"[email protected]"]);
3.min: 计算最小值
//求猪重量的最小值
NSLog("猪的重量的最小值是:%",[forest valueForKeyPath:"[email protected]"]);
4.sum: 计算总和
//求猪重量的总和
NSLog("猪的重量的总值是:%",[forest valueForKeyPath:"[email protected]"]);
5.avg: 计算平均值
//求猪重量的平均值
NSLog("猪的重量的平均值是:%",[forest valueForKeyPath:"[email protected]"]);
三.针对数组
1. 自动去除数组重复的元素 distinctUnionOfObjects
NSArray *arr = [1,@2,@3,@2];
NSLog("数组里的元素有%@",[arr valueForKeyPath:"@distinctUnionOfObjects.self"]);
2. 不会自动去除数组多余的元素 @unionOfObjects
NSLog(@"数组里的元素有%@",[arrvalueForKeyPath:@"@unionOfObjects.self"]);
六 KVO: key value observing 当类属性的值发生改变时,会自动调用监听回调方法进行提醒
一:KVO中必有的三种方法
1.给对象添加监听者
[被监听者 addObserver:监听者 forKeyPath:@“类属性” options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
注意:被监听者和监听者可以是同一类实例化出的两个相同或不同的对象,也可以是不同类分别实例化出的对象
类属性可以是自己类的属性,也可以是对象类属性里面的属性
2.给对象移除监听者
[被监听者 removeObserver:监听者 forKeyPath:@"类属性"];
3.一旦类属性的值发生改变,监听回调以下方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
其中的四个属性:
keyPath :监听的key
object: 监听的对象
change: 返回新值和旧值(在添加监听者时需要设置)
context:上下文内容
它们的输出方式为:
NSLog("keyPath :%",keyPath);
NSLog("object :%",[object valueForKey:“类属性”]);
NSLog("change :%",change);
取出旧值:[change valueForKey:“old”];
取出新值:[change valueForKey:@“new”];
类属性值发生改变时是发生在添加和移除监听者之间的
举例说明KVO
人监听账户里的账户名和账户余额属性
main.m
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Account.h"
int main(int argc, const char * argv[]) {
//实例化person对象
Person *person = [[Person alloc]init];
//实例化一个账户
Account *account = [[Account alloc]init];
//给账户设置余额和账户名
[account setValue:@"200" forKey:@"money"];
[account setValue:@"123456" forKey:@"name"];
//给账户添加监听者来监听它的余额
[account addObserver:person forKeyPath:@"money" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
//给账户添加监听者来监听它的zhanghum
[account addObserver:person forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
//给账户重新设置余额和账户名
[account setValue:@"500" forKey:@"money"];
[account setValue:@"678954" forKey:@"name"];
//移除监听者
[account removeObserver:person forKeyPath:@"money"];
[account removeObserver:person forKeyPath:@"name"];
return 0;
}
person.m
#import "Person.h"
@implementation Person
//一旦被监听者的属性值发生改变,立即进入此方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
// NSLog(@"keyPath :%@",keyPath);
// NSLog(@"object :%@",[object valueForKey:@"money"]);
// NSLog(@"change :%@",change);
//当同时监听对象的多个属性时,输出改变值时需要进行判断
if ([keyPath isEqualToString:@"money"]) {
NSLog(@"您的账户余额发生改变,当前金额为%@元",[object valueForKey:keyPath]);
NSLog(@"您当前余额总共有%i元",[[change valueForKey:@"old"]intValue] + [[change valueForKey:@"new"]intValue]);
}
else
if ([keyPath isEqualToString:@"name"]) {
NSLog(@"您的账户名发生改变,当前账户名为:%@",[object valueForKey:keyPath]);
}
}
@end