前些日子在项目中因为误用了单例而导致了一系列问题。原来在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. [转]在WPF中使用WinForm控件方法

    本文转自:http://blog.csdn.net/lianchangshuai/article/details/6415241 下面以在Wpf中添加ZedGraph(用于创建任意数据的二维线型.条型 ...

  2. Linux 内核模块可选信号

    一 . 内核模块可选信号 1 . 模块申明 (1). MODULE_LICENSE(遵守的协议) 申明该模块遵守的许可证协议,如:“GPL”."GPL V2" (2). MODUL ...

  3. 【PHP基础】常用mySQL语句以及WampServer2.2设置数据库默认编码

    一.WampServer2.2设置数据库默认编码(此部分转自http://www.cnsecer.com/5984.html) wamp下MySQL的默认编码是Latin1,不支持中文,要支持中文的话 ...

  4. Javascript addEventListener dispatchEvent

    测试代码:分别在嵌套的元素body,div#level1,div#level2,div#level3上附加事件,仅在chrome中测试通过. <!DOCTYPE html> <htm ...

  5. jQuery中的选择器<思维导图>

    选择器是jQuery的重要组成部分,在jQuery中,对事件处理.遍历DOM和Ajax操作都依赖于选择器.如果能熟练地使用选择器,不仅能简化代码,而且可以达到事半功倍的效果. 下面是关于jQuery中 ...

  6. jquery设置div,文本框 表单的值示例

    我们将使用前一章中的三个相同的方法来设置内容: text() - 设置或返回所选元素的文本内容html() - 设置或返回所选元素的内容(包括 HTML标记)val() - 设置或返回表单字段的值 1 ...

  7. 细说SQL 连接

          连接条件可在FROM或WHERE子句中指定,建议在FROM子句中指定连接条件.WHERE和HAVING子句       也可以包含搜索条件,以进一步筛选连接条件所选的行.         ...

  8. OC5_Block

    // // main.m // OC5_Block // // Created by zhangxueming on 15/6/26. // Copyright (c) 2015年 zhangxuem ...

  9. linux 内核和应用程序区别

    应用程序存在于虚拟内存中, 有一个非常大的堆栈区. 堆栈, 当然, 是用来保存函数调用历史以及所有的由当前活跃的函数创建的自动变量. 内核, 相反, 有一个非常小的堆栈; 它可能小到一个, 4096 ...

  10. (转)手把手教你如何架设VPN

    简介 让远程用户连接Exchange Server的传统解决方案是使用Outlook Web Access.然而,为何不使用虚拟专用网(Virtual Private Network,VPN)让你的远 ...