OS中atomic的实现解析

转自:http://my.oschina.net/majiage/blog/267409 
 
摘要 atomic属性线程安全,会增加一定开销,但有些时候必须自定义atomic。这时候,我们就需要知道atomic的实现原理及方法了。这篇文章主要就是讲解自定义atomic的实现。

atomic原子性与non-atomic非原子性

iOS中有两个属性non-atomic和atomic,前者是非原子性的(线程不安全),后者是原子性的(线程安全),一般情况下不会去重写它们,但某些时候确实有重写的需求。

那些int、float之类的类型,你重写想出错都很难。但是强引用类型(retain)就需要注意了。

简单的说一下非原子性的nonatomic实现,方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)setCurrentImage:(UIImage *)currentImage
{
    if (_currentImage != currentImage) {
        [_currentImage release];
        _currentImage = [currentImage retain];
             
        // do something
    }
}
- (UIImage *)currentImage
{
    return _currentImage;
}

atomic实现:

关于atomic的实现最开始的方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- (void)setCurrentImage:(UIImage *)currentImage
{
    @synchronized(self) {
    if (_currentImage != currentImage) {
        [_currentImage release];
        _currentImage = [currentImage retain];
                 
        // do something
        }
    }
}
 
- (UIImage *)currentImage
{
    @synchronized(self) {
        return _currentImage;
    }
}

具体讲就是retain的同步版本,本来以为没问题,但在用GCD重绘currentImage的过程中,有时候currentImage切换太频繁。在完成之前就把之前的currentImage释放了,程序仍然会崩溃。还需要在resize过程中增加retain和release操作,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
- (UIImage *)resizedImage:(CGSize)newSize interpolationQuality:(CGInterpolationQuality)quality {
    // For multithreading
    [self retain];
         
    BOOL drawTransposed;
    CGAffineTransform transform = CGAffineTransformIdentity;
         
    // In iOS 5 the image is already correctly rotated. See Eran Sandler's
    // addition here: http://eran.sandler.co.il/2011/11/07/uiimage-in-ios-5-orientation-and-resize/
         
    if([[[UIDevice currentDevice]systemVersion]floatValue] >= 5.0) {
        drawTransposed = NO;
    else {
    switch(self.imageOrientation) {
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            drawTransposed = YES;
            break;
        default:
            drawTransposed = NO;
    }
             
    transform = [self transformForOrientation:newSize];
    }
    transform = [self transformForOrientation:newSize];
         
    UIImage *image = [self resizedImage:newSize transform:transform drawTransposed:drawTransposed interpolationQuality:quality];
    [self release];
    return image;
}

原始版本的resize函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
- (UIImage *)resizedImage:(CGSize)newSize interpolationQuality:(CGInterpolationQuality)quality {
    BOOL drawTransposed;
    CGAffineTransform transform = CGAffineTransformIdentity;
        
    // In iOS 5 the image is already correctly rotated. See Eran Sandler's
    // addition here: http://eran.sandler.co.il/2011/11/07/uiimage-in-ios-5-orientation-and-resize/
         
    if([[[UIDevice currentDevice]systemVersion]floatValue] >= 5.0) {
        drawTransposed = NO;
    else {
        switch(self.imageOrientation) {
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            drawTransposed = YES;
            break;
        default:
            drawTransposed = NO;
        }
             
    transform = [self transformForOrientation:newSize];
    }
    transform = [self transformForOrientation:newSize];
         
    return [self resizedImage:newSize transform:transform drawTransposed:drawTransposed interpolationQuality:quality];
}

但是之前在没有重写getter之前,用atomic的getter程序不会崩溃。于是我就想现在的getter和atomic自己实现的getter肯定有区别。

最后,答案出现:在getter的return之前retain,再autorelease一次就可以了。getter函数就变成了这样:

1
2
3
4
5
6
7
- (UIImage *)currentImage
{
    @synchronized(self) {
        UIImage *image = [_currentImage retain];
        return [image autorelease];
    }
}

这样可以确保currentImage在调用过程中不会因为currentImage被释放或者改变,使它的retainCount次数变为0,再在调用时让程序直接崩溃。

Runtime方法

Memory and thread-safe custom property methods这篇文章中还提到了一种Objective-C的runtime解决方案。

Objective-C的runtime中实现了以下函数:

1
2
3
id <strong>objc_getProperty</strong>(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic);
void <strong>objc_setProperty</strong>(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy);
void <strong>objc_copyStruct</strong>(void *dest, const void *src, ptrdiff_t size, BOOL atomic, BOOL hasStrong);

这几个函数被实现了,但没有被声名。如果要使用他们,必须自己声名。它们比用@synchronized实现的要快。因为它的实现方式与一般情况不同,静态变量只在接收并发时才会锁住。

声名方式:

1
2
3
4
5
6
7
8
#define <strong>AtomicRetainedSetToFrom</strong>(dest, source) \
        objc_setProperty(self, _cmd, (ptrdiff_t)(&dest) - (ptrdiff_t)(self), source, YES, NO)
#define <strong>AtomicCopiedSetToFrom</strong>(dest, source) \
        objc_setProperty(self, _cmd, (ptrdiff_t)(&dest) - (ptrdiff_t)(self), source, YES, YES)
#define <strong>AtomicAutoreleasedGet</strong>(source) \
        objc_getProperty(self, _cmd, (ptrdiff_t)(&source) - (ptrdiff_t)(self), YES)
#define <strong>AtomicStructToFrom</strong>(dest, source) \
        objc_copyStruct(&dest, &source, sizeof(__typeof__(source)), YES, NO)

用这些宏定义,上面something的copy getter和setter方法将变成这样:

1
2
3
4
5
6
7
8
- (NSString *)someString
{
    return AtomicAutoreleasedGet(someString);
}
- (void)setSomeString:(NSString *)aString
{
    AtomicCopiedSetToFrom(someString, aString);
}

someRect存取方法将变成这样:

1
2
3
4
5
6
7
8
9
10
- (NSRect)someRect
{
    NSRect result;
    AtomicStructToFrom(result, someRect);
    return result;
}
- (void)setSomeRect:(NSRect)aRect
{
    AtomicStructToFrom(someRect, aRect);
}

OS中atomic的实现解析的更多相关文章

  1. .NET Core中的认证管理解析

    .NET Core中的认证管理解析 0x00 问题来源 在新建.NET Core的Web项目时选择“使用个人用户账户”就可以创建一个带有用户和权限管理的项目,已经准备好了用户注册.登录等很多页面,也可 ...

  2. 让 ASP.NET vNext 在 Mac OS 中飞呀飞。。。

    写在前面 阅读目录: 娓娓道来 Install ASP.NET vNext Command Line Tools 安装 Homebrew 使用 Homebrew,安装 KVM Install Subl ...

  3. AngularJS中的指令全面解析(转载)

    说到AngularJS,我们首先想到的大概也就是双向数据绑定和指令系统了,这两者也是AngularJS中最为吸引人的地方.双向数据绑定呢,感觉没什么好说的,那么今天我们就来简单的讨论下AngularJ ...

  4. Java中的static关键字解析

    Java中的static关键字解析 static关键字是很多朋友在编写代码和阅读代码时碰到的比较难以理解的一个关键字,也是各大公司的面试官喜欢在面试时问到的知识点之一.下面就先讲述一下static关键 ...

  5. Java中的static关键字解析 转载

    原文链接:http://www.cnblogs.com/dolphin0520/p/3799052.html Java中的static关键字解析 static关键字是很多朋友在编写代码和阅读代码时碰到 ...

  6. [JavaScript] JS中对Base64的解析

    JS中对Base64的解析 <script type="text/javascript"> /** * UTF16和UTF8转换对照表 * U+00000000 – U ...

  7. Oracle中Clob类型处理解析:ORA-01461:仅可以插入LONG列的LONG值赋值

    感谢原作者:破剑冰-Oracle中Clob类型处理解析 上一篇分析:ORA-01461: 仅能绑定要插入 LONG 列的 LONG 值 最近为Clob字段在插入数据时发现当字符的字节数(一个半角字符一 ...

  8. Java中的static关键字解析(转自海子)__为什么main方法必须是static的,因为程序在执行main方法的时候没有创建任何对象,因此只有通过类名来访问。

    Java中的static关键字解析 static关键字是很多朋友在编写代码和阅读代码时碰到的比较难以理解的一个关键字,也是各大公司的面试官喜欢在面试时问到的知识点之一.下面就先讲述一下static关键 ...

  9. 【Chromium中文文档】Chrom{e,ium}{,OS}中的硬件视频加速

    Chrom{e,ium}{,OS}中的硬件视频加速 转载请注明出处:https://ahangchen.gitbooks.io/chromium_doc_zh/content/zh//General_ ...

随机推荐

  1. 60阶单群同构于A5的证明

    设$G$是$60$阶的单群,我们来证明他同构于$A_5$,一个比较直观地思路是考虑群表示$\phi:G\to S(\Sigma)$,由同态基本定理得到$$G/{\rm Ker}\phi \simeq ...

  2. CentOS7 下ifconfig command not found解决办法

    今天尝鲜用VMWare安装了CentOS7,选择了最小安装包模式,安装完毕之后想查看一下本机的ip地址,发现报错 # ifcon -bash: ifconfig: command not found ...

  3. socket 收发报文小程序

    需要注意,由于是从文件读取,而WINDOWS自带的文本文档程序,亲测对于UTF-8文件会不可见地在文件头多出3个字节.另外回车换行也是不可见字节,需要考虑到. package com.test.com ...

  4. SQL 谜题(硬币的组合)

    问题:早在ITPUB中看过有个SQL高手,喜欢出谜题,以下是一个谜题.我试用SQL SERVER解决此问题. 用1分,5分,10分,25分,50分硬币凑成一元,总共有几种组合办法? SELECT'1* ...

  5. vue-cli

    vue-cli 脚手架   vue-loader 作用:提供基本项目结构 本身集成了很多项目模板:simple,webpack ,webpack-simple; simple:几乎没什么用: webp ...

  6. JAVA 引入 junit工具框架

    我遇到的麻烦 : 开始直接按照视频上的来做,直接也是引入的他上面的jar ,但是我只引入了一个,就是上面的junit-4.4.jar,然后就会报错,会出现,空指针的错误, 后面我又按照网上的教程 这里 ...

  7. NetBeans连接SQL server数据库教程

    不废话,直接开始 1.下载sqljdbc.jar 可以从微软中国官方网站下载 SQLJDBC微软中国 笔者提供一个网盘链接Sqljdbc.jar 4个压缩包视版本选择,SQL 2012 用sqljdb ...

  8. C# 解析JSON的几种办法

    欲成为海洋大师,必知晓海中每一滴水的真名. 刚开始只是想找一个转换JSON数组的方法,结果在MSDN翻到一大把. 搜索过程中免不了碰到一大堆名词:WCF => DataContract => ...

  9. Lambda表达式详解

    前言 1.天真热,程序员活着不易,星期天,也要顶着火辣辣的太阳,总结这些东西. 2.夸夸lambda吧:简化了匿名委托的使用,让你让代码更加简洁,优雅.据说它是微软自c#1.0后新增的最重要的功能之一 ...

  10. PB12.5.2安装

    一.从12.5升级到12.5.2_build5550安装步骤: 1.安装VS2010及SP1 2.安装12.50(可以只装PB)DV68538-65-1250-01.zip 3.安装EBF20963 ...