本文授權轉載,作者:吳白
1.關於關鍵字volatile
一個定義為volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。精確地說就是,優化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在寄存器裡的備份。就像大家更熟悉的const一樣,volatile是一個類型修飾符。它是被設計用來修飾被不同線程訪問和修改的變量。如果不加入volatile,基本上會導致這樣的結果:要麼無法編寫多線程程序,要麼編譯器失去大量優化的機會。
Volatile變量具有 synchronized 的可見性特性,但是不具備原子特性。這就是說線程能夠自動發現 volatile變量的最新值。Volatile變量可用於提供線程安全,但是只能應用於非常有限的一組用例:多個變量之間或者某個變量的當前值與修改後值之間沒有約束。因此,單獨使用 volatile 還不足以實現計數器、互斥鎖或任何具有與多個變量相關的不變式(Invariants)的類(例如 “start <=end”)。
出於簡易性或可伸縮性的考慮,您可能傾向於使用 volatile變量而不是鎖。當使用 volatile變量而非鎖時,某些習慣用法更加易於編碼和閱讀。此外,volatile變量不會像鎖那樣造成線程阻塞,因此也很少造成可伸縮性問題。在某些情況下,如果讀操作遠遠大於寫操作,volatile變量還可以提供優於鎖的性能優勢。
代碼示例
volatile int i=10; int j = i; ... int k = i;
volatile 告訴編譯器i是隨時可能發生變化的,每次使用它的時候必須從i的地址中讀取,因而編譯器生成的可執行碼會重新從i的地址讀取數據放在k中。編譯器在產生release版可執行碼時會進行編譯優化,加volatile關鍵字的變量有關的運算,將不進行編譯優化。而優化做法是,由於編譯器發現兩次從i讀數據的代碼之間的代碼沒有對i進行過操作,它會自動把上次讀的數據放在k中。而不是重新從i裡面讀。這樣以來,如果i是一個寄存器變量或者表示一個端口數據就容易出錯,所以說volatile可以保證對特殊地址的穩定訪問,不會出錯。
int square(volatile int *ptr) { return *ptr * *ptr; }
這段代碼的目的是用來返指針ptr指向值的平方,但是,由於ptr指向一個volatile型參數,編譯器將產生類似下面的代碼:
int square(volatile int *ptr) { int a,b; a = *ptr; b = *ptr; return a * b; }
由於*ptr的值可能被意想不到地該變,因此a和b可能是不同的。結果,這段代碼可能返不是你所期望的平方值!正確的代碼如下:
long square(volatile int *ptr) { int a; a = *ptr; return a * a; }
下面是volatile變量的幾個使用:
並行設備的硬件寄存器(如:狀態寄存器)
一個中斷服務子程序中會訪問到的非自動變量(Non-automatic variables)
多線程應用中被幾個任務共享的變量
那麼問題來了:
一個參數既可以是const還可以是volatile嗎?答案是是的。一個例子是只讀的狀態寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應該試圖去修改它。
一個指針可以是volatile 嗎?答案是是的。盡管這並不很常見。一個例子是當一個中服務子程序修該一個指向一個buffer的指針時。
在編寫多線程程序中使用volatile的關鍵點:
1)將所有的共享對象聲明為volatile;
2)不要將volatile直接作用於基本類型;
3)當定義了共享類的時候,用volatile成員函數來保證線程安全;
在多線程中,我們可以利用鎖的機制來保護好資源臨界區。在臨界區的外面操作共享變量則需要volatile,在臨界區的裡面則non-volatile了。
2.關鍵字const的位置
const意味著”只讀”,分析下面的含義:
const int a; int const a;// 前兩個的作用是一樣,a是一個常整型數。 const int *a;// 第三個意味著a是一個指向常整型數的指針(整型數是不可修改的,但指針可以) int * const a;// 第四個意思a是一個指向整型數的常指針(指針指向的整型數是可以修改的,但指針是不可修改的) int const * const a; // a是一個指向常整型數的常指針(指針指向的整型數是不可修改的,同時指針也是不可修改的)。 //表示a是一個指針常量,初始化的時候必須固定指向一個int常量或者int變量,之後就不能再指向別的地方了,它總是把它所指向的目標當作一個int常量。 //也可以寫成const int* const a;含義相同。
合理地使用關鍵字const可以使編譯器很自然地保護那些不希望被改變的參數,防止其被無意的代碼修改。這樣可以減少bug的出現。欲阻止一個變量被改變,可以使用 const 關鍵字。
在定義 const 變量時,通常需要對它進行初始化,因為以後就沒有機會再去改變它了;對指針來說,可以指定指針本身為 const,也可以指定指針所指的數據為 const,或二者同時指定為 const;在一個函數聲明中,const 可以修飾形參,表明它是一個輸入參數,在函數內部不能改變其值;對於類的成員函數,若指定其為 const 類型,則表明其是一個常函數,不能修改類的成員變量;對於類的成員函數,有時候必須指定其返回值為 const 類型,以使得其返回值不為“左值”。
3.滑動的時候隱藏navigation bar
navigationController.hidesBarsOnSwipe = Yes;
4. 消除導航條返回鍵帶的title
[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -60) forBarMetrics:UIBarMetricsDefault];
5. 將Navigationbar變成透明而不模糊
[self.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault]; self.navigationController.navigationBar .shadowImage = [UIImage new]; self.navigationController.navigationBar .translucent = YES;
6. static
函數體內 static 變量的作用范圍為該函數體,不同於 auto 變量,該變量的內存只被分配一次,因此其值在下次調用時仍維持上次的值;在模塊內的 static 全局變量可以被模塊內所用函數訪問,但不能被模塊外其它函數訪問;在模塊內的 static 函數只可被這一模塊內的其它函數調用,這個函數的使用范圍被限制在聲明它的模塊內;在類中的 static 成員變量屬於整個類所擁有,對類的所有對象只有一份拷貝;在類中的 static 成員函數屬於整個類所擁有,這個函數不接收 this 指針,因而只能訪問類的static 成員變量。
7.用一個pan手勢來代替UISwipegesture的各個方向
- (void)pan:(UIPanGestureRecognizer *)sender { typedef NS_ENUM(NSUInteger, UIPanGestureRecognizerDirection) { UIPanGestureRecognizerDirectionUndefined, UIPanGestureRecognizerDirectionUp, UIPanGestureRecognizerDirectionDown, UIPanGestureRecognizerDirectionLeft, UIPanGestureRecognizerDirectionRight }; static UIPanGestureRecognizerDirection direction = UIPanGestureRecognizerDirectionUndefined; switch (sender.state) { case UIGestureRecognizerStateBegan: { if (direction == UIPanGestureRecognizerDirectionUndefined) { CGPoint velocity = [sender velocityInView:recognizer.view]; BOOL isVerticalGesture = fabs(velocity.y) > fabs(velocity.x); if (isVerticalGesture) { if (velocity.y > 0) { direction = UIPanGestureRecognizerDirectionDown; } else { direction = UIPanGestureRecognizerDirectionUp; } } else { if (velocity.x > 0) { direction = UIPanGestureRecognizerDirectionRight; } else { direction = UIPanGestureRecognizerDirectionLeft; } } } break ; } case UIGestureRecognizerStateChanged: { switch (direction) { case UIPanGestureRecognizerDirectionUp: { [self handleUpwardsGesture:sender]; break ; } case UIPanGestureRecognizerDirectionDown: { [self handleDownwardsGesture:sender]; break; } case UIPanGestureRecognizerDirectionLeft: { [self handleLeftGesture:sender]; break; } case UIPanGestureRecognizerDirectionRight: { [self handleRightGesture:sender]; break ; } default : { break; } } break; } case UIGestureRecognizerStateEnded: { direction = UIPanGestureRecognizerDirectionUndefined; break; } default: break; } }
8. 拉伸圖片不變形
等同於:
[[UIImage imageNamed:@""] stretchableImageWithLeftCapWidth:10 topCapHeight:10]; [[UIImage imageNamed:@""] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
9. Gif圖片顯示優化
FLAnimatedImage可以幫你完成GIF的顯示處理。解決GIF顯示卡頓的情況。
FLAnimatedImage *image = [FLAnimatedImage animatedImageWithGIFData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://upload.wikimedia.org/wikipedia/commons/2/2c/Rotating_earth_%28large%29.gif"]]]; FLAnimatedImageView *imageView = [[FLAnimatedImageView alloc] init]; imageView.animatedImage = image; imageView.frame = CGRectMake(0.0, 0.0, 100.0, 100.0); [self.view addSubview:imageView];
使用就是這麼簡單。
10. CollectionView實現tableview的懸停header
CSStickyHeaderFlowLayout可以解決您的疑問。
#import "CSStickyHeaderFlowLayout.h" - (void)viewDidLoad { [super viewDidLoad]; // Locate your layout CSStickyHeaderFlowLayout *layout = (id)self.collectionViewLayout; if ([layout isKindOfClass:[CSStickyHeaderFlowLayout class]]) { layout.parallaxHeaderReferenceSize = CGSizeMake(320, 200); } } - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { // Check the kind if it's CSStickyHeaderParallaxHeader if ([kind isEqualToString:CSStickyHeaderParallaxHeader]) { UICollectionReusableView *cell = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:@"header" forIndexPath:indexPath]; return cell; } }
就是這麼簡單。