本文大體是一些細節(jié)性的小坑,不定期更新,歡迎糾正。
關(guān)于變量的讀寫
變量的讀寫通常使用 self. 和 _ 兩種方式,但他們有什么區(qū)別呢?我們先看下面兩個例子。
@property (nonatomic, strong) NSMutableString * str1;
@property (nonatomic, copy) NSMutableString * str2;
假設(shè)我們定義了兩個變量,一個用strong屬性修飾,一個用copy屬性修飾。
NSMutableString * strA = [[NSMutableString alloc] initWithString:@"A"];
self.str1 = strA;
self.str2 = strA;
[strA appendFormat:@"C"];
NSLog(@"%@, %@", _str1, _str2); // 輸出AC, A
第一個例子使用了self.來給變量賦值,由于str2使用了copy屬性修飾,因此它給strA創(chuàng)建了一個不可變副本,當strA發(fā)生變化時,不影響str2。
NSMutableString * strB = [[NSMutableString alloc] initWithString:@"B"];
_str1 = strB;
_str2 = strB;
[strB appendFormat:@"C"];
NSLog(@"%@, %@", _str1, _str2); // 輸出BC, BC
第二個例子直接使用來賦值,但是輸出的結(jié)果卻與第一個不一致,str2也發(fā)生了改變。
當我們使用self.方式時,實際上是調(diào)用了set方法來對變量賦值,而使用賦值時繞過了set方法,因此copy修飾符沒生效,采用了默認的strong修飾符,所以str2的值也跟著發(fā)生改變。改進方法:
_str2 = [strB copy];
關(guān)于常量的定義
常量我們通常使用#define或者extern來定義。
define其實就是個替代宏,編譯器在編譯的時候會把該位置轉(zhuǎn)為具體的代碼,如果使用define來定義常量,并且該常量被應(yīng)用于多個位置,那么就可能造成內(nèi)存的浪費。舉一個簡單的例子:
#define NAME @"name"
NSString * name1 = [NSString stringWithString:NAME];
NSString * name2 = [NSString stringWithString:NAME];
// 上面兩行代碼在編譯的時候就相當于下面這種寫法,多開辟了兩個name的內(nèi)存空間
NSString * name1 = [NSString stringWithString:@"name"];
NSString * name2 = [NSString stringWithString:@"name"];
因此比較合理的做法是采用extern的方式來聲明。
// 頭文件(.h)中定義
extern NSString * const name;
// 實現(xiàn)文件(.m),在import下方書寫
NSString * const name = @"name";
這種方式的寫法在未使用name這個常量時,它是個nil值不占內(nèi)存空間。當?shù)谝淮问褂胣ame時,系統(tǒng)自動為其分配內(nèi)存,并持續(xù)存在,后面的第N次使用都相當于訪問這塊內(nèi)存。
另外const的位置也是有講究的,舉個例子:
NSString * const constVal1 = @"A";
NSString const * constVal2 = @"B";
const NSString * constVal3 = @"C";
constVal1 = @"A1"; // 報錯
constVal2 = @"B1";
constVal3 = @"C1";
constVal1就是我們常規(guī)意義上的常量,它的值是無法改變的。而對于constVal2和constVal3它倆是沒有區(qū)別的,都是代表指針地址不變,但可指向新的值。
</br></br>
iOS7的手勢返回
這里說的手勢返回并不是指深度定制的返回動畫,而是自帶的從屏幕邊緣滑動返回。
重新設(shè)定導航欄的leftBarButtonItem后會導致手勢返回失效,此時可以通過一行簡單的代碼來使其重新生效:
self.navigationController.interactivePopGestureRecognizer.delegate
= (id)self;
但我們的項目通常會給VC設(shè)定一個公共的父類,在父類中設(shè)置公共樣式的leftBarButtonItem,并且把這行代碼寫在viewDidLoad中。此時會遇到一個問題——在第一個VC中觸發(fā)邊緣手勢,然后點擊任意可跳轉(zhuǎn)入二級VC的按鈕,會發(fā)現(xiàn)界面假死了。
最大的原因就在于把上述代碼寫在父類的viewDidLoad時,由于第一個VC也繼承了該父類,因此也會觸發(fā)邊緣返回手勢,但因為已經(jīng)是棧底了無法返回,就會導致返回出錯,導致想要進入二級界面時,界面假死。
一個合理的解決方案是將其寫在viewDidAppear中,并且過濾第一個VC:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if([self.navigationController.viewControllers count] >= 2) {
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
self.navigationController.interactivePopGestureRecognizer.delegate = (id)self;
}else{
self.navigationController.interactivePopGestureRecognizer.delegate = nil;
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}
}
狀態(tài)欄
在iOS7之后控制狀態(tài)欄的樣式通常使用-preferredStatusBarStyle方法來設(shè)定狀態(tài)欄的風格。(注:View controller-based status bar appearance 要設(shè)定為 YES)
但有時候你會發(fā)現(xiàn),在導航控制器里面的VC重寫此方法是無效的。原因很簡單,主控權(quán)不在當前VC中,而是在它的父VC——NavigationController中。有兩種解決方式,重寫NavC中的-preferredStatusBarStyle方法或-childViewControllerForStatusBarStyle方法。
- (UIStatusBarStyle)preferredStatusBarStyle
{
return [self.topViewController preferredStatusBarStyle];
}
// 上下兩種方法任選一
- (UIViewController *)childViewControllerForStatusBarStyle
{
return self.topViewController;
}
另外,狀態(tài)欄的隱藏是可以帶動畫,一種是Fade,一種是Slide
- (BOOL)prefersStatusBarHidden
{
return ![UIApplication sharedApplication].statusBarHidden;
}
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation
{
return UIStatusBarAnimationSlide;
}
- (IBAction)hideOrShowStatusBar:(id)sender {
[UIView animateWithDuration:0.2 animations:^{
[self setNeedsStatusBarAppearanceUpdate];
}];
}