New Objective-C Literals

NSArray:

NSInteger _appid = 12345;
NSArray *array = @[ @"title", @(_appid)];

NSString *title = array[0];
array[0] = @"newTitle";

NSDictionary:

NSDictionary *dict = @{
    @"appid" : @(_appid),
    @"title" : _title,
};

NSString *title = dict[@"title"];
dict[@"title"] = @"newTitle";

NSNumber:

NSNumber *intNum = @123;
NSNumber *floatNum = @1.23f;
NSNumber *boolNum = @YES;

More: Objective-C Literals

iOS URL Loading System

iOS 整个网络请求系统分为这几部分:

  • URL Loading:
    • NSURLRequest / NSMutableURLRequest
    • NSURLResponse / NSHTTPURLResponse
    • NSURLConnection
  • Cache Management
    • NSURLCache
    • NSCachedURLResponse
  • Authentication and Credentials
    • NSURLCredential
    • NSURLCredentialStorage
    • NSURLAuthenticationChallenge
    • NSURLAuthenticationChallengeSender
    • NSURLProtectionSpace
  • Cookie Storage
    • NSHTTPCookie
    • NSHTTPCookieStorage
  • Protocol Support

参考 URL Loading System Overview.

禁用 git merge 完成后的提交说明

Git 1.7.10+ 版本对 git merge 做了一个改动,就是 merge 成功后会自动打开编辑器等待输入 merge 提交说明,而之前版本是自动完成这个 log。Linus 大神说之前的做法其实是一个设计失误 via

we never even fire up the editor by default for a “git merge”, but we do for a “git commit”. That was a design mistake, and it means that if you want to actually add a note to a merge, you have to do extra work.

然而大部分时候我们 merge 的时候都不太会去手动添加 merge log,那么这个功能就是个干扰,每次都要手动去关掉编辑器。这里 找到一个解决方案:

export GIT_MERGE_AUTOEDIT=no

Done.

CocoaPods Notes

CocoaPods, an Objective-C library dependency manager.


安装:

sudo gem install cocoapods
pod setup //初始化更新 Specs

新建项目,在项目 根目录 新建 Podfile 文件:

platform :ios, '5.0'

pod 'AFNetworking'
pod 'SDWebImage', :git => 'https://github.com/appwilldev/SDWebImage.git'
pod 'JSONKit', :podspec => 'https://raw.github.com/gist/1346394/1d26570f68ca27377a27430c65841a0880395d72/JSONKit.podspec'

Podfile 可以指定具体代码地址,具体一个 commit/tag,或者具体 podspec (多用于私有库)。安装相关 Pods:

pod install

CocoaPods 会新建一个和项目同名的 workspace,以后就用这个 workspace 来打开项目。需要新加或删除库的话就直接编辑 Podfile 然后再 pod install.


添加 CocoaPods/Specs 没有或私有库:

pod spec create WeiboEngine https://github.com/fannheyward/WeiboEngine
// 如果指定 Github 链接,会获取代码库相关信息来初始化 podspec.

自动生成 WeiboEngine.podspec 文件,按照模板编辑修改相关作者、项目主页等信息。重点是 s.source 设置。s.source 指定代码库地址,支持 git/hg/svn 代码库,支持 http://example.com/source.zip 代码压缩包,支持用 :tag :commit 指定具体版本。s.source_files 指明代码目录文件。

验证生成的 podspec 文件是否合法正确:

pod spec lint WeiboKit.podspec

验证通过后把 podspec 保存在 ~/.cocoapods/master/ 即可直接通过 pod install 进行安装;也可以向 CocoaPods/Specs 提交新建的 spec:

pod setup --push
pod push master

iOS 6 下自动旋转的变化

iOS 6 SDK 中的屏幕自动旋转有了一些变化,简单纪录之。举例:Master-Detail 类型 App,master ViewController 不支持屏幕旋转, detail ViewController 支持屏幕旋转。

在 Info.plist 或 Target-Summary 启用自动旋转,选中需要的 Supported Interface Orientations。新建 UINavigationController+Autorotation.h category,根据需要禁用最底层 NavController 的自动旋转:

- (BOOL)shouldAutorotate
{
    return NO;
}

在 AppDelegate 设置 window.rootViewController = navController;,由于 shouldAutorotateToInterfaceOrientation: 从 iOS 6 起 deprecated,在需要自动旋转的 viewController 改用 supportedInterfaceOrientations+preferredInterfaceOrientationForPresentation

- (BOOL)shouldAutorotate
{
    return YES;
}

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskAllButUpsideDown;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    return UIInterfaceOrientationLandscapeRight;
}

