前些日子在项目中因为误用了单例而导致了一系列问题。原来在objective-c中的单例并没有java或者C#那么简单的实现,这里记录下;

问题是这样被发现的,在对于一个UIViewController进行pop时并没有被dealloc,导致了内存泄露。问题代码类似于下面的:

  1. //LWChatViewController.h
  2. @interface LWChatViewController : LWTableViewController <LWObjSelectViewDelegate>{
  3. UINavigationController *root;
  4. }
  5. @property (nonatomic, retain) UINavigationController *root;
  6. @end
  7. //LWChatViewController.m
  8. - (void)viewDidLoad
  9. {
  10. [super viewDidLoad];
  11. // Do any additional setup after loading the view from its nib.
  12. self.root = LWNavigationController;
  13. }

这里的LWNavigationController是一个顶级的单例。

问题就出在@property (nonatomic, retain) 这里root居然是一个retain的对象指针,在这里retain一个static的单例将导致内存泄露,MD,这个bug找的我好久。。。

解决这个问题其实很简单,把retain改为assign就行了,但这样如果在协作编程的时候如果别人不在意这个是单例直接进行常规操作的话会带来很大的问题。

继续,我们来从根本上解决这个问题。

我们需要重写一些方法:

  1. - (id)retain
  2. {
  3. return self;
  4. }
  5. - (NSUInteger) retainCount
  6. {
  7. return NSUIntegerMax;
  8. }
  9. - (void) release
  10. {
  11. // do nothing
  12. }
  13. - (id)autorelease
  14. {
  15. return self;
  16. }

在retain和autorelease什么都不做只是返回自己,release的时候啥都不做,将retainCount设为UInt的极大值。

其次是关于线程安全的实现,这些java都有明确的代码模式:

关于线程安全的单例,这篇外文 http://www.numbergrinder.com/2008/12/patterns-in-objective-c-singleton-pattern/ 有比较详细的解释。

  1. @implementation Singleton
  2. static Singleton *instance = nil;
  3. + (Singleton *)sharedInstance  {
  4. @synchronized(self)
  5. {
  6. if(!instance) {
  7. instance = [[Singleton alloc] init];
  8. }
  9. }
  10. return instance;
  11. }
  12. @end

嗯,这样就可以实现线程安全的单例了,当然这里也可以用NSLock实例去实现。

最后趁机深入了下单例模式,发现instance = [[Singleton alloc] init];这样的语句是有问题的,它不能以其他方式发起分配内存去实例化对象,可能会造成多个实例被创建。(见《pro objective-c design patterns for ios》 )

该书推荐用

  1. instance = [[super allocWithZone:NULL] init];

传NULL到allocWithZone其实等同与alloc默认方法,但注意这里是调用super的alloc;

本类的allocWithZone被改写为:

  1. + (id)allocWithZone:(NSZone *)zone
  2. {
  3. return [self sharedInstance];
  4. }
  5. - (id)copyWithZone:(NSZone *)zone
  6. {
  7. return self;
  8. }

同时深拷贝也直接重载阻止掉多个实例的出现。上面的allocWithZone的重载使得这个单例也能够直接用alloc或是allocWithZone进行初始化,但返回的一如既往是那个static的实例。

这样一个objective-c的单例模式才算是完整了。。。啦啦啦,每月末一博写完,睡觉去了。。。

