Simple RBAC | Wiki | Yii Framework

Simple RBAC

8

Follow @yiiframework

7,350 followers

report it

27 followers

The user model

The authentication

Checking permissions: structure

Checking permissions: usage

How to filter access to actions

How to display a different menu according to roles

Going further: access context

If you need simple Role based access control without the long RBAC process then this article is just for you. Lets jump to the point.

The user model

On your user table make a column named ‘roles’. Create the model accordingly. It will be named "User" here.

When you add users you can assign them a role among ‘admin’, ‘user’, ‘staff’ etc etc.

The authentication

In the file "protected/components/UserIdentity.php" write something like:

class UserIdentity extends CUserIdentity
{
    private $id;
 
    public function authenticate()
    {
        $record=User::model()->findByAttributes(array('email'=>$this->username));
        if($record===null)
            $this->errorCode=self::ERROR_USERNAME_INVALID;
        else if($record->password!==md5($this->password))
            $this->errorCode=self::ERROR_PASSWORD_INVALID;
        else
        {
            $this->id=$record->id;
            $this->setState('roles', $record->roles);            
            $this->errorCode=self::ERROR_NONE;
        }
        return !$this->errorCode;
    }
 
    public function getId(){
        return $this->id;
    }
}

The important line is $this->setState('roles', $record->roles);
It adds user roles to their session.
You can fetch the role of the current user with Yii::app()->user->getState('roles') or simply Yii::app()->user->roles.

Checking permissions: structure

Modify or create the "WebUser.php" file under the "protected/components" directory so that it overloads the checkAccess() method.

<?php 
class WebUser extends CWebUser
{
    /**
     * Overrides a Yii method that is used for roles in controllers (accessRules).
     *
     * @param string $operation Name of the operation required (here, a role).
     * @param mixed $params (opt) Parameters for this operation, usually the object to access.
     * @return bool Permission granted?
     */
    public function checkAccess($operation, $params=array())
    {
        if (empty($this->id)) {
            // Not identified => no rights
            return false;
        }
        $role = $this->getState("roles");
        if ($role === 'admin') {
            return true; // admin role has access to everything
        }
        // allow access if the operation request is the current user's role
        return ($operation === $role);
    }
}

You can define your own logic in this checkAccess() methods.

Make sure this class is used by Yii. The config file "protected/config/main.php" must contain:

'components' => array(
    // ...
    'user' => array(
        'class' => 'WebUser',
    ),

Sidenote:CWebUser::checkAccess() usually connects to the authorization system loaded in Yii.
Here we are replacing it with a simple system that just deals with roles instead of the hierarchical system defined by the derivatives of CAuthManager. See the official tutorial, Role-Based Access Control for details.

Checking permissions: usage

  • In your PHP code, use Yii::app()->user->checkAccess('admin') to check if the current user has the ‘admin’ role.
    The call Yii::app()->user->checkAccess('staff') will return true if the user has the role "staff" or "admin".
  • In your controller, you can filter with accessRules() using the "roles" attribute of the rule.

See examples below.

How to filter access to actions

The controller must contain:

public function filters()
{
    return array(
        'accessControl', // perform access control for CRUD operations
    );
}
 
public function accessRules()
{   
    return array(
        array('allow',
            'actions'=>array('admin'),
            'roles'=>array('staff', 'devel'),
        ),
        array('deny',  // deny all users
            'users'=>array('*'),
        ),
    );
}

Here the "admin" action of the controller has restricted access: only those with roles "staff" or "devel" can access it.

As described in the API doc of CAccessRule, the "roles" attribute will in fact call Yii::app()->user->checkAccess().

How to display a different menu according to roles

You can also use just one menu for all users based upon different roles. for example

<?php
$user = Yii::app()->user; // just a convenience to shorten expressions
$this->widget('zii.widgets.CMenu',array(
    'items'=>array(             
        array('label'=>'Users', 'url'=>array('/manageUser/admin'), 'visible'=>$user->checkAccess('staff')),
        array('label'=>'Your Ideas', 'url'=>array('/userarea/ideaList'), 'visible'=>$user->checkAccess('normal')),
        array('label'=>'Login', 'url'=>array('/site/login'), 'visible'=>$user->isGuest),
        array('label'=>'Logout ('.Yii::app()->user->name.')', 'url'=>array('/site/logout'), 'visible'=>!$user->isGuest)
    ),
));
?>

Going further: access context

A very usual need is to allow a user to update its own data but not other’s.
In this case, the user’s role is meaningless without the context: the data that will be modified.

This is why CWebUser::checkAccess() has an optional "$param" parameter. Now suppose we want to check is a user has the right to update a Post record. We can write:

if (Yii::app()->user->checkAccess('normal', $post)) {

Of course WebUser::checkAccess() must be extended to use this "$params" parameter.
This will depend on your application’s logic.
For instance, it could be as simple as granting access iff $post->userId == $this->id.

Total 11 comments

#11351
report it

hari maliya at 2013/01/08 04:08am

I con’t access action according to role ?

Hi guys

I try to all type of set access role and set role ,
I got role in controller but this can’t be access a action page

thnaks

hari maliya

#11343
report it

hari maliya at 2013/01/07 08:31am

How to create Access (view page) role in yii ?

Hi guys

How to create access role in yii and where in yii application part ?

I want to create access role in yii application but i have a problem and dont know about where to assign role in yii like
i have three department role
1.admin -: admin have a all access role in our application
2.staff -: staff same of page and access role like to edit or update
3.user -: user have a all access page only viewing in our application

These type of role can set in controller but i can justify where to write all access in yii and how to set access role ,

thank
hari maliya

#10792
report it

Jonas at 2012/11/23 05:30pm

One user with multiple roles

If you want to assign multiple roles to a user (separated by a comma) you can replace

$role = $this->getState("roles");
if ($role === 'admin') {
   return true; // admin role has access to everything
}
// allow access if the operation request is the current user's role
return ($operation === $role);

with

$roles = explode(',', $this->getState("roles"));
 
if (in_array('admin', $roles) || in_array($operation, $roles))
   return true;

#10633
report it

Giacomo at 2012/11/09 08:41am

checkAccess

Sorry guys, but this point it’s not really clear for me

"The call Yii::app()->user->checkAccess(‘staff’) will return true if the user has the role "staff" or "admin"."

What do i’ve to specify inside checkAccess?
In my app, I’ve 3 user types (admin, operator, account) and i’d like to filter the access inside controllers and filter displayed content.

Inside the controller, i use

public function accessRules()
    {
        return array(
            array('allow', 
                'roles'=>array(User::ROLE_ADMIN,User::ROLE_OPERATOR),
            ),
            array('deny',  // deny all users
                'users'=>array('*'),
            ),
        );
    }

where User::ROLE_ADMIN and User::ROLE_OPERATOR are int 1 and 2, and it’s the value assigned inside userIdentity

$this->setState('roles', $record->role_id);

where $record->role_id can be 1 or 2

But it’s not working, if i chanche the accessRules with

'roles'=>array(User::ROLE_ADMIN),

users with User::ROLE_OPERATOR can access anyway. 🙁

Any suggestion? Thank you

ps:

this is the code inside checkAccess

$role_id = $this->getState('roles');
                if(($role_id == User::ROLE_ADMIN) || ($role_id == User::ROLE_OPERATOR))
            return true;
        else
            throw new CHttpException(404, '');

#10206
report it

sidtj at 2012/10/11 03:24am

How to display a different menu according to roles:

You can also use YiiSmartMenu extension.

$this->widget('ext.YiiSmartMenu',array( //calls YiiSmartMenu
    'items'=>array(             
        array('label'=>'Users', 'url'=>array('/manageUser/admin'), 'authItemName'=>'staff', //define what should be checked
...

#9897
report it

cgsmith at 2012/09/19 02:02pm

Multiple Role Check

Hello,

I needed to be able to check for multiple roles. With a minor change I can now do this:

Yii::app()->user->checkAccess('user,manager,admin')

*Note: you can do any combination | ; , .
I just choose commas. The command will find any match.

Place the following below $role === ‘admin’ in protected/components/WebUser.php

if (strstr($operation,$role) !== false) { // Check if multiple roles are available
     return true;
}

Full protected/components/WebUser.php file:

<?php
class WebUser extends CWebUser
{
    /**
     * Overrides a Yii method that is used for roles in controllers (accessRules).
     *
     * @param string $operation Name of the operation required (here, a role).
     * @param mixed $params (opt) Parameters for this operation, usually the object to access.
     * @return bool Permission granted?
     */
    public function checkAccess($operation, $params=array())
    {
        if (empty($this->id)) { // Not identified => no rights
            return false;
        }
 
        $role = $this->getState("roles"); // Get role of user
        if ($role === 'admin') {
            return true; // admin role has access to everything
        }
 
        if (strstr($operation,$role) !== false) { // Check if multiple roles are available
            return true;
        }
 
        return ($operation === $role);// allow access if the operation request is the current user's role
    }
}

#9766
report it

Mao Danh at 2012/09/09 11:00am

very useful

I had the idea, thanks.

#9616
report it

joyal at 2012/08/29 06:35am

Thanks

Thank you very much for the text.

#8183
report it

SpiLLeR at 2012/05/15 06:52am

Importantly

If set autoLogin=>true in config all data will saved in cookie and i can change my role. WebUser it’s better place for method getRole().

#8099
report it

Trejder at 2012/05/10 03:34am

Great text!

Hi, Thank you for a great text! I’ve been looking for "as simpliest as possible" solution like that, to incorporate it into smaller projects, that do not need a complex, "heavy" RBAC module. Thank you again.

#7967
report it

Sukhwinder at 2012/04/30 02:26am

The method isAdmin, isUser are not static

I have one doubt, since the methods isAdmin, isUser etc of Utils class are not static (public Methods) How can we refer them as Utils::isAdmin in access rules or i Zii widgest?

Leave a comment

Please login to leave your comment.

理解inode – 阮一峰的网络日志

理解inode

作者: 阮一峰

日期: 2011年12月 4日

inode是一个重要概念,是理解Unix/Linux文件系统和硬盘储存的基础。

我觉得,理解inode,不仅有助于提高系统操作水平,还有助于体会Unix设计哲学,即如何把底层的复杂性抽象成一个简单概念,从而大大简化用户接口。

下面就是我的inode学习笔记,尽量保持简单。

===================================

理解inode

作者:阮一峰

一、inode是什么?

理解inode,要从文件储存说起。

文件储存在硬盘上,硬盘的最小存储单位叫做"扇区"(Sector)。每个扇区储存512字节(相当于0.5KB)。

操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个"块"(block)。这种由多个扇区组成的"块",是文件存取的最小单位。"块"的大小,最常见的是4KB,即连续八个 sector组成一个 block。

文件数据都储存在"块"中,那么很显然,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为"索引节点"。

每一个文件都有对应的inode,里面包含了与该文件有关的一些信息。

二、inode的内容

inode包含文件的元信息,具体来说有以下内容:

  * 文件的字节数

  * 文件拥有者的User ID

  * 文件的Group ID

  * 文件的读、写、执行权限

  * 文件的时间戳,共有三个:ctime指inode上一次变动的时间,mtime指文件内容上一次变动的时间,atime指文件上一次打开的时间。

  * 链接数,即有多少文件名指向这个inode

  * 文件数据block的位置

可以用stat命令,查看某个文件的inode信息:

  stat example.txt

总之,除了文件名以外的所有文件信息,都存在inode之中。至于为什么没有文件名,下文会有详细解释。

三、inode的大小

inode也会消耗硬盘空间,所以硬盘格式化的时候,操作系统自动将硬盘分成两个区域。一个是数据区,存放文件数据;另一个是inode区(inode table),存放inode所包含的信息。

每个inode节点的大小,一般是128字节或256字节。inode节点的总数,在格式化时就给定,一般是每1KB或每2KB就设置一个inode。假定在一块1GB的硬盘中,每个inode节点的大小为128字节,每1KB就设置一个inode,那么inode table的大小就会达到128MB,占整块硬盘的12.8%。

查看每个硬盘分区的inode总数和已经使用的数量,可以使用df命令。

  df -i

查看每个inode节点的大小,可以用如下命令:

  sudo dumpe2fs -h /dev/hda | grep "Inode size"

由于每个文件都必须有一个inode,因此有可能发生inode已经用光,但是硬盘还未存满的情况。这时,就无法在硬盘上创建新文件。

四、inode号码

每个inode都有一个号码,操作系统用inode号码来识别不同的文件。

这里值得重复一遍,Unix/Linux系统内部不使用文件名,而使用inode号码来识别文件。对于系统来说,文件名只是inode号码便于识别的别称或者绰号。

表面上,用户通过文件名,打开文件。实际上,系统内部这个过程分成三步:首先,系统找到这个文件名对应的inode号码;其次,通过inode号码,获取inode信息;最后,根据inode信息,找到文件数据所在的block,读出数据。

使用ls -i命令,可以看到文件名对应的inode号码:

  ls -i example.txt

五、目录文件

Unix/Linux系统中,目录(directory)也是一种文件。打开目录,实际上就是打开目录文件。

目录文件的结构非常简单,就是一系列目录项(dirent)的列表。每个目录项,由两部分组成:所包含文件的文件名,以及该文件名对应的inode号码。

ls命令只列出目录文件中的所有文件名:

  ls /etc

ls -i命令列出整个目录文件,即文件名和inode号码:

  ls -i /etc

如果要查看文件的详细信息,就必须根据inode号码,访问inode节点,读取信息。ls -l命令列出文件的详细信息。

  ls -l /etc

六、硬链接

一般情况下,文件名和inode号码是"一一对应"关系,每个inode号码对应一个文件名。但是,Unix/Linux系统允许,多个文件名指向同一个inode号码。

这意味着,可以用不同的文件名访问同样的内容;对文件内容进行修改,会影响到所有文件名;但是,删除一个文件名,不影响另一个文件名的访问。这种情况就被称为"硬链接"(hard link)。

ln命令可以创建硬链接:

  ln 源文件 目标文件

运行上面这条命令以后,源文件与目标文件的inode号码相同,都指向同一个inode。inode信息中有一项叫做"链接数",记录指向该inode的文件名总数,这时就会增加1。

反过来,删除一个文件名,就会使得inode节点中的"链接数"减1。当这个值减到0,表明没有文件名指向这个inode,系统就会回收这个inode号码,以及其所对应block区域。

这里顺便说一下目录文件的"链接数"。创建目录时,默认会生成两个目录项:"."和".."。前者的inode号码就是当前目录的inode号码,等同于当前目录的"硬链接";后者的inode号码就是当前目录的父目录的inode号码,等同于父目录的"硬链接"。所以,任何一个目录的"硬链接"总数,总是等于2加上它的子目录总数(含隐藏目录)。

七、软链接

除了硬链接以外,还有一种特殊情况。

文件A和文件B的inode号码虽然不一样,但是文件A的内容是文件B的路径。读取文件A时,系统会自动将访问者导向文件B。因此,无论打开哪一个文件,最终读取的都是文件B。这时,文件A就称为文件B的"软链接"(soft link)或者"符号链接(symbolic link)。

这意味着,文件A依赖于文件B而存在,如果删除了文件B,打开文件A就会报错:"No such file or directory"。这是软链接与硬链接最大的不同:文件A指向文件B的文件名,而不是文件B的inode号码,文件B的inode"链接数"不会因此发生变化。

ln -s命令可以创建软链接。

  ln -s 源文文件或目录 目标文件或目录

八、inode的特殊作用

由于inode号码与文件名分离,这种机制导致了一些Unix/Linux系统特有的现象。

  1. 有时,文件名包含特殊字符,无法正常删除。这时,直接删除inode节点,就能起到删除文件的作用。

  2. 移动文件或重命名文件,只是改变文件名,不影响inode号码。

  3. 打开一个文件以后,系统就以inode号码来识别这个文件,不再考虑文件名。因此,通常来说,系统无法从inode号码得知文件名。

第3点使得软件更新变得简单,可以在不关闭软件的情况下进行更新,不需要重启。因为系统通过inode号码,识别运行中的文件,不通过文件名。更新的时候,新版文件以同样的文件名,生成一个新的inode,不会影响到运行中的文件。等到下一次运行这个软件的时候,文件名就自动指向新版文件,旧版文件的inode则被回收。

(完)

文档信息

使用Dnsmasq加速苹果App Store、iCloud、iTunes、Mac更新等访问速度 – 一路凯歌

使用Dnsmasq加速苹果App Store、iCloud、iTunes、Mac更新等访问速度

发表于 2012/11/27 志一
新浪微博
QQ空间
人人网
开心网

因为苹果良好的产品使用体验和软件生态环境,现在用iPhone、iPad、Mac的人越来越多,大多数人都用App Store(还有部分人只用盗版平台),缺点是很多访问都要走美国或者其他地区的服务器,打开iTunes或者App Store和下载应用的速度极慢,还有很多使用了Game Center的游戏,要好久才连上Game Center然后出现欢迎您回到Game Center,很影响心情。我们如果能加速苹果各种服务的访问速度,那体验就会完全不同。

文章的开头会写一些原理和自己动手做的方法。如果不感兴趣可以直接跳到最后看现成的配置文件。

转载请注明出自 http://zhiyi.us

原理

苹果绝大多数服务都使用了CDN,用户发起DNS查询请求时,DNS服务器会根据上游DNS(即你请求的这个DNS)的地址返回一个距离用户最近的数据中心IP,而造成国内访问速度慢的原因是国内发起查询时,所返回的CDN节点IP大多位于美国。我们要做的就是找到速度最快的IP,然后通过配置Dnsmasq,让查询DNS的时候,Dnsmasq直接返回这个IP。

已有简单解决方法的优缺点

基于此原理,现在网络上最常见最简单的解决苹果服务下载速度方式有两种,一种是在设备上更改DNS到v2ex dns或者FasterApple或者114.114.114.114等提供App Store加速的DNS,一种是拨VPN来加速。改DNS这种方式的优点是简单,只要会进设置界面就可以,缺点也是显而易见的:首先是这些DNS的访问速度可能慢,影响其他网站解析速度,另外下载完软件还要改回来,否则访问国内的网站有可能享受不到CDN带来的加速效果,很麻烦。还有一点是这些DNS返回的节点,在你的网络下不一定快。那么我们如果本地架设一个DNS,只负责苹果相关域名的解析且返回的是自己访问最快的IP,其他解析还交给本地ISP,这些问题就解决了。况且如果买的可刷DD-WRT的路由足够好,还可以无缝翻墙(这个以后再详说)。

本地架设DNS

本地架设DNS有很多方法,最简单的就是在淘宝花一二百块钱买一个可以刷DD-WRT的路由器用自带的Dnsmasq,或者有Windows做家庭服务器,在其上面装Dnsmasq来实现本地DNS,直接返回苹果服务速度最快的IP。这个方案的优点是,访问国内和其他网站的时候,可以查询本地ISP的DNS来享受解析速度和国内CDN服务带来的效果,并且本地DNS缓存也能加快所有域名的解析速度。还可以把Google的服务指向国内数据中心来无缝上Google(斯巴达时Google的国内数据中心也被封掉了)。

用Dnsmasq的原因是,它的配置最简单。

实现

给无线路由刷DD-WRT和在Windows或者Linux装Dnsmasq请自行Google搜索,假设你已经有Dnsmasq在运行并且知道如何配置了。

苹果的CDN目前看到的最常用的是两家,AkamaiChinaCache。Akamai是国际上很有名的CDN提供商,包括微软在内很多大企业都用其提供的CDN服务,缺点是在国内几乎没有节点。ChinaCache是国内做的很大的一家CDN服务商,优势是国内节点很多。苹果最近几个月开始一些国内服务通过ChinaCache提供。

要拿到访问最快的IP,我们可以手工去查DNS然后挨个去试验。

第一步是拿到苹果绝大多数服务用到的域名。方式是本地假设一个代理服务器,然后把Mac或者iPhone/iPad的代理指向自己的代理服务器,然后启动App Store和Game Center以及系统更新等需要网络交互的应用,使用各种功能,记录下所有请求的域名。

第二步是整理域名,有很多域名是带有规律性的,比如App Store下载的就是“a数字.phobos.apple.com”,图片是“a数字.mzstatic.com”,这种域名我们不用把几千个都查询一遍,查询一个,其他的就都是同一个IP。

第三步便是查询和测试IP。基于前人的经验,我们知道苹果位于台湾的节点大多访问比较快,于是我们选定4个DNS服务器来查询IP,第一个是本地ISP的,第二个是fasterapple的,第三个是v2ex dns的,第四个是台湾的168.95.1.1。选择这四个服务器的原因是,如果苹果该服务有本地CDN,用当地ISP的DNS能拿到最快的节点IP,第二和第三个是可以直接使用其他人的劳动成果,可能会搜到更快的,使用第四个DNS是能拿到台湾节点。针对每个域名,我们都用nslookup去查询这四个服务器,然后挨个ping拿到的ip,之后把最快的指定到配置文件里。要注意的是,苹果的CDN每个节点可能提供多种服务的加速,也有可能只提供一种加速,所以不要拿到一个IP就配置到所有域名里,还是要每个都试验一下。还要注意的是,nslookup时可能会拿到多个IP,这种情况一定要每个IP都测试,因为返回的几个节点IP速度可能相差很多,甚至ChinaCache返回的IP速度都有很大差别。

整理之后,我们就会获得一份最快的列表,配置到Dnsmasq里即可。把家里设备的DNS改为你装Dnsmasq的那台设备的IP后,享受加速的快感吧!

配置文件

这个配置文件是北京联通用户适用,最后更新于2012年11月27日。

# —– apple —–
address=/swscan.apple.com/17.164.1.22
address=/swdist.apple.com/123.125.162.28
address=/store.apple.com/123.103.32.171
address=/discussions.apple.com/173.223.33.54
address=/init.gc.apple.com/203.69.113.166
address=/metrics.apple.com/23.14.146.217
address=/itunes.apple.com/118.214.226.217
address=/contentdelivery.itunes.apple.com/17.152.19.127
address=/su.itunes.apple.com/96.16.226.217
address=/userpub.itunes.apple.com/17.154.66.35
address=/buy.itunes.apple.com/17.154.66.16
address=/se.itunes.apple.com/118.214.226.217
address=/gc.itunes.apple.com/203.69.113.127
address=/init.itunes.apple.com/203.69.113.127
address=/phobos.apple.com/203.69.113.42
address=/appldnld.apple.com/106.3.78.74
address=/adcdownload.apple.com/1.255.2.24
address=/mzstatic.com/203.69.113.240
address=/s.mzstatic.com/118.215.179.205
address=/itc.mzstatic.com/118.215.179.205
address=/metrics.mzstatic.com/118.215.179.205
address=/itunesconnect.apple.com/118.214.234.64
address=/ssl.apple.com/118.214.237.88
address=/www.icloud.com/173.223.34.46
server=/apple.com.edgekey.net/168.95.1.1
address=/p1-buy.itunes.apple.com/17.173.66.49
address=/p2-buy.itunes.apple.com/17.173.66.49
address=/p3-buy.itunes.apple.com/17.173.66.49
address=/p4-buy.itunes.apple.com/17.173.66.49
address=/p5-buy.itunes.apple.com/17.173.66.49
address=/p6-buy.itunes.apple.com/17.173.66.49
address=/p7-buy.itunes.apple.com/17.173.66.49
address=/p8-buy.itunes.apple.com/17.173.66.49
address=/p9-buy.itunes.apple.com/17.173.66.49
address=/p10-buy.itunes.apple.com/17.173.66.49
address=/p11-buy.itunes.apple.com/17.173.66.49
address=/p12-buy.itunes.apple.com/17.173.66.49
address=/p13-buy.itunes.apple.com/17.173.66.49
address=/p14-buy.itunes.apple.com/17.173.66.49
address=/p15-buy.itunes.apple.com/17.173.66.49
address=/p16-buy.itunes.apple.com/17.173.66.49
address=/p17-buy.itunes.apple.com/17.173.66.49
address=/p18-buy.itunes.apple.com/17.173.66.49
address=/p19-buy.itunes.apple.com/17.173.66.49
address=/p20-buy.itunes.apple.com/17.173.66.49
address=/p21-buy.itunes.apple.com/17.173.66.49
address=/p22-buy.itunes.apple.com/17.173.66.49
address=/p23-buy.itunes.apple.com/17.173.66.49
address=/p24-buy.itunes.apple.com/17.173.66.49
address=/p25-buy.itunes.apple.com/17.173.66.49
address=/p26-buy.itunes.apple.com/17.173.66.49
address=/p27-buy.itunes.apple.com/17.173.66.49
address=/p28-buy.itunes.apple.com/17.173.66.49
address=/p29-buy.itunes.apple.com/17.173.66.49
address=/p30-buy.itunes.apple.com/17.173.66.49
address=/p31-buy.itunes.apple.com/17.173.66.49
address=/p32-buy.itunes.apple.com/17.173.66.49
address=/p33-buy.itunes.apple.com/17.173.66.49
address=/p34-buy.itunes.apple.com/17.173.66.49
address=/p35-buy.itunes.apple.com/17.173.66.49
address=/p36-buy.itunes.apple.com/17.173.66.49
address=/p37-buy.itunes.apple.com/17.173.66.49
address=/p38-buy.itunes.apple.com/17.173.66.49
address=/p39-buy.itunes.apple.com/17.173.66.49
address=/p40-buy.itunes.apple.com/17.173.66.49
address=/p41-buy.itunes.apple.com/17.173.66.49
address=/p42-buy.itunes.apple.com/17.173.66.49
address=/p43-buy.itunes.apple.com/17.173.66.49
address=/p44-buy.itunes.apple.com/17.173.66.49
address=/p45-buy.itunes.apple.com/17.173.66.49
address=/p46-buy.itunes.apple.com/17.173.66.49
address=/p47-buy.itunes.apple.com/17.173.66.49
address=/p48-buy.itunes.apple.com/17.173.66.49
address=/p49-buy.itunes.apple.com/17.173.66.49
address=/p50-buy.itunes.apple.com/17.173.66.49
address=/p51-buy.itunes.apple.com/17.173.66.49
address=/p52-buy.itunes.apple.com/17.173.66.49
address=/p53-buy.itunes.apple.com/17.173.66.49
address=/p54-buy.itunes.apple.com/17.173.66.49
address=/p55-buy.itunes.apple.com/17.173.66.49
address=/p56-buy.itunes.apple.com/17.173.66.49
address=/p57-buy.itunes.apple.com/17.173.66.49
address=/p58-buy.itunes.apple.com/17.173.66.49
address=/p59-buy.itunes.apple.com/17.173.66.49
address=/p60-buy.itunes.apple.com/17.173.66.49
address=/p61-buy.itunes.apple.com/17.173.66.49
address=/p62-buy.itunes.apple.com/17.173.66.49
address=/p63-buy.itunes.apple.com/17.173.66.49
address=/p64-buy.itunes.apple.com/17.173.66.49
address=/p65-buy.itunes.apple.com/17.173.66.49
address=/p66-buy.itunes.apple.com/17.173.66.49
address=/p67-buy.itunes.apple.com/17.173.66.49
address=/p68-buy.itunes.apple.com/17.173.66.49
address=/p69-buy.itunes.apple.com/17.173.66.49
address=/p70-buy.itunes.apple.com/17.173.66.49

此条目发表在 互联网, 杂七杂八 分类目录,贴了 , , 标签。将固定链接加入收藏夹。

web开发设计人员不可不用的在线web工具和应用 | CSS | 前端观察

web开发设计人员不可不用的在线web工具和应用

发表于 06. Jan, 2013, 分类: CSS, Javascript , 4 条评论 »标签:

大家可能还记得在过去的文章我们我们曾经介绍我们收集的前端开发人员必备的工具,脚本和资源,在今天的这篇文章中,我们将继续推荐给大家一组我们精挑细选的web开发设计必备的在线工具应用。相信会在web开发和设计的过程中给你带来方便和快捷!

前端开发相关

jsfiddle

jsfiddle是老牌的在线调试和分享代码的网站,它可以帮助你在线调试javascript/css/html代码, 并且方便的发布到社区,论坛或者社交媒体上与朋友们分享或者提问。整合了很多的不同的类库供大家选择。 类似的工具还有jsbin.com,也非常不错。更多工具请参考我们以前的文章

codepen

codepen也是一个可以帮助你在线调试和分享前端代码的地方,和jsfiddle不太一样的地方,它更像一个社区,你可以看到最近的用户添加的在线演示或者代码,并且可以更加方便的分享到其它网站或者是fork。和jsfiddle相比,更像一个帮助展示前端效果的平台。如果你常常光顾的话,能够找到很多很不错的前端效果,包括html5,js,CSS3等等。相当不错,向大家推荐!

gbdebug

gbdebug是gb标签社区中整合的前端调试工具,最初是为了方便社区的朋友提问或者展示jQuery代码效果而开发,如果你使用jsfiddle的话,肯定会觉得非常熟悉。最强大的在于和GB社区内容的整合,你可以分享自己开发的前端展示,模板或者教程,并且使用gbdebug来添加”在线调试“,更方便大家在线阅读和调试。

reFiddle+

如果你是个正则表单式的狂热爱好者的话,这款在线工具肯定是你必备的工具之一,reFiddle+可以帮助你在线的调试正则表单式,你只需要输入数据和对应的正则表达式后,就可以看到高亮显示的结果,是不是非常直观和简单,相信你肯定会喜欢!

RubyFiddle

如果你的开发语言是Ruby的话,你可能已经知道了这个在线工具 RubyFiddle,使用这个在线工具可以帮助你在线的调试Ruby代码,有点儿类似jsfiddle和codepen,只不过是Ruby版本的代码而已。 你只需要粘贴ruby代码并且点击”RUN”按钮即可,甚至支持Vim或者Emacs的输入方式哦!

CSS3 generator

这个CSS3的代码生成器和一般的CSS3代码生成工具不太一样,它使用了视差滚动的方式来展示内容,并且提供了自定义的选项,可以动态的生成你需要的CSS3代码。超酷超有型!

HTML5 Please

这个网站在之前的相关文章中也介绍过,如果你需要了解如何正确的使用HTML5相关技术的话,最好仔细的看看这个工具网站吧,相当的实用!

前端性能优化工具

DOM Monster

当我们开发web应用的时候,性能是一个永远不能回避的问题。其实对于DOM的性能调试也是一个不可或缺的过程。使用DOM monster你只需要添加到你的”书签中“,在任何需要调试的页面点击这个书签,它就能够帮助你列出目前页面出现的性能问题。是不是很方便!

zBugs

zbugs看上去像是一个bug相关工具,其实它是一个快速帮助你压缩CSS/Javascript的在线工具,你只需要提供一个你的网站的链接,下载相关的文件,再上传到你的网站即可!

其它类型工具

BuiltWith

buildwith是一个帮助里了解网站使用技术的在线工具,你只需输入你要检测的网站地址,它会给你生成此网站使用的前后台技术和相关链接,相信如果你是一个黑客的话,这个工具必不可少 :D







作者: terry
To be developer and designer .

如果你喜欢本文,欢迎 订阅本站 以获得本站最新内容。.

您或许也会喜欢:

  • 暂无相关文章

mysql 设置外键关联错误 150 – w21fanfan1314的专栏 – 博客频道 – CSDN.NET

当你试图在mysql中创建一个外键的时候,这个出错会经常发生,这是非常令人沮丧的。像这种不能创建一个.frm 文件的报错好像暗示着操作系统的文件的权限错误或者其它原因,但实际上,这些都不是的,事实上,这个mysql报错已经被报告是一个mysql本身的bug并出现在mysql
开发者列表当中很多年了,然而这似乎又是一种误导。 

在很多实例中,这种错误的发生都是因为mysql一直以来都不能很好的支持的关系的问题, 更不幸的是它也并没有指明到底是哪一个问题会导致上面那种错误,下面我把导致这个可怕 的150错误的常见原因列出来了,并且我以可能性的大小作了排序 
已知的原因: 
1, 两个字段的类型或者大小不严格匹配,例如,如果一个是INT(10), 那么外键也必须设置成INT(10), 而不是 INT(11) 也不能是 TINYINT. 你得使用 SHOW 命令来查看字段的大小,因为一些查询浏览器有时候把 int(10)
和int(11) 都显示为integer。另外,你还必须确定两个字段是否一个为 SIGNED,而另一个又是UNSIGNED, 这两字段必须严格地一致匹配,更多关于signed 和 unsigned 的信息,请参阅:http://www.verysimple.com/blog/?p=57 

2, 你试图引用的其中一个外键没有建立起索引,或者不是一个primary key , 如果其中一个不是primary key 的放,你必须为它创建一个索引。 
3, 外键的名字是一个已经存在的一个键值了,这个时候,你应该检查你的数据库以确保外健名字是唯一的,或者你在键名后面加上几个随机的字符以测试是否是这个原因。 
4, 其中一个或者两个表是MyISAM引擎的表,若想要使用外键约束,必须是InnoDB引擎,(实际上,如果两个表都是MyISAM 引擎的,这个错误根本不会发生,但也不会产生外键),你可以通过查询浏览器来设置表的引擎类型 
5, 你可能设置了ON DELETE SET NULL, 但是相关的键的字段又设置成了NOTS NULL 值。你可能通过修改cascade 的属性值或者把字段属性设置成 allow null 来搞定这个bug. 
6, 请确定你的Charset 和 Collate 选项在表级和字段级上的一致 
7, 你可能设置为外键设置了一个默认值,如 default=0 
8, 在这个关系里面,其中的一个字段是一个混合键值中的一个,它没有自己独立的索引,这时,你必须为它创建一个独立的索引。 
9, ALTER 声明中有语法错误 

10. 要连接的两个表的编码格式不同



我中了第4条,呵呵

mac下配置php调试环境 – 鑫的方向

10

Aug/12

mac下配置php调试环境

by babo under mac

已经用了mac有2个月了,基本网络管理和开发工作都在mac下面了。

下面说下如何配置mac下面的php调试环境

我的环境:macbook air 2012款   Mac OS X 10.8 系统

php环境:XAMPP 1.7.3

开发工具:一般用sublime text 2,调试或者项目开发用netbeans

转载请注明:来源http://xinlogs.com

 

下面说说如何安装调试环境,主要就是xdebug的安装和配置

这里需要提下mac下面的安装包管理工具homebrew

我就是用brew来安装xdebug的。homebrew是一个用ruby开发的,类似macport的工具。可以在线安装软件,就像linux下的apt-get或者yum一样。


安装homebrew

需要系统可以支持ruby,通过ruby -v命令可以查看ruby版本。mac os 自带ruby

直接在终端里输入

ruby <(curl -fsSk https://raw.github.com/mxcl/homebrew/go)

上面命令执行完,brew命令就可以执行了。

先运行brew doctor测试下环境是否完整,如果需要,还要添加Command Line Tools。

添加Command Line Tools

打开Xcode,在preferences->downloads里面下载并安装Command Line Tools

一切正确的话,执行brew doctor显示如下

babomatoMacBook-Air:~ babo$ brew doctor

Your system is raring to brew.

安装好brew后,先执行brew update更新下软件包的源,然后再加载两个formula的资源

brew update

brew tap josegonzalez/homebrew-php

brew tap homebrew/dupes

完成后,就可以通过brew search和brew install来查找并安装软件了

brew install php53-xdebug

安装好xdebug后,还需要配置php.ini才可以正常使用。

先用brew info查看xdebug相关信息

babomatoMacBook-Air:~ babo$ brew info php53-xdebug
php53-xdebug: stable 2.2.1, HEAD
http://xdebug.org
Depends on: autoconf
/usr/local/Cellar/php53-xdebug/2.2.1 (4 files, 360K) *
https://github.com/josegonzalez/homebrew-php/commits/master/Formula/php53-xdebug.rb
==> Options
<code–<without-config-file
    Do not add ext-xdebug.ini to /usr/local/etc/php/5.3/conf.d
==> Caveats
To finish installing xdebug for PHP 5.3:
  * /usr/local/etc/php/5.3/conf.d/ext-xdebug.ini was created,
    do not forget to remove it upon extension removal.
  * Restart your webserver.
  * Write a PHP page that calls "phpinfo();"
  * Load it in a browser and look for the info on the xdebug module.
  * If you see it, you have been successful!

通过信息我们知道xdebug版本是 stable 2.2.1

配置文件在/usr/local/etc/php/5.3/conf.d/ext-xdebug.ini

我们xampp的配置文件在/Applications/XAMPP/xamppfiles/etc/php.ini

将xdebug的配置加入到/Applications/XAMPP/xamppfiles/etc/php.ini

在文件最下面加入如下信息

[xdebug]
zend_extension="/usr/local/Cellar/php53-xdebug/2.2.1/xdebug.so"
        xdebug.remote_enable=1
        xdebug.remote_host=localhost
        xdebug.remote_port=9000
        xdebug.remote_handler=dbgp

重启xampp的apache,然后访问phpinfo查看是否成功加载xdebug



配置netbeans可以调用xdebug

首先将netbeans的偏好设置里的php5解释器设置为xampp的php。不要用系统自带的


再设置调试的端口


这些都设置好,就可以调试php代码了

最后放一张我调试的截图


标签: php mac

相关日志:

mac下用ssh转发端口

如何找到执行慢的php程序

修改EMiDream模板

php性能调试工具

批量更换115网盘链接

<< 批量部署系统方案
| |
批量更换115网盘链接 >>

Google 黑板报 – Google (谷歌)中国的博客网志,走近我们的产品、技术和文化: 响应式设计 – 利用媒体查询的力量

Google (谷歌)中国的博客网志,走近我们的产品、技术和文化

响应式设计 – 利用媒体查询的力量

2013年1月25日 下午 06:45:00

发表者:Rupert Breheny, Edward Jung, & Matt Zürrer,Google网站管理员小组
原文:Responsive design – harnessing the power of media queries
转载自:谷歌中文网站管理员博客
发布时间:2013年1月24日 下午 03:43:00

我们热爱数据,同时我们还花了大量时间分析自己网站的数据情况。而对于采取同样行为的网站开发者来说,也必然会注意到来自移动设备的访问量在大幅增加。在过去的一年里,我们发现,许多主要网站的页面访问量中有相当一部分来自智能手机及平板电脑。而这类用户往往使用的都是支持最新HTML、CSS、以及JavaScript的浏览器,但同时,他们的屏幕宽度往往也都很有限,一般仅为320像素。

我们对产品的承诺是简单易操作,而这意味着,我们将竭尽全力保证所有用户都能获得愉快的网络浏览体验。我们之前面临的艰难抉择在于,究竟该开发移动网络专属网站还是该调整现有及新建网站以使其既适合台式机,又适合移动设备。如果开发两种网站,好处在于我们可以更有针对性的满足硬件设备需求;而保留一种网站,只做调整的话,则可以保留一种标准的URL,避免出现复杂的重定向问题,而且还可以简化网址共享。出于对可维护性的考虑,我们倾向于保留一种网站类型,同时我们也开始考虑如何才能满足以下准则:

1.我们的页面应保证在任何屏幕分辨率下都能够清晰渲染
2.我们对一种内容进行标注,使其在任何设备上都能够浏览
3.不论视窗尺寸如何,都不能出现水平滚动条

排列内容,改进导航,重新缩放图片尺寸 – Chromebooks

实施
作为起点,简单的语义标记在布局需要改变的情况下能使我们的页面更具灵活性。通过保证样式表使用流式布局,我们就能逐步实现在移动设备上的应用。我们舍弃了针对浏览器设定宽度的方法,改为采用最大宽度。至于高度方面,我们采用最小高度,这样大字体或多行文本就不会破坏浏览器边缘。而为了保证固定宽度的图片不会将液柱“撑开”,我们将运用CSS规则:

img
 {
max-width: 100%;

}

流式布局是一个好的开始,但仍缺乏一种特定的技巧。幸运的是,包括IE9+及大部分移动设备在内的现代浏览器均支持媒体查询功能。而这正是造就在移动浏览器上用户体验下降的网站与经过改进,能够有效利用简洁用户界面的网站之间区别的因素。不过,首先我们必须考虑的是,智能手机怎样向服务器表明了自己。

窗口
像素何时不为像素?答案是在智能手机上的时候。按照默认设定,智能手机浏览器伪装出高分辨率台式电脑浏览器的效果,其所呈现的页面会让用户以为自己是在通过台式电脑屏幕进行浏览。正因为如此,才有了必须放大才能阅读的微小文本“综观模式”。不论屏幕的实际物理像素是多少,Android版浏览器的默认窗口宽度为800像素,IOS浏览器为980像素。

为保证浏览器将您的页面渲染得可读性更强,您需要用到窗口元标记元素:

尽管移动设备的屏幕分辨率彼此之间差异很大,但现在的大多数智能手机浏览器使用的都是320像素的标准设备宽度。如果您的移动设备宽度为640物理像素,那么320像素宽的图片将通过倍增像素数目扩大至全屏尺寸。这也是为什么文本在小屏幕上看起来更加干净 – 与在标准台式电脑显示屏上时相比,像素密度增加了一倍。

在窗口元标记标签中根据设备宽度设定文件宽度的好处在于,当用户改变智能手机或平板电脑的定向时,设定也能够随之更新。将这一功能与媒体查询结合在一起可以使您在用户旋转设备时改进布局:


@media screen and (min-width:480px) and (max-width:800px) {
/* Target landscape smartphones, portrait tablets, narrow desktops
*/
}


 @media screen and (max-width:479px) {
/* Target portrait smartphones */
 }

在实际操作中,您可能会发现,根据网站在不同设备上连贯及表现效果的不同,您或许会用到各种断点。您也可以不涉及像素次元,在系统支持的情况下,通过定向媒体查询将某些特定定向设为目标。

 @media all and (orientation: landscape) {
  /* Target device in landscape mode */
}


@media all and (orientation: portrait) {
  /* Target device in portrait mode */
}

排列内容,较小图片-Google文化研究所

媒体查询范例

近日,我们重新启动了Google大全页面。除采用了流式布局以外,我们还加入了部分媒体查询功能以改善小屏用户,如平板电脑和智能手机用户的浏览效果。

我们并未采用针对特定设备分辨率的做法,而是设计了各种尺寸的断点。针对分辨率超过1024像素的屏幕,我们根据12列格,将页面按照原始效果进行渲染。而针对801像素至1024像素之间的屏幕,我们将通过流式布局为您呈现稍微经过压缩的浏览效果。

只有在屏幕分辨率低于800像素时,非核心内容才会被置于页面底端:


@media screen and (max-width: 800px) {
  /* specific CSS */
}

通过媒体查询功能,我们也踏入了智能手机领域:


@media screen and (max-width: 479px) {
  /* specific CSS */
}

至此,我们已不再载入大型图片,而是改为排列内容区块。另外,我们还会在不同内容之间插入空白,使内容处于不同区域且更容易辨别。

通过以上的简单措施,我们保证了网站在各种不同设备上皆能使用。

排列内容及移除大型图片-Google大全

总结

我们必须牢记,保证网站在移动设备及窄小窗口上也能浏览绝不是一件容易的事情。流式布局虽然是一个不错的开端,但我们仍需在设计上做出部分妥协。媒体查询功能虽然有助于许多设备改善浏览效果,但我们不能忘记,有25%的网站访问量仍是通过那些不支持此技术的台式电脑浏览器进行的。此外,该功能在运行方面也仍有不足之处。而如果您的网站上设置了好玩的插件,使用鼠标的用户或许能正常浏览,但对于难以实现精确操作的触屏用户,恐怕会带来很多不便。

关键的问题在于,既要早测试,又要常测试。花时间在智能手机或平板电脑上浏览自己的网站绝对是值得的。而如果您无法通过实际设备进行测试,您可以使用 Android SDK模拟器IOS模拟器。您也可以请朋友或同事在他们的设备上浏览您的网站来观察效果。

移动浏览器用户是新的网络用户中十分重要的一个组成部分。而学会如何满足他们的需求也是专业开发中一个激动人心的新领域。

更多Google响应式设计范例:
www.google.com.hk/about/
www.google.com.hk/goodtoknow
www.google.com.hk/culturalinstitute
www.google.com.hk/events/sciencefair
www.google.com.hk/intl/en/chrome/devices
picasa.google.com.hk/

标签:

固定链接  |

引用此贴的链接:

创建链接

Copyright © 2010 Google Inc. All rights reserved.

Privacy Policy
Terms of Service

design patterns – What should my Objective-C singleton look like? – Stack Overflow

My singleton accessor method is usually some variant of:

static MyClass *gInstance = NULL;

+ (MyClass *)instance
{
    @synchronized(self)
    {
        if (gInstance == NULL)
            gInstance = [[self alloc] init];
    }

    return(gInstance);
}

What could I be doing to improve this?

Rachel
31.4k341106

asked
Sep 28 ’08 at 3:38

schwa
8,596122748

1  

You probably want return(gInstance); – Dre Sep 28 ’08 at 3:40

23  

What you have is fine, though you could move the global variable declaration into your +instance method (the only place it needs to be used, unless you’re allowing it to be set as well) and use a name like +defaultMyClass or +sharedMyClass for your method. +instance isn’t intention-revealing. – Chris Hanson Sep 28 ’08 at 9:37

3  

Over 100 votes for a question which already includes an answer? It should have been added as a separate answer to the question. – Chei Jul 5 ’11 at 8:56

2  

Please don’t refer to self in static methods. While it is technically valid to do so in Objective-C, this is not a good habit to get into. – aroth Aug 5 ’11 at 5:06

13  

@aroth: Objective-C does not have static methods, and using self in a class method used as an initializer is actually essential to make subclassing work properly. – Josh Caswell Sep 26 ’11 at 7:09

show 5 more comments

feedback

25 Answers

active
oldest
votes

up vote
192
down vote

accepted

Another option is to use the +(void)initialize method. From the documentation:

The runtime sends initialize to each class in a program exactly one time just before the class, or any class that inherits from it, is sent its first message from within the program. (Thus the method may never be invoked if the class is not used.) The runtime sends the initialize message to classes in a thread-safe manner. Superclasses receive this message before their subclasses.

So you could do something akin to this:

static MySingleton *sharedSingleton;

+ (void)initialize
{
    static BOOL initialized = NO;
    if(!initialized)
    {
        initialized = YES;
        sharedSingleton = [[MySingleton alloc] init];
    }
}
answered
Dec 5 ’08 at 8:24

Robbie Hanson
2,71411111

7  

If the runtime will only ever call this once, what does the BOOL do? Is that a precaution in case someone calls this function explicitly from their code? – Aftermathew Apr 3 ’09 at 17:28

5  

Yes, it is a precaution since the function can also be called directly. – Robbie Hanson Apr 8 ’09 at 4:32

30  

This also is required because there could be subclasses. If they don’t override +initialize their superclasses implementation will be called if the subclass is first used. – Sven Sep 6 ’10 at 21:25

3  

@Paul you can override the release method and make it empty. 🙂 – Zoidberg Mar 25 ’11 at 22:20

4  

@aryaxt: From the docs listed, this is already thread safe. So, the call is once per runtime — period. This would seem to be the correct, thread-safe, optimally efficient solution. – lilbyrdie Jun 23 ’11 at 13:52

show 10 more comments

feedback

@interface MySingleton : NSObject
{
}

+ (MySingleton *)sharedSingleton;
@end

@implementation MySingleton

+ (MySingleton *)sharedSingleton
{
  static MySingleton *sharedSingleton;

  @synchronized(self)
  {
    if (!sharedSingleton)
      sharedSingleton = [[MySingleton alloc] init];

    return sharedSingleton;
  }
}

@end

[Source]

answered
Sep 28 ’08 at 3:46

Ben Hoffstein
24.3k34576

7  

This is all you should usually use for singletons. Among other things, keeping your classes separately instantiable makes them easier to test, because you can test separate instances instead of having a way to reset their state. – Chris Hanson Sep 28 ’08 at 9:36

3  

Stig Brautaset: No, it is not okay to leave out the @synchronized in this example. It is there to handle the possible race-condition of two threads executing this static function at the same time, both getting past the "if(!sharedSingleton)" test at the same time, and thus resulting in two [MySingleton alloc]s… The @synchronized {scope block} forces that hypothetical second thread to wait for the first thread to exit the {scope block} before being allowed to proceed into it. I hope this helps! =) – MechEthan Jul 13 ’11 at 16:29

3  

What stops someone from still making their own instance of the object? MySingleton *s = [[MySingelton alloc] init]; – lindon fox Oct 28 ’11 at 5:31

1  

@lindonfox What is the answer to your question? – Raffi Khatchadourian Dec 20 ’11 at 21:48

1  

@Raffi – sorry I think I must have forgot to paste in my answer. Anyway, I got the book Pro Objective-C Design Patterns for iOS and it spells out how you make a "strict" singelton. Basically since you can’t make the initiating methods private, you need to override the methods alloc and copy. So if you try and do something like [[MySingelton alloc] init] you will get a run time error (though not a compile time error unfortunately). I don’t understand how all the details of the object creation, but you implement + (id) allocWithZone:(NSZone *)zone which is called in sharedSingleton – lindon fox Dec 21 ’11 at 3:42

show 4 more comments

feedback

Since Kendall posted a threadsafe singleton that attempts to avoid locking costs, I thought I would toss one up as well:

#import <libkern/OSAtomic.h>

static void * volatile sharedInstance = nil;                                                

+ (className *) sharedInstance {                                                                    
  while (!sharedInstance) {                                                                          
    className *temp = [[self alloc] init];                                                                 
    if(!OSAtomicCompareAndSwapPtrBarrier(0x0, temp, &sharedInstance)) {
      [temp release];                                                                                   
    }                                                                                                    
  }                                                                                                        
  return sharedInstance;                                                                        
}

Okay, let me explain how this works:

  1. Fast case: In normal execution sharedInstance has already been set, so the while loop is never executed and the function returns after simply testing for the variable’s existence;

  2. Slow case: If sharedInstance doesn’t exist, then an instance is allocated and copied into it using a Compare And Swap (‘CAS’);

  3. Contended case: If two threads both attempt to call sharedInstance at the same time AND sharedInstance doesn’t exist at the same time then they will both initialize new instances of the singleton and attempt to CAS it into position. Whichever one wins the CAS returns immediately, whichever one loses releases the instance it just allocated and returns the (now set) sharedInstance. The single OSAtomicCompareAndSwapPtrBarrier acts as both a write barrier for the setting thread and a read barrier from the testing thread.

answered
Mar 15 ’10 at 18:56

Louis Gerbarg
27.1k45070

17  

This is complete overkill for the at-most one time it can happen during an application’s lifetime. Nevertheless, it is spot-on correct, and the compare-and-swap technique is a useful tool to know about, so +1. – Steve Madsen Apr 22 ’10 at 14:37

Nice answer – the OSAtomic family is a good thing to know about – Bill Feb 27 ’11 at 1:24

1  

@Louis: Amazing, really enlightening answer! One question though: what should my init method do in your approach? Throwing an exception when sharedInstance is initialized is not a good idea, I believe. What to do then to prevent user calling init directly many times? – delirus Sep 1 ’11 at 16:39

2  

I generally don’t prevent it. There are often valid reasons to allow what is generally a singleton to multiply instantiated, the most commons is for certain types of unit testing. If I really wanted to enforce a single instance I would probably have the init method check to see if the global existed, and if it did I have it release self and return the global. – Louis Gerbarg Sep 2 ’11 at 3:57

Hmm, why is the volatile qualification here necessary? Since sharedInstance is only initialized once how come we are preventing compiler from caching it in register by using volatile? – Tony Dec 29 ’11 at 17:37

show 1 more comment

feedback

Per my other answer below, I think you should be doing:

+ (id)sharedFoo
{
    static dispatch_once_t once;
    static MyFoo *sharedFoo;
    dispatch_once(&once, ^ { sharedFoo = [[self alloc] init]; });
    return sharedFoo;
}
answered
Sep 28 ’08 at 7:05

Colin Barrett
3,17211120

4  

Don’t bother with all of what you’re doing above. Make your (hopefully extremely few) singletons separately-instantiable, and just have a shared/default method.

What you’ve done is only necessary if you really, truly, ONLY want a single instance of your class. Which you don’t, esp. for unit tests. – Chris Hanson Sep 28 ’08 at 9:35

The thing is this is the Apple sample code for "creating a singleton". But yeah, you’re absolutely right. – Colin Barrett Oct 23 ’08 at 1:39

The Apple sample code is correct if you want a "true" singleton (i.e. an object that can only be instantiated once, ever) but as Chris says, this is rarely what you want or need whereas some kind of settable shared instance is what you usually want. – Luke Redpath Jul 25 ’09 at 1:25

+1 because singletons are generally a bad idea, and the +(id)sharedFoo approach works great. – Tom Dalling Sep 27 ’11 at 4:53

1  

Unit tests aside, there’s nothing speaking against this solution, correct? And it’s fast and safe. – LearnCocos2D Apr 6 ’12 at 20:19

show 1 more comment

feedback

static MyClass *sharedInst = nil;

+ (id)sharedInstance
{
    @synchronize( self ) {
        if ( sharedInst == nil ) {
            /* sharedInst set up in init */
            [[self alloc] init];
        }
    }
    return sharedInst;
}

- (id)init
{
    if ( sharedInst != nil ) {
        [NSException raise:NSInternalInconsistencyException
            format:@"[%@ %@] cannot be called; use +[%@ %@] instead"],
            NSStringFromClass([self class]), NSStringFromSelector(_cmd), 
            NSStringFromClass([self class]),
            NSStringFromSelector(@selector(sharedInstance)"];
    } else if ( self = [super init] ) {
        sharedInst = self;
        /* Whatever class specific here */
    }
    return sharedInst;
}

/* These probably do nothing in
   a GC app.  Keeps singleton
   as an actual singleton in a
   non CG app
*/
- (NSUInteger)retainCount
{
    return NSUIntegerMax;
}

- (oneway void)release
{
}

- (id)retain
{
    return sharedInst;
}

- (id)autorelease
{
    return sharedInst;
}
answered
Sep 28 ’08 at 4:34
Michael Nickerson

3  

I noticed that clang complains about a leak if you don’t assign the result of [[self alloc] init] to sharedInst. – pix0r May 6 ’09 at 18:21

Subverting init like this is a pretty ugly approach IMO. Don’t mess with init and/or the actual creation of the object. If you instead go for a controlled point of access to a shared instance, while not hard-baking singleton into the object, you’ll have a happier time later if writing tests etc. Hard singletons are far too overused. – occulus Jan 14 at 13:41

feedback

Edit: This implementation obsoleted with ARC. Please have a look at How do I implement an Objective-C singleton that is compatible with ARC? for correct implementation.

All the implementations of initialize I’ve read in other answers share a common error.

+ (void) initialize {
  _instance = [[MySingletonClass alloc] init] // <----- Wrong!
}

+ (void) initialize {
  if (self == [MySingletonClass class]){ // <----- Correct!
      _instance = [[MySingletonClass alloc] init] 
  }
}

The Apple documentation recommend you check the class type in your initialize block. Because subclasses call the initialize by default. There exists a non-obvious case where subclasses may be created indirectly through KVO. For if you add the following line in another class:

[[MySingletonClass getInstance] addObserver:self forKeyPath:@"foo" options:0 context:nil]

Objective-C will implicitly create a subclass of MySingletonClass resulting in a second triggering of +initialize.

You may think that you should implicitly check for duplicate initialization in your init block as such:

- (id) init { <----- Wrong!
   if (_instance != nil) {
      // Some hack
   }
   else {
      // Do stuff
   }
  return self;
}

But you will shoot yourself in the foot; or worse give another developer the opportunity to shoot themselves in the foot.

- (id) init { <----- Correct!
   NSAssert(_instance == nil, @"Duplication initialization of singleton");
   self = [super init];
   if (self){
      // Do stuff
   }
   return self;
}

TL;DR, here’s my implementation

@implementation MySingletonClass
static MySingletonClass * _instance;
+ (void) initialize {
   if (self == [MySingletonClass class]){
      _instance = [[MySingletonClass alloc] init];
   }
}

- (id) init {
   ZAssert (_instance == nil, @"Duplication initialization of singleton");
   self = [super init];
   if (self) {
      // Initialization
   }
   return self;
}

+ (id) getInstance {
   return _instance;
}
@end

(Replace ZAssert with our own assertion macro; or just NSAssert.)

answered
Jun 7 ’11 at 20:14

lorean
1,112215

1  

I would just live simpler and avoid initialize altogether. – Tom Andersen May 17 ’12 at 13:24

feedback

A thorough explanation of the Singleton macro code is on the blog Cocoa With Love

http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html.

answered
Jun 23 ’09 at 12:01

Matthieu Cormier
1,149721

feedback

I have an interesting variation on sharedInstance that is thread safe, but does not lock after the initialization. I am not yet sure enough of it to modify the top answer as requested, but I present it for further discussion:

// Volatile to make sure we are not foiled by CPU caches
static volatile ALBackendRequestManager *sharedInstance;

// There's no need to call this directly, as method swizzling in sharedInstance
// means this will get called after the singleton is initialized.
+ (MySingleton *)simpleSharedInstance
{
    return (MySingleton *)sharedInstance;
}

+ (MySingleton*)sharedInstance
{
    @synchronized(self)
    {
        if (sharedInstance == nil)
        {
            sharedInstance = [[MySingleton alloc] init];
            // Replace expensive thread-safe method 
            // with the simpler one that just returns the allocated instance.
            SEL origSel = @selector(sharedInstance);
            SEL newSel = @selector(simpleSharedInstance);
            Method origMethod = class_getClassMethod(self, origSel);
            Method newMethod = class_getClassMethod(self, newSel);
            method_exchangeImplementations(origMethod, newMethod);
        }
    }
    return (MySingleton *)sharedInstance;
}
answered
Feb 19 ’10 at 6:15

1  

+1 that’s really intriguing. I might use class_replaceMethod to transform sharedInstance into a clone of simpleSharedInstance. That way you wouldn’t ever have to worry about acquiring an @synchronized lock again. – Dave DeLong Feb 19 ’10 at 6:19

It’s the same effect, using exchangeImplementations means that after init when you call sharedInstance, you are really calling simpleSharedInstance. I actually started out with replaceMethod, but decided it was better to just switch the implementations around so the original still existed if needed… – Kendall Helmstetter Gelner Feb 19 ’10 at 7:46

In further testing, I could not get replaceMethod to work – in repeated calls, the code still called the original sharedInstance instead of simpleSharedInstance. I think it may be because they are both class level methods… The replace I used was: class_replaceMethod(self, origSel, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)); and some variations thereof. I can verify the code I have posted works and simpleSharedInstance is called after the first pass through sharedInstance. – Kendall Helmstetter Gelner Feb 19 ’10 at 7:58

You can make a thread safe version that does not pay locking costs after initialization without doing a bunch of runtime mucking, I have posted an implementation below. – Louis Gerbarg Mar 15 ’10 at 18:57

1  

+1 great idea. I just love those things one can do with the runtime. But in most cases this probably is premature optimization. If I’d really have to get rid of the synchronization cost I’d probably use the lockless version by Louis. – Sven Sep 6 ’10 at 21:35

show 1 more comment

feedback

I’ve rolled singleton into a class, so other classes can inherit singleton properties.

Singleton.h :

static id sharedInstance = nil;

#define DEFINE_SHARED_INSTANCE + (id) sharedInstance {  return [self sharedInstance:&sharedInstance]; } \
                               + (id) allocWithZone:(NSZone *)zone { return [self allocWithZone:zone forInstance:&sharedInstance]; }

@interface Singleton : NSObject {

}

+ (id) sharedInstance;
+ (id) sharedInstance:(id*)inst;

+ (id) allocWithZone:(NSZone *)zone forInstance:(id*)inst;

@end

Singleton.m :

#import "Singleton.h"


@implementation Singleton


+ (id) sharedInstance { 
    return [self sharedInstance:&sharedInstance];
}

+ (id) sharedInstance:(id*)inst {
    @synchronized(self)
    {
        if (*inst == nil)
            *inst = [[self alloc] init];
    }
    return *inst;
}

+ (id) allocWithZone:(NSZone *)zone forInstance:(id*)inst {
    @synchronized(self) {
        if (*inst == nil) {
            *inst = [super allocWithZone:zone];
            return *inst;  // assignment and return on first allocation
        }
    }
    return nil; // on subsequent allocation attempts return nil
}

- (id)copyWithZone:(NSZone *)zone {
    return self;
}

- (id)retain {
    return self;
}

- (unsigned)retainCount {
    return UINT_MAX;  // denotes an object that cannot be released
}

- (void)release {
    //do nothing
}

- (id)autorelease {
    return self;
}


@end

And here is an example of some class, that you want to become singleton.

#import "Singleton.h"

@interface SomeClass : Singleton {

}

@end

@implementation SomeClass 

DEFINE_SHARED_INSTANCE;

@end

The only limitation about Singleton class, is that it is NSObject subclass. But most time I use singletons in my code they are in fact NSObject subclasses, so this class really ease my life and make code cleaner.

answered
Nov 30 ’10 at 10:00

obscenum
5113

You might want to use some other locking mechanism because @synchronized is horribly slow and should be avoided. – DarkDust Apr 17 ’12 at 8:04

feedback

Short answer: Fabulous.

Long answer: Something like….

static SomeSingleton *instance = NULL;

@implementation SomeSingleton

+ (id) instance {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (instance == NULL){
            instance = [[super allocWithZone:NULL] init];
        }
    });
    return instance;
}

+ (id) allocWithZone:(NSZone *)paramZone {
    return [[self instance] retain];
}

- (id) copyWithZone:(NSZone *)paramZone {
    return self;
}

- (id) autorelease {
    return self;
}

- (NSUInteger) retainCount {
    return NSUIntegerMax;
}

- (id) retain {
    return self;
}

@end

Be sure to read the dispatch/once.h header to understand what’s going on. In this case the header comments are more applicable than the docs or man page.

answered
Feb 8 ’12 at 3:12

quellish
1,06439

feedback

How about

static MyClass *gInstance = NULL;

+ (MyClass *)instance
{
    if (gInstance == NULL) {
        @synchronized(self)
        {
            if (gInstance == NULL)
                gInstance = [[self alloc] init];
        }
    }

    return(gInstance);
}

So you avoid the synchronization cost after initialization?

answered
Dec 29 ’11 at 18:32

Tony
1,337316

feedback

This works in a non-garbage collected environment also.

@interface MySingleton : NSObject {
}

+(MySingleton *)sharedManager;

@end


@implementation MySingleton

static MySingleton *sharedMySingleton = nil;

+(MySingleton*)sharedManager {
    @synchronized(self) {
        if (sharedMySingleton == nil) {
            [[self alloc] init]; // assignment not done here
        }
    }
    return sharedMySingleton;
}


+(id)allocWithZone:(NSZone *)zone {
    @synchronized(self) {
        if (sharedMySingleton == nil) {
            sharedMySingleton = [super allocWithZone:zone];
            return sharedMySingleton;  // assignment and return on first allocation
        }
    }
    return nil; //on subsequent allocation attempts return nil
}


-(void)dealloc {
    [super dealloc];
}

-(id)copyWithZone:(NSZone *)zone {
    return self;
}


-(id)retain {
    return self;
}


-(unsigned)retainCount {
    return UINT_MAX;  //denotes an object that cannot be release
}


-(void)release {
    //do nothing    
}


-(id)autorelease {
    return self;    
}


-(id)init {
    self = [super init];
    sharedMySingleton = self;

    //initialize here

    return self;
}

@end

Dana
7,86663759

answered
Sep 28 ’08 at 4:58

lajos
9,23463659

feedback

You don’t want to synchronize on self… Since the self object doesn’t exist yet! You end up locking on a temporary id value. You want to ensure that no one else can run class methods ( sharedInstance, alloc, allocWithZone:, etc ), so you need to synchronize on the class object instead:

@implementation MYSingleton

static MYSingleton * sharedInstance = nil;

+( id )sharedInstance {
    @synchronized( [ MYSingleton class ] ) {
        if( sharedInstance == nil )
            sharedInstance = [ [ MYSingleton alloc ] init ];
    }

    return sharedInstance;
}

+( id )allocWithZone:( NSZone * )zone {
    @synchronized( [ MYSingleton class ] ) {
        if( sharedInstance == nil )
            sharedInstance = [ super allocWithZone:zone ];
    }

    return sharedInstance;
}

-( id )init {
    @synchronized( [ MYSingleton class ] ) {
        self = [ super init ];
        if( self != nil ) {
            // Insert initialization code here
        }

        return self;
    }
}

@end

community wiki

1  

The rest of the methods, accessor methods, mutator methods, etc should synchronize on self. All class(+) methods and initializers (and probably -dealloc) should synchronize on the class object. You can avoid having to manually sync if you use Objective-C 2.0 properties instead of accessor/mutator methods. All object.property and object.property = foo, are automatically synchronized to self. – Rob Dotson Jan 13 ’10 at 22:10

2  

Please explain why you think that the self object doesn’t exist in a class method. The runtime determines which method implementation to invoke based on the exact same value that it provides as self to every method (class or instance). – dreamlax Feb 19 ’10 at 6:53

Inside of a class method, self is the class object. Try it yourself: #import <Foundation/Foundation.h>

@interface Eggbert : NSObject
+ (BOOL) selfIsClassObject;
@end
@implementation Eggbert

+ (BOOL) selfIsClassObject {
return self == [Eggbert class];
}
@end

int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

NSLog(@"%@", [Eggbert selfIsClassObject] ? @"YES" : @"NO");

[pool drain];
return 0;
}
– Josh Caswell Sep 27 ’11 at 2:23

feedback

Shouln’t this be threadsafe and avoid the expensive locking after the first call?

+ (MySingleton*)sharedInstance
{
    if (sharedInstance == nil) {
        @synchronized(self) {
            if (sharedInstance == nil) {
                sharedInstance = [[MySingleton alloc] init];
            }
        }
    }
    return (MySingleton *)sharedInstance;
}
answered
Mar 17 ’10 at 11:13

Jompe
211

2  

The double-checked locking technique used here is often a real problem in some environments (see aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf or Google it). Until shown otherwise, I’d assume that Objective-C isn’t immune. Also see wincent.com/a/knowledge-base/archives/2006/01/…. – Steve Madsen Apr 22 ’10 at 15:04

feedback

Here’s a macro that I put together:

http://github.com/cjhanson/Objective-C-Optimized-Singleton

It is based on the work here by Matt Gallagher
But changing the implementation to use method swizzling as described here by Dave MacLachlan of Google.

I welcome comments / contributions.

answered
Mar 2 ’10 at 6:26

CJ Hanson
34639

the link seems broken – where can I get that source? – amok Aug 29 ’10 at 21:09

feedback

For an in-depth discussion of the singleton pattern in Objective-C, look here:

Using the Singleton Pattern in Objective-C

user1873471

answered
May 21 ’10 at 22:39

feedback

Just wanted to leave this here so I don’t lose it. The advantage to this one is that it’s usable in InterfaceBuilder, which is a HUGE advantage. This is taken from another question that I asked:

static Server *instance;

+ (Server *)instance { return instance; }

+ (id)hiddenAlloc
{
    return [super alloc];
}

+ (id)alloc
{
    return [[self instance] retain];
}


+ (void)initialize
{
    static BOOL initialized = NO;
    if(!initialized)
    {
        initialized = YES;
        instance = [[Server hiddenAlloc] init];
    }
}

- (id) init
{
    if (instance)
        return self;
    self = [super init];
    if (self != nil) {
        // whatever
    }
    return self;
}
answered
Jan 16 ’11 at 23:29

Yar
18.6k14106194

feedback

static mySingleton *obj=nil;

@implementation mySingleton

-(id) init {
    if(obj != nil){     
        [self release];
        return obj;
    } else if(self = [super init]) {
        obj = self;
    }   
    return obj;
}

+(mySingleton*) getSharedInstance {
    @synchronized(self){
        if(obj == nil) {
            obj = [[mySingleton alloc] init];
        }
    }
    return obj;
}

- (id)retain {
    return self;
}

- (id)copy {
    return self;
}

- (unsigned)retainCount {
    return UINT_MAX;  // denotes an object that cannot be released
}

- (void)release {
    if(obj != self){
        [super release];
    }
    //do nothing
}

- (id)autorelease {
    return self;
}

-(void) dealloc {
    [super dealloc];
}
@end

binnyb
6,24813052

answered
Jun 18 ’10 at 10:22

feedback

I know there are a lot of comments on this "question", but I don’t see many people suggesting using a macro to define the singleton. It’s such a common pattern and a macro greatly simplifies the singleton.

Here are the macros I wrote based on several Objc implementations I’ve seen.

Singeton.h

/**
 @abstract  Helps define the interface of a singleton.
 @param  TYPE  The type of this singleton.
 @param  NAME  The name of the singleton accessor.  Must match the name used in the implementation.
 @discussion
 Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class.
 */
#define SingletonInterface(TYPE, NAME) \
+ (TYPE *)NAME;


/**
 @abstract  Helps define the implementation of a singleton.
 @param  TYPE  The type of this singleton.
 @param  NAME  The name of the singleton accessor.  Must match the name used in the interface.
 @discussion
 Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class.
 */
#define SingletonImplementation(TYPE, NAME) \
static TYPE *__ ## NAME; \
\
\
+ (void)initialize \
{ \
    static BOOL initialized = NO; \
    if(!initialized) \
    { \
        initialized = YES; \
        __ ## NAME = [[TYPE alloc] init]; \
    } \
} \
\
\
+ (TYPE *)NAME \
{ \
    return __ ## NAME; \
}

Example of use:

MyManager.h

@interface MyManager

SingletonInterface(MyManager, sharedManager);

// ...

@end

MyManager.m

@implementation MyManager

- (id)init
{
    self = [super init];
    if (self) {
        // Initialization code here.
    }

    return self;
}

SingletonImplementation(MyManager, sharedManager);

// ...

@end

Why a interface macro when it’s nearly empty? Code consistency between the header and code files; maintainability in case you want to add more automatic methods or change it around.

I’m using the initialize method to create the singleton as is used in the most popular answer here (at time of writing).

answered
Nov 22 ’11 at 20:00

Nate
10119

feedback

With Objective C class methods, we can just avoid using the singleton pattern the usual way, from:

[[Librarian sharedInstance] openLibrary]

to:

[Librarian openLibrary]

by wrapping the class inside another class that just has Class Methods, that way there is no chance of accidentally creating duplicate instances, as we’re not creating any instance!

I wrote a more detailed blog here 🙂

answered
Feb 10 ’12 at 22:35

sid
682512

feedback

To extend the example from @robbie-hanson …

static MySingleton* sharedSingleton = nil;

+ (void)initialize {
    static BOOL initialized = NO;
    if (!initialized) {
        initialized = YES;
        sharedSingleton = [[self alloc] init];
    }
}

- (id)init {
    self = [super init];
    if (self) {
        // Member initialization here.
    }
    return self;
}
answered
Mar 22 ’12 at 16:52

JJD
2,3841543

feedback

My way is simple like this:

static id instanceOfXXX = nil;

+ (id) sharedXXX
{
    static volatile BOOL initialized = NO;

    if (!initialized)
    {
        @synchronized([XXX class])
        {
            if (!initialized)
            {
                instanceOfXXX = [[XXX alloc] init];
                initialized = YES;
            }
        }
    }

    return instanceOfXXX;
}

If the singleton is initialized already, the LOCK block will not be entered. The second check if(!initialized) is to make sure it is not initialized yet when the current thread acquires the LOCK.

answered
Dec 23 ’12 at 22:01

TienDC
11

feedback

KLSingleton is:

  1. Subclassible (to the n-th degree)
  2. ARC compatible
  3. Safe with alloc and init
  4. Thread-safe
  5. Lock-free (uses +initialize, not @synchronize)
  6. Macro-free
  7. Swizzle-free
  8. Simple

This implementation chooses the singleton philosophy of "not lazy"
instead of "requires explicit initialization" or "locks to allocate".

KLSingleton

user1873471

answered
May 3 ’12 at 21:50

kevinlawler
347210

I’m using your NSSingleton for my project, and It seems to be incompatible with KVO. The matter is that KVO creates subclass for every KVO object with prefixing it NSKVONotifying_*MyClass*. And it makes MyClass +initialize and -init methods to be called twice. – Oleg Trakhman Jul 3 ’12 at 18:28
I tested this on the latest Xcode and didn’t have any trouble registering for or receiving KVO events. You can verify this with the following code: gist.github.com/3065038 As I mentioned on Twitter, the +initialize methods are called once for NSSingleton and once for each subclass. This is a property of Objective-C. – kevinlawler Jul 7 ’12 at 6:19
If you add NSLog(@"initialize: %@", NSStringFromClass([self class])); to the +initialize method you can verify that the classes are initialized only once. – kevinlawler Jul 7 ’12 at 6:24
NSLog(@"initialize: %@", NSStringFromClass([self class])); – Oleg Trakhman Jul 7 ’12 at 10:24
You might want to also have it be IB compatible. Mine is: stackoverflow.com/questions/4609609/… – Yar Sep 5 ’12 at 19:55

feedback

I usually use code roughly similar to that in Ben Hoffstein’s answer (which I also got out of Wikipedia). I use it for the reasons stated by Chris Hanson in his comment.

However, sometimes I have a need to place a singleton into a NIB, and in that case I use the following:

@implementation Singleton

static Singleton *singleton = nil;

- (id)init {
    static BOOL initialized = NO;
    if (!initialized) {
        self = [super init];
        singleton = self;
        initialized = YES;
    }
    return self;
}

+ (id)allocWithZone:(NSZone*)zone {
    @synchronized (self) {
        if (!singleton)
            singleton = [super allocWithZone:zone]; 	
    }
    return singleton;
}

+ (Singleton*)sharedSingleton {
    if (!singleton)
        [[Singleton alloc] init];
    return singleton;
}

@end

I leave the implementation of -retain (etc.) to the reader, although the above code is all you need in a garbage collected environment.

answered
Jun 24 ’09 at 4:53

Gregory Higley
3,39221935

2  

Your code is not thread-safe. It uses synchronized in the alloc method, but not in the init method. Checking on the initialized bool is not thread-safe. – Mecki Jun 29 ’09 at 12:44

feedback

The accepted answer, although it compiles, is incorrect.

+ (MySingleton*)sharedInstance
{
    @synchronized(self)  <-------- self does not exist at class scope
    {
        if (sharedInstance == nil)
            sharedInstance = [[MySingleton alloc] init];
    }
    return sharedInstance;
}

Per Apple documentation:

… You can take a similar approach to synchronize the class methods of the associated class, using the Class object instead of self.

Even if using self works, it shouldn’t and this looks like a copy and paste mistake to me.
The correct implementation for a class factory method would be:

+ (MySingleton*)getInstance
{
    @synchronized([MySingleton class]) 
    {
        if (sharedInstance == nil)
            sharedInstance = [[MySingleton alloc] init];
    }
    return sharedInstance;
}
answered
Jan 23 ’11 at 23:23

deleted_user
2,4941516

6  

self most certainly does exist it class scope. It refers to the class instead of the instance of the class. Classes are (mostly) first class objects. – schwa Jan 25 ’11 at 18:30

Why do you put @synchroninzed WITHIN a method? – Jim Thio May 4 ’11 at 2:06

1  

As schwa already said, self is the class object inside of a class method. See my comment for a snippet demonstrating this. – Josh Caswell Sep 27 ’11 at 2:27

self exists, but using it as the identifier passed to @synchronized will synchronize access to the methods of the instance. As @user490696 points out, there are cases (like singletons) where using the class object is preferable. From The Obj-C Programming Guide: You can take a similar approach to synchronize the class methods of the associated class, using the class object instead of self. In the latter case, of course, only one thread at a time is allowed to execute a class method because there is only one class object that is shared by all callers. – quellish Feb 8 ’12 at 19:50

feedback

Your Answer

 

log in

or
Name

Email

Home Page

By posting your answer, you agree to the
privacy policy and
terms of service.

Not the answer you’re looking for?
Browse other questions tagged
or ask your own question.

Unit Testing with Cocos2d and Xcode 4  | prosoxi.com

Unit Testing with Cocos2d and Xcode 4

Adding unit testing to my Cocos2d project took longer than I expected. In hindsight it was because I didn’t really understand how targets worked in Xcode. After stumbling around for a while, I figured out how to get this working. Here’s the steps I used. If you know a better/faster way, please leave a comment below.

If you already know Xcode well, this post probably isn’t for you.

Adding Unit Testing to an Existing XCode Project

Xcode offers a way to create tests with a new project, but the Cocos2d template does not. The first step is to create the test target.

1) Click on your project name in the left Project Navigator
2) Click Add Target on the bottom of your screen
3) Select Cocoa Touch Unit Testing Bundle. Click Next
4) Name your test whatever you’d like (ex: UnitTests). Click Finish