几个需要注意的地方:

  1. window 需要设置 rootViewController,[window addSubview:navController.view]; 无效;
  2. shouldAutorotate 在最底层设置才有效;
  3. presentModalViewController 下用之前的自动旋转控制无效,须用 category 解决。

NSLinguisticTagger Notes

NSLinguisticTagger 是 iOS 5+/OS X 10.7+ 引入的自然语言智能分析类。一个简单的 sample:

NSString *text = @"The iPhone is a line of smartphones designed and marketed by Apple Inc. The iPhone runs Apple's iOS mobile operating system, originally named iPhone OS. The first iPhone was unveiled by then CEO of Apple Steve Jobs on January 9, 2007, and released on June 29, 2007. The most recent iPhone, the 5th generation iPhone 4S, was released in October 2011. iPhone是苹果公司旗下的一个智能手机系列,此系列手机搭载苹果公司研发的iOS手机操作系统。第一代iPhone于2007年1月9日由时任苹果公司CEO的史蒂夫·乔布斯发布,并在6月29日正式发售;最新的iPhone 4s于2011年10月4日发布,并于同年10月14日正式发售。"; // text from Wikipedia.

NSArray *schemes = [NSArray arrayWithObject:NSLinguisticTagSchemeNameTypeOrLexicalClass];
NSLinguisticTagger *tagger = [[NSLinguisticTagger alloc] initWithTagSchemes:schemes options:0];
tagger.string = text;
[tagger enumerateTagsInRange:NSMakeRange(0, text.length)
                      scheme:NSLinguisticTagSchemeNameTypeOrLexicalClass
                     options:NSLinguisticTaggerOmitWhitespace | NSLinguisticTaggerOmitPunctuation // 忽略空格和标点
                  usingBlock:^(NSString *tag, NSRange tokenRange, NSRange sentenceRange, BOOL *stop) {
                      NSLog(@"%@ is a %@", [text substringWithRange:tokenRange], tag);
                  }];

// OR

[text enumerateLinguisticTagsInRange:NSMakeRange(0, text.length)
                              scheme:NSLinguisticTagSchemeNameTypeOrLexicalClass
                             options:NSLinguisticTaggerOmitWhitespace | NSLinguisticTaggerOmitPunctuation
                         orthography:nil
                          usingBlock:^(NSString *tag, NSRange tokenRange, NSRange sentenceRange, BOOL *stop) {
                              NSLog(@"%@ is a %@", [text substringWithRange:tokenRange], tag);
                    }];

结果输出:

2012-09-11 11:56:45.192 LinguisticTaggerSample[3342:c07] The is a Determiner
2012-09-11 11:56:45.193 LinguisticTaggerSample[3342:c07] iPhone is a Noun
2012-09-11 11:56:45.193 LinguisticTaggerSample[3342:c07] is is a Verb
2012-09-11 11:56:45.193 LinguisticTaggerSample[3342:c07] a is a Determiner
2012-09-11 11:56:45.194 LinguisticTaggerSample[3342:c07] line is a Noun
2012-09-11 11:56:45.194 LinguisticTaggerSample[3342:c07] of is a Preposition
2012-09-11 11:56:45.194 LinguisticTaggerSample[3342:c07] smartphones is a Adverb
2012-09-11 11:56:45.194 LinguisticTaggerSample[3342:c07] designed is a Verb
2012-09-11 11:56:45.195 LinguisticTaggerSample[3342:c07] and is a Conjunction
2012-09-11 11:56:45.195 LinguisticTaggerSample[3342:c07] marketed is a Verb
2012-09-11 11:56:45.195 LinguisticTaggerSample[3342:c07] by is a Preposition
2012-09-11 11:56:45.196 LinguisticTaggerSample[3342:c07] Apple is a OrganizationName
2012-09-11 11:56:45.196 LinguisticTaggerSample[3342:c07] Inc is a OrganizationName
...
2012-09-11 11:56:45.203 LinguisticTaggerSample[3342:c07] Steve is a PersonalName
2012-09-11 11:56:45.203 LinguisticTaggerSample[3342:c07] Jobs is a PersonalName
...
2012-09-11 11:56:45.222 LinguisticTaggerSample[3342:c07] iPhone is a Noun
2012-09-11 11:56:45.223 LinguisticTaggerSample[3342:c07] 是 is a Particle
2012-09-11 11:56:45.223 LinguisticTaggerSample[3342:c07] 苹果 is a Verb
2012-09-11 11:56:45.223 LinguisticTaggerSample[3342:c07] 公司 is a Particle
2012-09-11 11:56:45.224 LinguisticTaggerSample[3342:c07] 旗下 is a Verb
2012-09-11 11:56:45.224 LinguisticTaggerSample[3342:c07] 的 is a Particle
2012-09-11 11:56:45.224 LinguisticTaggerSample[3342:c07] 一 is a Verb
2012-09-11 11:56:45.288 LinguisticTaggerSample[3342:c07] 个 is a Particle
2012-09-11 11:56:45.288 LinguisticTaggerSample[3342:c07] 智能 is a Verb
2012-09-11 11:56:45.289 LinguisticTaggerSample[3342:c07] 手机 is a Particle
2012-09-11 11:56:45.289 LinguisticTaggerSample[3342:c07] 系列 is a Verb
...
2012-09-11 11:56:45.310 LinguisticTaggerSample[3342:c07] 史 is a Verb
2012-09-11 11:56:45.311 LinguisticTaggerSample[3342:c07] 蒂 is a Particle
2012-09-11 11:56:45.311 LinguisticTaggerSample[3342:c07] 夫 is a Verb
2012-09-11 11:56:45.311 LinguisticTaggerSample[3342:c07] 乔 is a Particle
2012-09-11 11:56:45.312 LinguisticTaggerSample[3342:c07] 布 is a Verb
2012-09-11 11:56:45.312 LinguisticTaggerSample[3342:c07] 斯 is a Particle

