iOS开发ARC与MRC下单例的完整写法与通用宏定义
#import "XMGTool.h"
/**
* 1:ARC下的完整的单例写法:alloc内部会调用+(instancetype)allocWithZone:(struct _NSZone *)zone方法,所以重写该方法,用GCD一次性函数,默认是线程安全的加了一把锁,也可以自己去加锁
@synchronized(self) {
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
} 2:还要考虑copy和mutableCopy两种情况,要想重写两种方法,必须先遵守两个协议:<NSCopying, NSMutableCopying>协议,直接返回一个实例就可以,其中对象调用copy:无论可变不可变copy后的对象都是不可变的,若源对象不可变,则copy返回的是源对象,源对象引用计数加1,并copy了源对象的值,若源对象是可变的,则会产生一个新对象,新对象引用计数加1,mutableCopy,拷贝的对象都是可变的,也都是新对象,新对象引用计数加1,只要是产生了新对象就是深拷贝,没产生新对象,只拷贝了指针就是浅拷贝
3:对象调用new方法创建对象,就相当于调用了alloc init方法创建的对象
*/
@implementation XMGTool //0.提供全局变量
static XMGTool *_instance; //1.alloc-->allocWithZone
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
//加互斥锁解决多线程访问安全问题
// @synchronized(self) {
// if (_instance == nil) {
// _instance = [super allocWithZone:zone];
// }
// } //本身就是线程安全的
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
}); return _instance;
} //2.提供类方法
+(instancetype)shareTool
{
return [[self alloc]init];
} //3.严谨
-(id)copyWithZone:(NSZone *)zone
{
return _instance;
} -(id)mutableCopyWithZone:(NSZone *)zone
{
return _instance;
}
@end
2:MRC
#import "XMGTool.h"
/**
* MRC下单例的实现:1:需要重写release方法,什么都不做,因为不需要release操作 2:retain会产生新对象操作会使引用计数加1,此时返回一个已经创建的实例,3:重写retainCount方法,返回一个最大值
*/
@implementation XMGTool //修改环境为MRC
//0.提供全局变量
static XMGTool *_instance; //1.alloc-->allocWithZone
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
//加互斥锁解决多线程访问安全问题
// @synchronized(self) {
// if (_instance == nil) {
// _instance = [super allocWithZone:zone];
// }
// } //本身就是线程安全的
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
}); return _instance;
} //2.提供类方法
+(instancetype)shareTool
{
return [[self alloc]init];
} //3.严谨
-(id)copyWithZone:(NSZone *)zone
{
return _instance;
} -(id)mutableCopyWithZone:(NSZone *)zone
{
return _instance;
} #if __has_feature(objc_arc)
//条件满足 ARC
#else
// MRC
-(oneway void)release
{ } -(instancetype)retain
{
return _instance;
} //习惯
-(NSUInteger)retainCount
{
return MAXFLOAT;
} #endif @end
3:通用宏定义:
#define SingleH(name) +(instancetype)share##name; #if __has_feature(objc_arc)
//条件满足 ARC
#define SingleM(name) static id _instance;\
+(instancetype)allocWithZone:(struct _NSZone *)zone\
{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
_instance = [super allocWithZone:zone];\
});\
\
return _instance;\
}\
\
+(instancetype)share##name\
{\
return [[self alloc]init];\
}\
\
-(id)copyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
\
-(id)mutableCopyWithZone:(NSZone *)zone\
{\
return _instance;\
} #else
//MRC
#define SingleM(name) static id _instance;\
+(instancetype)allocWithZone:(struct _NSZone *)zone\
{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
_instance = [super allocWithZone:zone];\
});\
\
return _instance;\
}\
\
+(instancetype)share##name\
{\
return [[self alloc]init];\
}\
\
-(id)copyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
\
-(id)mutableCopyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
-(oneway void)release\
{\
}\
\
-(instancetype)retain\
{\
return _instance;\
}\
\
-(NSUInteger)retainCount\
{\
return MAXFLOAT;\
}
#endif
```objc
使用Crearte函数创建的并发队列和全局并发队列的主要区别:
1.全局并发队列在整个应用程序中本身是默认存在的,并且对应有高优先级、默认优先级、低优先级和后台优先级一共四个并发队列,我们只是选择其中的一个直接拿来用。而Crearte函数是实打实的从头开始去创建一个队列。
2.在iOS6.0之前,在GCD中凡是使用了带Create和retain的函数在最后都需要做一次release操作。而主队列和全局并发队列不需要我们手动release。当然了,在iOS6.0之后GCD已经被纳入到了ARC的内存管理范畴中,即便是使用retain或者create函数创建的对象也不再需要开发人员手动释放,我们像对待普通OC对象一样对待GCD就OK。
3.在使用栅栏函数的时候,苹果官方明确规定栅栏函数只有在和使用create函数自己的创建的并发队列一起使用的时候才有效(没有给出具体原因)
4.其它区别涉及到XNU内核的系统级线程编程,不一一列举。
5.给出一些参考资料(可以自行研究):
GCDAPI:https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/index.html#//apple_ref/c/func/dispatch_queue_create
Libdispatch版本源码:http://www.opensource.apple.com/source/libdispatch/libdispatch-187.5/
```
###1.单例模式
- 1.1 概念相关
(1)单例模式
在程序运行过程,一个类只有一个实例
(2)使用场合
在整个应用程序中,共享一份资源(这份资源只需要创建初始化1次)
- 1.2 ARC实现单例
(1)步骤
01 在类的内部提供一个static修饰的全局变量
02 提供一个类方法,方便外界访问
03 重写+allocWithZone方法,保证永远都只为单例对象分配一次内存空间
04 严谨起见,重写-copyWithZone方法和-MutableCopyWithZone方法
(2)相关代码
```objc
//提供一个static修饰的全局变量,强引用着已经实例化的单例对象实例
static XMGTools *_instance;
//类方法,返回一个单例对象
+(instancetype)shareTools
{
//注意:这里建议使用self,而不是直接使用类名Tools(考虑继承)
return [[self alloc]init];
}
//保证永远只分配一次存储空间
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
//使用GCD中的一次性代码
// static dispatch_once_t onceToken;
// dispatch_once(&onceToken, ^{
// _instance = [super allocWithZone:zone];
// });
//使用加锁的方式,保证只分配一次存储空间
@synchronized(self) {
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
}
/*
1. mutableCopy 创建一个新的可变对象,并初始化为原对象的值,新对象的引用计数为 1;
2. copy 返回一个不可变对象。分两种情况:(1)若原对象是不可变对象,那么返回原对象,并将其引用计数加 1 ;(2)若原对象是可变对象,那么创建一个新的不可变对象,并初始化为原对象的值,新对象的引用计数为 1。
*/
//让代码更加的严谨
-(nonnull id)copyWithZone:(nullable NSZone *)zone
{
// return [[self class] allocWithZone:zone];
return _instance;
}
-(nonnull id)mutableCopyWithZone:(nullable NSZone *)zone
{
return _instance;
}
```
- 1.3 MRC实现单例
(1)实现步骤
01 在类的内部提供一个static修饰的全局变量
02 提供一个类方法,方便外界访问
03 重写+allocWithZone方法,保证永远都只为单例对象分配一次内存空间
04 严谨起见,重写-copyWithZone方法和-MutableCopyWithZone方法
05 重写release方法
06 重写retain方法
07 建议在retainCount方法中返回一个最大值
(2)配置MRC环境知识
01 注意ARC不是垃圾回收机制,是编译器特性
02 配置MRC环境:build setting ->搜索automatic ref->修改为NO
(3)相关代码
```objc
//提供一个static修饰的全局变量,强引用着已经实例化的单例对象实例
static XMGTools *_instance;
//类方法,返回一个单例对象
+(instancetype)shareTools
{
//注意:这里建议使用self,而不是直接使用类名Tools(考虑继承)
return [[self alloc]init];
}
//保证永远只分配一次存储空间
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
//使用GCD中的一次性代码
// static dispatch_once_t onceToken;
// dispatch_once(&onceToken, ^{
// _instance = [super allocWithZone:zone];
// });
//使用加锁的方式,保证只分配一次存储空间
@synchronized(self) {
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
}
//让代码更加的严谨
-(nonnull id)copyWithZone:(nullable NSZone *)zone
{
// return [[self class] allocWithZone:zone];
return _instance;
}
-(nonnull id)mutableCopyWithZone:(nullable NSZone *)zone
{
return _instance;
}
//在MRC环境下,如果用户retain了一次,那么直接返回instance变量,不对引用计数器+1
//如果用户release了一次,那么什么都不做,因为单例模式在整个程序运行过程中都拥有且只有一份,程序退出之后被释放,所以不需要对引用计数器操作
-(oneway void)release
{
}
-(instancetype)retain
{
return _instance;
}
//惯用法,有经验的程序员通过打印retainCount这个值可以猜到这是一个单例
-(NSUInteger)retainCount
{
return MAXFLOAT;
}
```
- 1.4 通用版本
(1)有意思的对话
01 问:写一份单例代码在ARC和MRC环境下都适用?
答:可以使用条件编译来判断当前项目环境是ARC还是MRC
02 问:条件编译的代码呢,么么哒?
```objc
//答:条件编译
#if __has_feature(objc_arc)
//如果是ARC,那么就执行这里的代码1
#else
//如果不是ARC,那么就执行代理的代码2
#endif
```
03 问:在项目里面往往需要实现很多的单例,比如下载、网络请求、音乐播放等等,弱弱的问一句单例可以用继承吗?
答:单例是不可以用继承的,如果想一次写就,四处使用,那么推荐亲使用带参数的宏定义啦!
04 问:宏定义怎么弄?
答:这个嘛~~回头看一眼我的代码咯,亲。
(2)使用带参数的宏完成通用版单例模式代码
01 注意条件编译的代码不能包含在宏定义里面
02 宏定义的代码只需要写一次就好,之后直接拖到项目中用就OK
iOS开发ARC与MRC下单例的完整写法与通用宏定义的更多相关文章
- IOS 开发 ARC兼容MRC框架
在后面加 -fno-objc-arc
- iOS开发ARC机制下的内存管理技术要点
转载一篇: iOS开发ARC内存管理技术要点.ARC内存管理原则总结.iOS ARC内存管理总结 ARC内存管理机制 (一)ARC的判断准则: 只要没有任何一个强指针指向该对象,该对象就会被释放. ( ...
- App开发流程之通用宏定义及头文件
工欲善其事,必先利其器. 在正式实现各种炫酷的功能和UI前,做好准备工作是提高后续开发效率的必经之路. 所以,这个系列,我不是在各种堆技术,更关注的是“兵马动”之前的“粮草行”,有些繁琐,但当清晰理出 ...
- iOS开发ARC内存管理技术要点
本文来源于我个人的ARC学习笔记,旨在通过简明扼要的方式总结出iOS开发中ARC(Automatic Reference Counting,自动引用计数)内存管理技术的要点,所以不会涉及全部细节.这篇 ...
- iOS开发ARC入门和使用
本文引自:http://www.onevcat.com/2012/06/arc-hand-by-hand/ 英文原版:http://www.raywenderlich.com/5677/beginni ...
- (转)iOS开发ARC内存管理技术要点
转自:http://www.cnblogs.com/flyFreeZn/p/4264220.html 本文来源于我个人的ARC学习笔记,旨在通过简明扼要的方式总结出iOS开发中ARC(Automati ...
- IOS开发 arc与非Arc代码的区别
是属于ios开发中的内存管理问题:在这我简要概述一下,详细讲的话内容挺多,而且是作为一个ios开发人员,或ios开发爱好者,这是必须了解的:Objective-c中提供了两种内存管理机制MRC(Man ...
- iOS开发ARC内存管理
本文的主要内容: ARC的本质 ARC的开启与关闭 ARC的修饰符 ARC与Block ARC与Toll-Free Bridging ARC的本质 ARC是编译器(时)特性,而不是运行时特性,更不是垃 ...
- IOS开发 ARC和非ARC下使用Block属性的问题
1. Block的声明和线程安全 Block属性的声明,首先需要用copy修饰符,因为只有copy后的Block才会在堆中,栈中的Block的生命周期是和栈绑定的,可以参考之前的文章(iOS: 非AR ...
随机推荐
- Linux平台下使用AdventNet ManageEngine OpUtils监控网络
AdventNet ManageEngine OpUtils 是一套系统和网络监视工具,它有Linux/Windows系统平台的免费版和企业版,该软件是一款用于监视诸如路由器,交换机,服务器或者桌面这 ...
- (转)Tomcat文件详解
做web项目,最常用的服务器就是Apache的tomcat.虽然一直在用tomcat,但都是仅限在使用的阶段,一直没有深入学习过.想深入学习tomcat,首推的肯定是官网:http://tomcat. ...
- 3/21 Django框架 模板路径及模板过滤器 1.模板路径查找
3/21 Django框架 模板路径及模板过滤器 1.模板路径查找 先找settings.py里的TEMPLATES列表下的DIRS路径.如果APP_DIRS为True,还会到注册了的APP文件夹下依 ...
- python ATM机 案例代码
利用目前学的流程控制写的 ''' ATM机 需求: 1.登陆 输入账号输入密码 每日只有3次登陆密码错误的机会,超过3次禁止登陆 2.查询余额 3.存款 4.取款 5.转帐 6.退出 ''' info ...
- 【习题 7-2 UVA-225】Golygons
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 暴力枚举每次走哪里就好. 用一个二维数组来判重.(数据里,要求不能经过一个点两次->但路径可以相交 然后再用一个flag数组, ...
- WEB安全实战(二)带你认识 XSS 攻击
前言 上一篇文章写了关于 WEB 安全方面的实战,主要是解决 SQL 盲注的安全漏洞.这篇文章本来是要写一篇关于怎样防治 XSS 攻击的,可是想来想去,还是决定先从理论上认识一下 XSS 吧.下一篇文 ...
- BeautifulSoup的高级应用 之 contents children descendants string strings stripped_strings
继上一节.BeautifulSoup的高级应用 之 find findAll,这一节,主要解说BeautifulSoup有关的其它几个重要应用函数. 本篇中,所使用的html为: html_doc = ...
- drawable-实现图片旋转
今天因为需要,所以要让一个图片随着某种需要进行旋转.但是,又不能一张张的做动态图片.所以就在网上找了这么个方法.但是,这个方法有个问题,就是虽然能实现图片的旋转.但是,图片旋转以后会进行缩放.具体原因 ...
- vue <input type="file">上传图片、预览、删除
使用原生<input type="file">上传图片.预览.删除:multiple实现可上传多张 参数名 类型 说明 fileTypes Array 文件类型, 默认 ...
- angular 响应式自定义表单控件—注册头像实例
1. 组件继承ControlValueAccessor,ControlValueAccessor接口需要实现三个必选方法 writeValue() 用于向元素中写入值,获取表单的元素的元素值 regi ...