Singletons in Cocoa, are they evil?

2024-05-03 17:08
文章标签 cocoa evil singletons

本文主要是介绍Singletons in Cocoa, are they evil?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

故事

这事是这样的,去年我在上课的时候,和老师讨论了一下关于架构的问题,我是开发Cocoa/iOS的,老师是开发Web的,而老师是一个坚定的singletons are evil的拥护者,我和他说了我的App的架构,直接被他一顿猛劈,强烈的谴责了我使用Singletons,我回应说,这个pattern在Cocoa里是大量使用的,结果被搞了一句“用的多的就是对的么?你回去多学习一下再来讨论吧”。

于是我非常郁闷的回去搜索的一大顿的资料,还在Stackoverflow上发起了一个问题:singletons in cocoa, are they evil?。甚至在某个社区,假扮singleton are evil的拥护者,把所有singleton的缺点列了一堆,结果又是群起而攻之一场舌战。

关于Singleton的缺点,放出一段引用:

  1. They are generally used as a global instance, why is that so bad? Because you hide the dependencies of your application in your code, instead of exposing them through the interfaces. Making something global to avoid passing it around is a code smell.

  2. They violate the Single Responsibility Principle: by virtue of the fact that they control their own creation and lifecycle.

  3. They inherently cause code to be tightly coupled. This makes faking them out under test rather difficult in many cases.

  4. They carry state around for the lifetime of the app. Another hit to testing since you can end up with a situation where tests need to be ordered which is a big no no for unit tests. Why? Because each unit test should be independent from the other.

公说公有理,婆说婆有理,一度把我弄得越来越困惑,后来我看到这一段话,我就彻底释然了:

As for degrees of evil – it’s like speech or literature. F-words are “evil”. If you speak constantly using f-words words the quality of your language is lower – people can’t tell if you want to say something or just swearing. But in some situations such words help you to get things done (think of the battlefield orders). It sort of the same thing with features/patterns and people who have to read and maintain their usage.

– hoha

BTW,今天我甚至看到了Accessors Are Evil这样的东西,更坚定了我再也不相信xxx are evil这种说法的决心。

我现在认为Design pattern是前人总结的经验,不同的设计模式有不同的优缺点,比如说用工厂代替单例的,虽说解决了单例的一些问题,但你要真去写一个工厂就知道有多蛋疼,多浪费生命了。然而在较为大型的应用,非常多人协作的项目,队友对项目的把握不一致,水平有高低之分,这时工厂又反而是一种安全的,省时省力的做法。

其实在代码的世界里面,你想要更多的安全,就会丧失更多的灵活性和便利性。如何在这中间取舍,就需要我们彻底的了解某种模式(或者说某种编程方法)的优缺点,在保证基本的安全性的情况下,尽可能的减少工作量,提高工作效率。

Singletons in Cocoa

回到正题,还是来说说Cocoa上的单例。Cocoa中的普遍的,大部分的单例,并不是严格的单例(strict singleton),而是一种共享单例(shared singleton),例如sharedApplication,sharedURLCache等。即,大多数情况,我们访问同一个类方法,就可以获得一个同样的实例,但若真的需要存在多个实例亦可。通常,共享单例使用一个shared开的类方法识别。只有当真的只有唯一的一个共享资源的时候,或者不可能有多个资源的时候(比如GPS模块),才会使用严格意义的共享单例。

线程安全的Singleton

绝大多数情况下,使用一个共享单例比使用共享单例要好,然而这里有一个常见的创建共享单例的错误,即使是Apple自己的开发者文档也没弄清楚的一个错误,他们把Singleton写成了非线程安全的:

+ (MyClass *)sharedInstance {static MyClass *sharedInstance;if (sharedInstance == nil) {sharedInstance = [[MyClass alloc] init];}return sharedInstance;
}

正确的写法应该是:

+ (MyClass *)sharedInstance {static MyClass *sharedInstance;@synchronized(self) {if (sharedInstance == nil) {sharedInstance = [[MyClass alloc] init];}}return sharedInstance;
}

更恰当的写法是使用dispatch_once()

