object == nil or nil == object
在 Objective-C 中拿到一个对象后检查对象是否为空,一般有两种写法
一:
if (object == nil) {
//...
}
二:
if (nil == object) {
//...
}
这两种写法其实没有任何区别,从代码的可读性上来说第一种 object == nil 方式会好一点。但是推荐用第二种 nil == object 方式,最大的好处就是如果由于笔误 == 写成了 =,编译器会直接报错处理。而 object = nil 不会报错,一旦笔误写成了 object = nil 是很难 debug 查找问题。
via object == nil or nil == object to check whether an object is nil?
2 Years in Beijing
留记。
2010.06.17 到 2012.06.17,很有收获的两年,很有成长的两年。是一个结点,也是新的起点。
postNotificationName with GCD
用 GCD 在后台线程进行下载任务,下载完成后通过 NSNotificationCenter post 一个消息出来,这时候要注意 postNotificationName: 必须要回到主线程进行,不然会引发 crash.
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AnyNotification
object:nil];
});
resignFirstResponder doesn't work on iPad
在 iPad 上,用 modalPresentationStyle = UIModalPresentationFormSheet 方式推出一个 viewController,这时这个 viewController 不会响应 resignFirstResponder,其他样式的 modalPresentationStyle 没有问题。苹果一个开发在开发者论坛说这是个 feature,不是 bug,devforums.apple.com
Was your view by any chance presented with the UIModalPresentationFormSheet style? To avoid frequent in-and-out animations, the keyboard will sometimes remain on-screen even when there is no first responder. This is not a bug.
就算不是 bug 也很恼人,有人给出了解决方法 devforums.apple.com。新建一个 UINavigationController category,禁掉 disablesAutomaticKeyboardDismissal:
- (BOOL)disablesAutomaticKeyboardDismissal
{
return NO;
}
然后把 viewController 挂在 UINavigationController 下即可:
MyViewController *myViewController = [[MyViewController alloc] initWithNibName:@"MyViewController" bundle:nil];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:myViewController];
theNavigationController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:theNavigationController animated:YES];
SO 参考:
隐藏 UITableView 下不需要的分割线
UITableViewStylePlain 样式下的 UITableView 如果显示分割线,就会在 tableView 下显示额外的空白 cell 和分割线。在 SO 上发现一个小技巧来解决这个问题 Eliminate Extra separators below UITableView - in iphone sdk?
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.width, 1)];
v.backgroundColor = [UIColor whiteColor];
[self.tableView setTableFooterView:v];
@autoreleasepool in loop or loop in @autoreleasepool
如果循环中会生成大量的 autorelease 对象,可以考虑用 autorelease pool 来进行封装。封装时候有两种方式:
1:
while ([rs next]) {
@autoreleasepool {
NSDictionary *dict = [self dictFromXX];
//...
}
}
2:
@autoreleasepool {
while ([rs next]) {
NSDictionary *dict = [self dictFromXX];
//...
}
}
第一种,也就是 @autoreleasepool in loop 方式下每次循环都会生成一个 pool,在单次循环结束后被 drain 掉,适用于每次循环都有大量的 autorelease 对象生成,在单次循环结束后可以及时的将资源释放。
第二种,loop in @autoreleasepool 下只有一个 pool,只会在整个循环结束后 drain 掉,也就是说第一次循环时生成的 autorelease 对象也要等到整个循环结束时候才会随着 pool 释放。适用于循环次数不太多,且每次循环只有少量的 autorelease 对象生成,毕竟这些对象都要等到循环结束后才会被释放。
Difference between objectForKey and valueForKey in NSDictionary
从 NSDictionary 取值的时候有两个方法,objectForKey: 和 valueForKey:,这两个方法具体有什么不同呢?
先从 NSDictionary 文档中来看这两个方法的定义:
objectForKey: returns the value associated with aKey, or nil if no value is associated with aKey. 返回指定 key 的 value,若没有这个 key 返回 nil.
valueForKey: returns the value associated with a given key. 同样是返回指定 key 的 value。
直观上看这两个方法好像没有什么区别,但文档里 valueForKey: 有额外一点:
If key does not start with “@”, invokes objectForKey:. If key does start with “@”, strips the “@” and invokes [super valueForKey:] with the rest of the key. via Discussion
一般来说 key 可以是任意字符串组合,如果 key 不是以 @ 符号开头,这时候 valueForKey: 等同于 objectForKey:,如果是以 @ 开头,去掉 key 里的 @ 然后用剩下部分作为 key 执行 [super valueForKey:]。
比如:
NSDictionary *dict = [NSDictionary dictionaryWithObject:@"theValue"
forKey:@"theKey"];
NSString *value1 = [dict objectForKey:@"theKey"];
NSString *value2 = [dict valueForKey:@"theKey"];
这时候 value1 和 value2 是一样的结果。如果是这样一个 dict:
NSDictionary *dict = [NSDictionary dictionaryWithObject:@"theValue"
forKey:@"@theKey"];// 注意这个 key 是以 @ 开头
NSString *value1 = [dict objectForKey:@"@theKey"];
NSString *value2 = [dict valueForKey:@"@theKey"];
value1 可以正确取值,但是 value2 取值会直接 crash 掉,报错信息:
Terminating app due to uncaught exception ‘NSUnknownKeyException’, reason: ‘[<__NSCFDictionary 0x892fd80> valueForUndefinedKey:]: this class is not key value coding-compliant for the key theKey.’
这是因为 valueForKey: 是 KVC(NSKeyValueCoding) 的方法,在 KVC 里可以通过 property 同名字符串来获取对应的值。比如:
@interface Person : NSObject
@property (nonatomic, retain) NSString *name;
@end
...
Person *person = [[Person alloc] init];
person.name = @"fannheyward";
NSLog(@"name:%@", [person name]);
//name:fannheyward
NSLog(@"name:%@", [person valueForKey:@"name"]);
//name:fannheyward
[person release];
valueForKey: 取值是找和指定 key 同名的 property accessor,没有的时候执行 valueForUndefinedKey:,而 valueForUndefinedKey: 的默认实现是抛出 NSUndefinedKeyException 异常。参考Getting Attribute Values Using Key-Value Coding
回过头来看刚才 crash 的例子, [dict valueForKey:@"@theKey"]; 会把 key 里的 @ 去掉,也就变成了 ` [dict valueForKey:@”theKey”];,而 dict 不存在 theKey 这样的 property,转而执行 [dict valueForUndefinedKey:@”theKey”];,抛出 NSUndefinedKeyException` 异常后 crash 掉。
objectForKey: 和 valueForKey: 在多数情况下都是一样的结果返回,但是如果 key 是以 @ 开头,valueForKey: 就成了一个大坑,建议在 NSDictionary 下只用 objectForKey: 来取值。
多 Target 下不同的 Bundle Display Name
真不好用一个标题来概括这个东西。Xcode 4.2+ 在项目多语言包 xx.lproj 里引入了一个叫 InfoPlist.strings 的文件,可以对同一个 App 在不同系统语言下显示不同的 Display Name。比如:
InfoPlist.strings (English) - "CFBundleDisplayName" = "English Name";
InfoPlist.strings (Chinese) - "CFBundleDisplayName" = "中文";
在单 Target 下很容易做,多 Target 的时候就需要做一点额外的处理。在项目目录下新建与 Target 同名的文件夹(同名是为了方便区分),然后将 xx.lproj 文件夹 复制 到各个 Target 下面,目录结构会是这个样子:
./Target1/
en.lproj/InfoPlist.strings
zh-Hans.lproj/InfoPlist.strings
./Target2/
en.lproj/InfoPlist.strings
zh-Hans.lproj/InfoPlist.strings
复制后保持项目目录下还有 xx.lproj 文件夹,里面保留 Localizable.strings,因为多语言化一般是通用的,没必要针对每一个 Target 做多语言。复制后的 Target1/xx.lproj 下只有 InfoPlist.strings。然后添加到 Xcode 项目里,打开 Xcode - Views - Utilities (Command+Option+0),在 Target Membership 下针对不同的 Target 把对应文件夹下的 InfoPlist.strings 对应连接起来,Done。
最大化 Xcode Debug Console 窗口
参考 How to get back the Console window in XCode4 做了一点点改动,Run 的时候自动切换到 Console Tab 并且是最大化展示,效果还不错。
- 打开 Tab 支持,View - Show Tab Bar.
- 双击或点 + 添加一个 Tab.
- 双击新加的 Tab 改名,比如 CONSOLE.
- 激活 Console 显示,View - Debug Aera - Activate Console,或者直接 Command+Shift+C.
- 拖动 Console 区至顶端,整个 Tab 只显示这个 Console.
- Command+, 进入 Preferences - Behaviors, 在 Run Start 里勾选 Show Tab,填 CONSOLE,就是刚才的 Tab 名。
- Done。
再运行项目时会自动的切换到新 Tab 页查看输出结果,然后通过 Command+Shift+[/] 来切换 Tab.
Enable SPDY in Firefox 11
SPDY is a new network protocol developed by Google for faster web. Google Chrome has a build-in support of SPDY long long ago. Now Firefox brings SPDY support in Firefox 11, but is disabled by default. You can enable it as follow:
Open about:config in Firefox, search network.http.spdy.enabled and set to true. via here