一直想花时间把之前项目常用的控件及功能整理出来,本打算把AFNetworking二次封装下,想到需要使用单例来封装AFHTTPSessionManager类,看到很多使用单例的方法处理不够严谨,这里还是单独拎出来,把单例的用法讲一下。
知识点
1.alloc int 的功能及调用顺序
2.allocWithZone 的调用
3.单列的实现原理及正确使用
1.alloc init 的使用
我们通常创建一个oc对象的时候,一般都是使用alloc init方法。
alloc 给对象分配内存空间
init 初始化该对象
我们通常会这么创建对象
person * p1 = [[person alloc]init];
这样操作,就完成一个对象的创建过程。
那么系统在什么时候给对象分配内存空间呢?其实当我们执行alloc方法的时候,系统会自动调用他如下方法来为对象分配内存地址
+ (instancetype)allocWithZone:(struct _NSZone *)zone OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
我们点击进去可以查看到
系统方法.png
这是NSObject的方法,也就是只要是oc对象,都拥有此方法。
其实创建对象,也就是创建一个类型的指针,创建的时候分配内存地址。
person * p1 = [[person alloc]init];//执行顺序如下
// 1.alloc
// 2.+ (instancetype)allocWithZone
// 3.init
还是看代码来验证
我们创建一个常用的person类
#import "person.h"
@implementation person
//调用顺序 如下
-(instancetype)init{
if (self = [super init])// 3
{
}
return self;// 4
}
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
static person *person = nil;
person = [super allocWithZone:zone];// 1
return person;// 2
}
在外部调用
person * p1 = [[person alloc]init];
编译项目,看行执行顺序
alloc顺序.gif
编译顺序,如上注释。
我们来创建三个person对象
person * p1 = [[person alloc]init];
person * p2 = [[person alloc]init];
person * p3 = [[person alloc]init];
NSLog(@"%@ \n %@ \n %@",p1,p2,p3);
打印内存地址
<person: 0x60000001fa90>
<person: 0x60000001fb60>
<person: 0x60000001fb50>
每次创建对象,都会分配新的内存地址。其实创建oc对象的就是创建了一个对象类型的指针,分配内存地址,之后让指针指向自己的内存控件,之后再init方法实现对对象的初始化,拥有调用自己属性和方法的权利。
单例使用常用方法
单例是在整个工程中只拥有一个该类实例。
1.单例使用
我们来看下,我们通常创建单例的方法
+(instancetype)sharedInstance;
{
static person *single = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
single =[[self alloc]init];
});
return single;
}
这里,我们使用GCD提供的dispatch_once的方法,在这个方法内部使用创建对象的[[self alloc]init]方法,保证这个方法只执行一遍。
我们创建单列如下
person * p4 = [person sharedInstance];
person * p5 = [person sharedInstance];
person * p6 = [person sharedInstance];
NSLog(@"%@ \n %@ \n %@",p4,p5,p6);
打印内存地址如下
<person: 0x60c00000cc00>
<person: 0x60c00000cc00>
<person: 0x60c00000cc00>
通过验证,的确返回的同一个实例,我们的目标达到了?
问题
可是如果还是用以下方法,还会返回同一个实例对象嘛
person * p1 = [[person alloc]init];
我们来验证下
person * p1 = [[person alloc]init];
person * p2 = [[person alloc]init];
person * p3 = [[person alloc]init];
NSLog(@"%@ \n %@ \n %@",p1,p2,p3);
person * p4 = [person sharedInstance];
person * p5 = [person sharedInstance];
person * p6 = [person sharedInstance];
NSLog(@"%@ \n %@ \n %@",p4,p5,p6);
打印内存地址如下
<person: 0x60000001fa90>
<person: 0x60000001fb60>
<person: 0x60000001fb50>
<person: 0x60c00000cc00>
<person: 0x60c00000cc00>
<person: 0x60c00000cc00>
可见通过alloc init方法创建的对象,还是分配的不同的内存空间。
我们该怎么避免这样的现象发生呢?
单例使用完善
这里我们重写系统创建对象时分配内存地址的方法,并把创建单例方法写在此方法中,保证每次创建对象都返回同一个内存空间
// 把创建单例的写法写在系统为对象分配内存地址的方法中
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
static person * single = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
single = [super allocWithZone:zone];// 最先执行,只执行一次
});
return single;
}
使用方法如下
// sharedInstance方法中,返回的是[[self alloc]init]方法返回的实例
+(instancetype)sharedInstance
{
return [[self alloc]init];// 这里会调用init方法
}
// init方法,重写init方法
-(instancetype)init
{
if (self = [super init]) {
}
return self;
}
// 把创建单例的写法写在系统为对象分配内存地址的方法中
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
static person * single = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 记住这里只会执行一次
single = [super allocWithZone:zone];// 最先执行,只执行一次
});
return single;
}
通过代码来验证下
person * p1 = [[person alloc]init];
person * p2 = [[person alloc]init];
person * p3 = [[person alloc]init];
NSLog(@"%@ \n %@ \n %@",p1,p2,p3);
person * p4 = [person sharedInstance];
person * p5 = [person sharedInstance];
person * p6 = [person sharedInstance];
NSLog(@"%@ \n %@ \n %@",p4,p5,p6);
<person: 0x60800000a3e0>
<person: 0x60800000a3e0>
<person: 0x60800000a3e0>
<person: 0x60800000a3e0>
<person: 0x60800000a3e0>
<person: 0x60800000a3e0>
返回来了同一个实例,不管用alloc init创建还是直接用sharedInstance这个方法。
这样的创建方法,比我们文中提到的常用创建的单例方法更完善。
[end]
网友评论