+ (MYClass *)sharedInstance
{static dispatch_once_t pred = 0;static MYClass _sharedObject = nil;dispatch_once(&pred, ^{_sharedObject = [[self alloc] init]; // or some other init method});return _sharedObject;
}

dispatch_once()即为执行且仅仅执行某个block一次,他是同步的方法(记住GCD也有很多同步的方法),其速度也比 @synchronized 快许多。

严格的单例(strict singleton)

尽管我们很少会使用到严格的单例模式,但当真的需要的时候,还是可以实现的。

苹果官方文档提供了一个严格单例的实现(传送门)。 其重载了allocWithZone:, copyWithZone, retain, retainCount, release, autorelease。使得这个实现变得无比复杂而难以理解和控制。

而大多数情况下,实现严格的单例模式,只需要和共享单例相同的代码,再使用NSAssert使得一切调用init的代码作为一个错误处理即可,代码如下:

+ (MYSingleton *)sharedSingleton {static dispatch_once_t pred;static MYSingleton *instance = nil;dispatch_once(&pred, ^{instance = [[self alloc] initSingleton];});return instance;
}- (id)init {// Forbid calls to –init or +newNSAssert(NO, @”Cannot create instance of Singleton”);// You can return nil or [self initSingleton] here, // depending on how you prefer to fail.return nil;
}
// Real (private) init method
- (id)initSingleton {self = [super init];if ((self = [super init])) {// Init code }return self; 
}

这份代码的优点是很明显的,避免了复杂的内存操作和重载,又静止了调用者创建多个实例。

小结

小结一下,单例模式是Cocoa中非常常用的一个模式,对于应用程序中广泛使用的对象,单例模式是非常便利的方法。而我们也应当在使用的时候多注意单例模式的一些缺点,尽可能的在实现的时候避免他们,比如让单例不存在过于复杂的依赖性和继承,保证其松耦合等。

Edit:

One more thing:有筒子问到是@synchronized(self)还是@synchronized(sharedInstance)?

答案是:均可,常规来说使用self

self,在实例方法中表现是实例,这一点自不用多说。在类方法中常表现为一种多态的类实例(class instance),他总是会返回正确的类型,比如这样的类方法,同样的实现对于不同的类总是能返回正确的类型:

+ (id)new
{return [[self alloc] init];
}

而在本文的这个@synchronized(self)里的self,总是会指向同一个对象,即那个特殊的类实例。(class也是一个对象),故而此处使用self。

有兴趣继续研究的同学可以使用下面的代码,自行测试:

+ (void)foo
{NSLog(@"class method: %@", self);
}
- (void)foo
{NSLog(@"instance method: %@", self);
}

lancy

原文链接: http://gracelancy.com/?p=386

这篇关于Singletons in Cocoa, are they evil?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/957231

相关文章

[Cocoa]深入浅出 Cocoa 之 Core Data(2)- 手动编写代码

深入浅出 Cocoa 之 Core Data(2)- 代码示例 罗朝辉( http://blog.csdn.net/kesalin) CC 许可,转载请注明出处 前面 详细讲解了 Core Data 的框架以及设计的类,下面我们来讲解一个完全手动编写代码使用这些类的示例,这个例子来自苹果官方示例。在这个例子里面,我们打算做这样一件事情:记录程序运行记录(时间与 proc

Cocoa执行AppleScript添加或移除守护进程

首先了解一下Mac OS X中几种守护进程: ~/Library/LaunchAgents Per-user agents provided by the user./Library/LaunchAgents Per-user agents provided by the administrator./Library/LaunchDaemons

cocoa开发之:自定义NSButton,为NSButton添加鼠标移入移出效果

一直感觉在cocoa开发下的NSButton没有在ios开发下的UIButton使用起来方便,简单!但是还是需要自己去研究,通过各种自定义来实现想要的效果!接下来,我会以向NSButton添加鼠标移入移出效果为例,给大家简单的介绍下如何实现自定义NSButton! 首先新建工程,然后创建继承于系统的NSButton的BaseBtn类,然后为里面添加一个BOOL类型的isSelected,用来区分是

Apple - Cocoa Event Handling Guide

本文翻译整理自:Cocoa Event Handling Guide( https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/EventOverview/Introduction/Introduction.html#//apple_ref/doc/uid/10000060i 文章目录 一、导

OCLint的部分规则(CoCoa 部分)

OCLint的部分规则(CoCoa 部分) 对OCLint的部分规则进行简单翻译解释,有部分进行了验证以及进一步分析、测试。OCLint其他相关内容如下: -- OCLint-iOS-OC项目几种简单使用OCLint的部分规则(Basic 部分)OCLint的部分规则(Unuseed 部分)OCLint的部分规则(Size 部分)OCLint的部分规则(Redundant 部分)OCLin

emacs evil-matchit实现verilog配对的代码跳转

背景emacs插件evil-matchit参考文档 背景 vim里常使用%进行跳转。遇到代码段较长的情况,跳转方便而且有助于debug。 vim 实现begin end 配对 使用matchit插件 - 岁月长河 - 博客园 http://www.cnblogs.com/air-of-code/p/4733151.html emacs怎么搞? emacs插件evil-

Apple - Cocoa Text Architecture Guide

翻译整理自:Cocoa Text Architecture Guide https://developer.apple.com/library/archive/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Introduction/Introduction.html#//apple_ref/doc/uid/TP40009459

Property's synthesized getter follows Cocoa naming convention for returning

Property's synthesized getter follows Cocoa naming convention forreturning. 今天早上在整理代码的时候发现了如上警告。 在网上查询后发现,是因为苹果在新的编码,不推荐变量以new、copy等关键字开头。 突然响起来之前也有朋友问过类似的问题。特做以记录。 也希望大家在

红队神器Evil-winrm的使用

前言          Evil-winrm 工具最初是由 Hackplayers 团队开发的。开发该工具的目的是尽可能简化渗透测试,尤其是在 Microsoft Windows 环境中。 Evil-winrm 使用 PowerShell 远程协议 (PSRP),且系统和网络管理员经常使用Windows Remote Management 协议进行上传和管理。 WinRM 是一种基于对防火墙友好

uva 10716 Evil Straw Warts Live

题意:求最少的步骤使得原串变为回文串,贪心的做法很容易想到:每次都找离两端距离和最近的一对字符,然后分别移到两端就行了 #include <iostream>#include <cstdio>#include <cstring>using namespace std;const int MAXN = 8005;char str[MAXN];int vis[MAXN];void sw