前些日子在项目中因为误用了单例而导致了一系列问题。原来在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. 最初步的正则表达式引擎:nfa的转换规则。

    [在此处输入文章标题] 正则到nfa 前言 在写代码的过程中,本来还想根据龙书上的说明来实现re到nfa的转换.可是写代码的时候发现,根据课本来会生成很多的无用过渡节点和空转换边,需要许多的代码.为了 ...

  2. Android apk 的安装过程

    Android应用安装有如下四种方式 1.系统应用安装――开机时完成,没有安装界面 2.网络下载应用安装――通过market应用完成,没有安装界面 3.ADB工具安装――没有安装界面. 4.第三方应用 ...

  3. 查询score中选学多门课程的同学中分数为非最高分成绩的记录。

    20.查询score中选学多门课程的同学中分数为非最高分成绩的记录. select * from score a where sno in ( select sno from score group ...

  4. CKEditor配置及使用

    注:使用CKEditor版本为js版本的CKEditor 4,所有配置均参考自CKEditor官方API:http://docs.ckeditor.com/,以及实践经验 一.快速使用 1.引入CKE ...

  5. Part 6 Group by in sql server

  6. android sdk manager 无法更新

    1.在C:\Windows\System32\drivers\etc找到Hosts文件用记事本打开,在最末尾添加如下代码,保存关闭: #Google主页203.208.46.146 www.googl ...

  7. Winform实现鼠标可穿透的窗体镂空效果

    今天找到一个名叫LICEcap的录屏软件,录制界面是这样的:     这个炫酷的空心窗口就是镜头,调整好大小,然后对准需要录制的地方按下Record就可以生成gif了. 卧槽太NB了我也要做一个! 根 ...

  8. git使用小结

    本篇文章主要介绍自己在平时工作中使用git的一些常用命令,之前都是记录在本子上面,现在把他们记录在博客上,便于保存和回顾. 1. 建立自己的git仓库 1.1 在一个新建的repo文件夹里面,执行gi ...

  9. JS事件冒泡与捕获

    1事件传播——冒泡与捕获 默认情况下,事件使用冒泡事件流,不使用捕获事件流.然而,在Firefox和Safari里,你可以显式的指定使用捕获事件流,方法是在注册事件时传入useCapture参数,将这 ...

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

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