ios — 单例使用

一直想花时间把之前项目常用的控件及功能整理出来,本打算把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]

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容