目錄[-]html
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的實現最開始的方式以下:函數
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,再在調用時讓程序直接崩潰。線程
在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);
}
|