OS中atomic的实现解析

OS中atomic的实现解析

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

 

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

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

那些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操做,代码以下:ui

?
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函数以下:atom

?
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确定有区别。spa

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

?
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);
}
相关文章
相关标签/搜索