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);
}
相關文章
相關標籤/搜索