Now you’ve got tests added to your project. You can select your test name in the Scheme drop down menu. In the menu bar, click Product -> Test. You should receive the following error:

error: testExample (UnitTests) failed: Unit tests are not implemented yet in UnitTests

Success! This is what’s supposed to happen, because the only test you currently have is:

- (void)testExample { STFail(@"Unit tests are not implemented yet in UnitTests"); }

Testing Our Own Code

Now we want to actually test our code. Add the following to the top of your UnitTest.m file (or whatever you called your test file):

#import "cocos2d.h"

Add this method somewhere below:

- (void)testNode { CCNode *node = [[CCNode alloc] init]; STAssertNotNil(node, @"Node shouldn't be nil"); }

Disclamer: It doesn’t really make sense to test this. But since we have an empty project, we’re just trying to get the error to appear as an example. Using CCNode will trigger this error. In your project, you’d want to test things like calculations, transformations, etc..

Run Product -> Test to see the build failure:

Undefined symbols for architecture i386: “_OBJC_CLASS_$_CCNode”, referenced from: objc-class-ref in UnitTests.o

This is a link error, and is happening because our UnitTest target doesn’t know anything about our project. Usually when you create a new Xcode project, you create UnitTests from the beginning. When you add files to your project, you can select which targets the files belong to. Because we’re adding our testing after the fact, we have to add these manually.