对英文的分析要好于中文。不同的 scheme 有不同的返回结果,包括文本语言等。详细文档 NSLinguisticTagger Class Reference.

PS:可以用这个做一个微博关键词分析,罗列出自己微博最多的关键词。

UITableView 性能优化笔记

Hacking Week 技术总结最后一篇,记一下 UITableView 性能优化需要注意和改进的地方。

  1. 网络图片异步加载,SDWebImage。
  2. 文字直接 drawInRect/drawAtPoint 绘制,参考 ABTableViewCell,AdvancedTableViewCells
  3. 本地图片也可以直接绘制,或者用 CALayer 来添加显示。
  4. cell 重用机制。
  5. cell 内容尽量避免透明效果。
  6. 如非必要,减少 reloadData 全部 cell,只 reloadRowsAtIndexPaths。
  7. 如果 cell 是动态行高,计算出高度后缓存。tableView 会在加载的时候把全部 cell 的高度通过 heightForRowAtIndexPath: 都计算出来,即使 cell 还没有展示。
  8. 如果 cell content 的展示位置也不固定,第一次计算后也要缓存。
  9. cell 高度固定的话直接用 cell.rowHeight 设置高度,不要再实现 tableView:heightForRowAtIndexPath: delegate.
  10. cell content 的解析操作(尤其是复杂的解析)异步进行+预执行,解析结果要缓存。
  11. 可以预先加载需要的网络资源(图片等),SDWebImagePrefetcher.

There are performance implications to using tableView:heightForRowAtIndexPath: instead of the rowHeight property. Every time a table view is displayed, it calls tableView:heightForRowAtIndexPath: on the delegate for each of its rows, which can result in a significant performance problem with table views having a large number of rows (approximately 1000 or more). via Apple Document

Use copy property for NSString

一个简短例子来说明一下为什么 NSString @property 最好用 copy 而不是 retain:

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic, retain) NSString *name;
@property (nonatomic, copy) NSString *school;

@end

@implementation Person

@synthesize name, school;

@end

int main(int argc, char *argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];

        NSMutableString *s1 = [NSMutableString stringWithString:@"fannheyward"];
        NSMutableString *s2 = [NSMutableString stringWithString:@"hfut"];
        p.name = s1;
        p.school = s2;
        NSLog(@"%@, %@", p.name, p.school); // fannheyward, hfut
        [s1 appendString:@"---Heybot"];
        [s2 appendString:@"---Heybot"];
        NSLog(@"%@, %@", p.name, p.school); // fannheyward---Heybot, hfut
    }
}

简单来说就是 NSString 可以通过 NSMutableString (isa NSString) 来进行修改,如果 @property 是 retain 的话就可以绕过 Person 实例来修改 name 值(因为 name 指向 s1),大部分时候这种情况都是不应该发生的,用 copy 就没有这个问题。

这样来说象 NSArray/NSDictionary 等可修改类型都应该用 copy

For attributes whose type is an immutable value class that conforms to the NSCopying protocol, you almost always should specify copy in your @property declaration.

参考 NSString property: copy or retain?

Heybot - My Gtalk Hubot