【iOS】Objective-C简约而不简单的单例模式的更多相关文章

  1. IOS:利用dispatch_once创建单例

    在之前有一篇学习笔记中,记载了一篇如何在OC中实现单例的文章:<IOS学习笔记4—Objective C—创建单例>自苹果引入了Grand Central Dispatch (GCD)(M ...

  2. iOS开发笔记-两种单例模式的写法

    iOS开发笔记-两种单例模式的写法   单例模式是开发中最常用的写法之一,iOS的单例模式有两种官方写法,如下: 不使用GCD #import "ServiceManager.h" ...

  3. 通通玩blend美工(7)——简约而不简单的块

    原文:通通玩blend美工(7)--简约而不简单的块 最近在研发一个WPF快速开发框架,满脑子都是各种逻辑各种模式,写一篇比较休闲娱乐的博客,宣泄下我对美工的热爱. 我一直以来有意无意在手机应用或者各 ...

  4. 从Objective-C到Swift 单例模式

    在Objective-C中经常会用到单例模式.最常见的就是: [UIApplication sharedApplication].delegate 这里的sharedApplication就是一个返回 ...

  5. 【iOS开发】创建单例的两种方法

    创建一个单例很多办法.我先列举一个苹果官方文档中的写法. [cpp] view plaincopy   static AccountManager *DefaultManager = nil; + ( ...

  6. iOS开发——Swift篇&单例的实现

    Swift实现单例模式 Swift实现单例模式 由于Swift语言弱化了struct和class之间的界限,这里我分别给出自己写的两种的单例实现 class版本: class SwiftSinglet ...

  7. IOS GCD 使用(三)单例模式

    一  Dispatch_once函数简介      使用dispatch_once提价的代码块,即便你提交多次,只能执行一次.    void dispatch_once(dispatch_once_ ...

  8. iOS 开发中的单例

    在iOS开发中经常会用到单例,比如每个iOS程序本身就是一个单例,在比如进行个人偏好设置存储的时候用的也是一个单例.那我们如何自己来写一个单例类呢,用自己的单例对象呢?下面是我写的一个单例的头文件里的 ...

  9. Objective C—创建单例

    单例模式是在实际项目开发中用到比较多的一种设计模式,设计原理是整个系统只产生一个对象实例,通过一个统一的方法对外提供这个实例给外部使用. 在Java中,构造单例一般将类的构造函数声明为private类 ...

随机推荐

  1. 20145102 《Java程序设计》第5周学习总结

    20145102 <Java程序设计>第5周学习总结 教材学习内容总结 数组在内存中会是连续的线性空间,根据索引随机取回时速度快,如果操作上有这类需求时,像是排序,就可以使用;ArrayL ...

  2. DWZ按钮居中显示

  3. highchairts柱状图显示数值并且带单位

    $(target).highcharts({ chart: { type: 'bar' }, colors: [ "#1ab394" ], title: { text: title ...

  4. 【转】亿欧盘点:杭州十家代表性O2O企业

    [ 亿欧导读 ] 11月13日亿欧网将走入杭州,联合B座12楼.正和岛召开“2014 中国O2O新商业峰会“.亿欧网据O2O产业图谱,整理出杭州十家O2O企业:点我吧.快的打车.杭州19楼.婚礼纪.淘 ...

  5. Swiper之初识

    何为Swiper?Swiper是一款免费以及轻量级的移动设备触控滑块的框架,使用硬件加速过渡(如果该设备支持的话).主要使用与移动端的网站.网页应用程序(web apps),以及原生的应用程序(nat ...

  6. AJAX之JSON

    AJAX=Asynchronous JavaScript and XML(异步的JavaScript 和XML). AJAX不是新的编程语言,而是一种是用现代标准的新方法,用于创建快速动态网页的技术. ...

  7. php数据过滤函数与方法示例【转载】

    1.php提交数据过滤的基本原则 1)提交变量进数据库时,我们必须使用addslashes()进行过滤,像我们的注入问题,一个addslashes()也就搞定了.其实在涉及到变量取值时,intval( ...

  8. 【Linux C中文函数手册】之 目录操作函数

    目录操作函数 1)closedir 关闭目录 相关函数: opendir表头文件: #include<sys/types.h> #include<dirent.h>定义函数: ...

  9. java使用BufferedImage和Graphics实现图片合成

    package com.igoxin.weixin.custom; import java.awt.Graphics; import java.awt.image.BufferedImage; imp ...

  10. call与apply函数

    call与apply函数 1.为什么需要call与apply函数 Javascript中,每一个函数内部都有一个特殊的关键词this,其随着所处环境的不同其指向也是不同的. 函数的内部其this也是指 ...