Adding Compile Sources and Libraries

1) Click on your project name in the left Project Navigator
2) Click on your project target. This is not your testing target. It’s probably the one above it. Then click Build Phases
3) Expand the Compile Sources dropdown.
4) Select everything in the list
5) Right click. Click Reveal in Project Navigator
6) With the items in the Project Navigator still selected, click your testing target in the left Project Navigator
7) Click on Build Phases.
8) Drag and drop the selected files in the Project Navigator to the testing target’s compile sources

We’re almost done. Now you just have to add the library frameworks.

In your testing target’s build phases expand the Link Binary With Libraries dropdown.

You can manually add each framework required here (use your main project target as a reference for what should be here).

A faster way I found, was:

1) Expand the Frameworks folder in your Project Navigator
2) Select all frameworks in the folder (using Shift-Click or Command-Click)
3) Drag these frameworks to your test target’s Link Binary With Libraries dropdown

One more step. If you try to run your tests now, you’ll get these errors:

 

“_inflateInit2_”, referenced from: inflateMemory in ZipUtils.o
“_inflate”, referenced from: inflateMemory in ZipUtils.o
“_inflateEnd”, referenced from: inflateMemory in ZipUtils.o
“_gzopen”, referenced from: _ccInflateGZipFile in ZipUtils.o
“_gzread”, referenced from: _ccInflateGZipFile in ZipUtils.o
“_gzclose”, referenced from: _ccInflateGZipFile in ZipUtils.o
“_uncompress”, referenced from: _ccInflateCCZFile in ZipUtils.o

 

We need to add libz.dylib to the frameworks list under the Link Binary With Libraries dropdown section.

1) Click + under Link Binary With Libraries
2) Add libz.dylib

I’m not sure why this isn’t listed with the original project target libraries, but probably has to do with more Xcode underpinnings I don’t fully understand.

Whew. Go to Product -> Test to run your tests. It should compile successfully (but still fail because of testExample). You can remove – (void)testExample if you want to see the tests pass.

Create Test Shortcut (Optional)

This part isn’t necessary, but is very nice. We can simplify running our tests by adding them to our project scheme.

1) Select the Scheme dropdown menu, then click Edit Schemes
2) Select your project name in the scheme dropdown on the top of the dialog. This is not your tests you created
3) Select Test in the left hand view
4) Click the + icon right above Manage Schemes…*
5) Select your test. Click Add. Click OK.

Now you can go to Product -> Test without switching schemes first. Even better we can press Command+U to test.

Conclusion

Hopefully if you’ve run into this problem, these steps will solve it for you. This probably already makes sense to experienced Xcode users, but as someone new to the platform, it took me longer than I’d like. Hopefully this saves you some time.

This entry was published on June 5, 2011 in iOS

Unit Testing with OCUnit – Use Your Loaf

Fully testing an iPhone App is a big topic but a good place to start is to add unit tests for the Model classes. Testing the model avoids some of the complexity of testing UI elements but still has considerable benefit. XCode is already integrated with OCUnit (aka SenTestCase) so getting setup for unit testing is fairly painless.

Unit Testing

Of course “everybody” knows that it is a good idea to test your code but in my experience adding tests to a project that is approaching completion is a painful activity. If you get into the habit of writing tests as you write the code you not only reduce the pain but you may also find it has a positive impact on the quality of the code. There is something about the discipline of thinking about how you can easily test a piece of code that encourages you to keep it simple with well defined interfaces, minimal coupling and dependencies.

I don’t intend this to be a post about Test Driven Development (TDD). I see lots of benefits to the TDD approach but writing your tests before you write your code is but one approach. If I am being honest I tend to iterate between writing some code and/or writing the tests for that piece of code first. Either way can work but I guess the important thing is to do it and do it early.

Most iPhone applications follow the Model View Controller (MVC) pattern that is also common to Mac Cocoa and Ruby on Rails applications. I find an easy way to get started if you are not yet convinced about testing an iPhone App is to focus first on the model. Testing the model is a lot easier than testing User Interface elements and can also be done in the Simulator. The steps to get your Xcode project setup for unit testing are really easy so I would encourage you to always add a Unit Testing target when you first create a project.

I should also clarify that when I talk about Unit Tests I tend to mean tests that can be run in isolation of the full application. Apple refer to these as Logic Tests in their documentation. In an MVC application this most often means testing the Model as the Controller and View tests normally require a fully running application. I refer to the full application tests as Integration Tests for obvious reasons and will try to cover those in a future post.

The Sample Project

So having said that you should build your tests as you develop your application I will now completely ignore that advice by adding some unit tests to an existing project. The main point of this post is really to show you how easy Xcode makes it to get started with unit testing rather than to demonstrate TDD. I will start with the sample Apple project called TheElements as it provides a well structured Model-View-Controller application and look at how we can apply some unit tests to its model classes.

Setting up the project for Unit Testing

The first step in adding unit tests to the Xcode project is to create a new target that is used to build and run the unit tests. Right-click on the Targets in the Groups & Files section of the project window and select Add -> New Target… to bring up the template dialog window and select the Unit Test Bundle.

Name the new target UnitTests and add a group with the same name to the Groups & Files list which we will use to hold our unit test code. The basic idea is now to add a unit test class for each model class in our project. Each unit test we add will be responsible for unit testing its corresponding model class.

Adding a Unit Test Class

Our example project contains a model class named AtomicElement so with UnitTests set as the active target add a new unit test class named AtomicElementTests that will contain our test cases for this class. Xcode contains a ready made template for Objective-C test classes that we can use for this:

It is important to ensure that this file is only added for the UnitTests target and not for the main application target (named TheElements in this case).

Also since we want to test the AtomicElement class we need to add it to our UnitTests target. Right-click on the class file AtomicElement.m and select Get Info and then on the Targets tab ensure that the UnitTests target is selected in addition to the normal application target:

Note: If you are following along there is a modification that you need to make to AtomicElements.h to avoid some build errors. You need to import the standard UIKit header file. This is normally imported by the pre-compiled header file TheElements_Prefix.pch when building the standard app. Since our UnitTests target does not use this pre-compiled header file we will import it directly by adding the following line to AtomicElement.h

#import <UIKit/UIKit.h>

If all has gone well your Xcode project should now have the following additions for the UnitTest target:

A First Test Case

The default template unit test classes include some conditional code to allow you to write both unit and integrations tests. Since we are only interested in writing unit tests for our models we can simplify the code. The header file for AtomicElementTests.h looks like the following:

//  AtomicElementTests.h
#import <SenTestingKit/SenTestingKit.h>
#import <UIKit/UIKit.h>

@interface AtomicElementTests : SenTestCase {
  NSArray *testData;
}

@property (nonatomic, retain) NSArray *testData;

@end

I will explain the testData member variable in a moment but first it is worth understanding the basic structure of a test. As I already mentioned the basic strategy is to add a test class for each of our model classes that will contain the test cases for that model. Each of these test classes inherits from the SenTestCase base class.

To add a unit test case we simply add an instance method to this class. The only constraint is that the instance method name should start with “test” and have a return type of void. When we build and run our UnitTests target all of these test methods are run automatically for us.

We will make our first test case very simple to demonstrate the basic idea. As a first test case we will validate that we can successfully allocate and initialise an AtomicElement object. Our model contains an initializer named initWithDictionary that we will test. However this method expects a dictionary containing values that are used to initialise the object.

The ability to generate test data is one of the challenges of writing unit tests. The SenTest framework that Xcode uses makes it easy to manage this test data once for all the test cases in the class through the setUp and tearDown methods. So to allocate our test data we add the following method to our class:

- (void)setUp {
    // read in the test data
    NSBundle *thisBundle = [NSBundle bundleForClass:[self class]];
    NSString *testFile = [thisBundle  pathForResource:@"Elements"
                                               ofType:@"plist"];
    self.testData = [[NSArray alloc] initWithContentsOfFile:testFile];
}

This relies on the fact that the project already has a plist file (which we need to add to the UnitTests target) containing data for the elements that we can reuse as test data. Note that the plist file would normally be loaded from the mainBundle for iPhone OS applications but for our UnitTest target this assumption does not apply so we get the bundle name from the class.

To release the test data once the unit tests have completed we also add a tearDown method as follows:

- (void) tearDown {
  [testData release];
}

The setUp method will always be called before our test methods run and the tearDown method will always be called after all of our test methods have finished. Now we can add our first real unit test that will simply create a new object and test that it worked:

- (void) testCreationAtomicElement {
  NSDictionary *testItem = nil;

  // iterate over the values in the raw elements dictionary
  for (testItem in testData)
  {
    // create an atomic element instance for each
    AtomicElement *element = [[AtomicElement alloc] initWithDictionary:testItem];

    // Did we get back a valid element?
    STAssertNotNil(element, @"Unable to allocate Atomic Element");

    // Check the atomic number
    NSInteger expectedNumber = [element.atomicNumber integerValue];
    NSInteger actualNumber = [[testItem valueForKey:@"atomicNumber"] integerValue];
    STAssertEquals(expectedNumber, actualNumber,
                       @"atomicNumber should be %d but got %d",
                       expectedNumber, actualNumber);

    // Test the other member variables...
    // ...

    [element release];
  }
}

To run the test we just need to build the UnitTests target. If the tests are successful the build will complete with no issues. Some comments about this test case:

  • the test cycles though our test data, allocating a new object each time and then validating that the new object contains the values that we expect from out test data.
  • the STAssertNotNil macro is used to test that an object was allocated.
  • the STAssertEquals macro is used to test that a member variable has the value that we expect
  • to keep the example brief we only test for a single member variable (atomicNumber)

As I was looking at the AtomicElement class whilst writing the above test it became obvious that there is really no validation in the initializer method. Of course, this is only example code from Apple but what happens if we attempt to create a new object but do not pass a valid dictionary to the initializer?

Making the Test Pass

We are starting to adopt some of the workflow from Test Driven Development. We will add a test where we attempt to create a new AtomicElement object with an empty dictionary:

- (void)testEmptyDictionary {

  AtomicElement *element = [[AtomicElement alloc] initWithDictionary:nil];
  STAssertNil(element, @"AtomicElement creation should fail");
}

Now when we build our UnitTests we get a failure as our model does no checking and blindly allocates an empty object:

We have defined how our code should behave in the unit test and since our model does not currently check if it is passed a valid dictionary for initialisation our tests fails. To fix this we need to change our model to validate the dictionary parameter:

- (id)initWithDictionary:(NSDictionary *)aDictionary {

  if (aDictionary == nil) {
    [self release];
    return nil;
  }

Now when we build the UnitTests target our tests work and we have made a small but important improvement to the model. We can continue this stepwise refinement by testing other edge cases and error conditions but hopefully you already get the idea. The iPhone Development Guide contains a good list of the STAssert macros that you can use in your test cases.

Summing Up

This has turned into a long post but I hope if you have never tried unit testing your iPhone application that I have persuaded you to at least give it a try. By starting with your model classes the learning curve is fairly gentle. Testing the model may only test a fraction of your code but it gives you a firm foundation on which to build. Also the skills that you learn will be a big help when it comes to testing the controller and view code – a topic I hope to cover in a future post.