Github 是非常好的学习地方,Github Inc 这家公司也很有意思,一帮 Geek 程序员做了很多很好玩的东西,比如 Hubot, Play。Hubot 是一个机器人,可以音乐、搜索、搞怪逗乐等,在 Github 内部他们还用 Hubot 部署代码。开源版本的 Hubot 目前不支持代码部署等高级命令,不过可以自己写脚本(CoffeeScript)进行扩展。

Hubot 原生支持 Campfire、Shell 作接口,通过 npm 扩展可以用 Gtalk、IRC 等等。在 Heroku 上部署了一个用 Gtalk 作接口的 Heybot(Heyward’s Hubot),简单纪录一下。

wget https://github.com/downloads/github/hubot/hubot-2.3.2.tar.gz
tar xzvf hubot-2.3.2.tar.gz
cd hubot

vim Procfile 修改 adapter:
    app: bin/hubot -a gtalk -n Hubot
vim package.json 添加 hubot-gtalk 到 dependencies:
    "hubot-gtalk": ">= 0.0.1",

git init
git add *
git commit -m "init"

heroku apps:create
git push heroku master
heroku ps:scale app=1
heroku addons:add redistogo:nano
heroku config:add HUBOT_GTALK_USERNAME="xxx" HUBOT_GTALK_PASSWORD="xxx"
heroku ps:restart

添加 Gtalk 好友,hubot help 可以查看目前支持的命令。

NSPredicate Notes

在 Core Data 中可以给 NSFetchRequest 指定一个 predicate 来对数据进行过滤以方便查找,比如:

fetchRequest.predicate = [NSPredicate predicateWithFormat:@"id == %@", 123];

NSPredicate 的过滤查询规则不仅仅适用于 Core Data,字符串过滤也很方便。比如:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS %@", @"hello"];
BOOL b = [predicate evaluateWithObject:@"hello world"]; // YES

字符串支持的判断语法有 contains beginswith endswith like matches and/or/not/in

NSPredicate *predicate1 = [NSPredicate predicateWithFormat:@"SELF BEGINSWITH %@", @"hello"];
BOOL b = [predicate1 evaluateWithObject:@"hello world"]; // YES
BOOL n = [predicate1 evaluateWithObject:@"nohello world"]; // NO

like 匹配,支持 * 任意字符(可无),? 有且仅有一个字符:

NSPredicate *like = [NSPredicate predicateWithFormat:@"SELF LIKE %@", @"*like?"];
NSLog(@"%d", [like evaluateWithObject:@"alike"]); // 0-NO
NSLog(@"%d", [like evaluateWithObject:@"000liked"]); // 1-YES
NSLog(@"%d", [like evaluateWithObject:@"likes"]); // 1-YES

matches 正则匹配:

NSPredicate *match = [NSPredicate predicateWithFormat:@"SELF MATCHES '\\\\d+[a-z]'"];
NSLog(@"%d", [match evaluateWithObject:@"0A"]); // NO
NSLog(@"%d", [match evaluateWithObject:@"0a"]); // YES
NSLog(@"%d", [match evaluateWithObject:@"000000ab"]); // NO
NSLog(@"%d", [match evaluateWithObject:@"000000c"]); // YES

NSPredicate 可以组合起来用,这也是最为方便的地方,比如下面这个例子:

字符串以 CH 开头,长度大于 3 而小于 20 字符,包含至少一个数字,不包含 broken,不包含空格。

NSPredicate *one = [NSPredicate predicateWithFormat:@"SELF BEGINSWITH 'CH'"];
NSPredicate *two = [NSPredicate predicateWithFormat:@"SELF.length > 3 AND SELF.length < 20"];
NSPredicate *three = [NSPredicate predicateWithFormat:@"SELF MATCHES '.*\\\\d.*'"];
NSPredicate *four = [NSPredicate predicateWithFormat:@"NOT(SELF CONTAINS 'broken') AND NOT(SELF CONTAINS ' ')"];

NSArray *array = [NSArray arrayWithObjects:one, two, three, four, nil];
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:array];
NSLog(@"%d", [predicate evaluateWithObject:@"CH998broken"]); // NO
NSLog(@"%d", [predicate evaluateWithObject:@"CH998"]); //YES

@"attributeName == %@": the value of the key attributeName is the same as the value of the object(NSDate, NSNumber, NSDecimalNumber, or NSString). 完全相等判断。

@"%K == %@": the value of the key %K is the same as the value of the object %@. key 对应的值和给定的值相等。

@"name IN $NAME_LIST": the value of the key name is in the variable $NAME_LIST. @"'name' IN $NAME_LIST": the constant value ‘name’ (note the quotes around the string) is in the variable $NAME_LIST. 判断值是否在指定列表中,前者判断是 name 对应的值,后者 'name' 就是判断 name 字符串。

参考资料: