MarkNote – iPad上的Markdown文本编辑器

不才用业余时间鼓捣出一个叫MarkNote的iPad应用,用来给geek们编辑Markdown文本。使用方式是左侧编辑,右侧预览生成的HTML,然后接下来可以选择Email或是打印,复制之类。

MarkNote的功能

  • 实时预览
  • 自动保存
  • 多个主题选择,甚至可以做为一个简单的待办事项列表来使用(使用TopMarks主题)
  • 和Dropbox同步
  • 以html格式打印或邮件递送内容
  • 复制内容到其它应用中使用

真相图

\"TopMarks样式\"

\"夜间样式\"

BTW:这个应用苹果的审核人员只用一个小时就通过了,可见是多么简单的一个应用。

干掉xcode 4.2里的performselector警告

xcode 4.2非常可恶,原来的代码里有调用performselector:withObject:的地方无一例外获得一个警告:

Semantic Issue
PerformSelector may cause a leak because its selector is unknown

warning倒是不影响程序运行,但是这人要是有点代码小洁癖的话,那日子就没法过了,这warning怎么看都碍眼。所以必须得想办法把它弄没了:

#pragma clang diagnostic push
#pragma clang diagnostic ignored \"-Warc-performSelector-leaks\"
    [self performSelector:nextView];
#pragma clang diagnostic pop

世界清静啦

iOS 5的文件存储策略应对

苹果在iOS 5系统时,对app的文件存储提出了新的要求。从它的guildline来看,是推荐开发者尽量把app生成的文件放在Caches目录下的。原文如下:

Only user-generated data or that cannot otherwise be recreated by your application, should be stored in the /Documents directory and rest should be stored to /Library/Caches directory。

照做会怎么样?

如果这么做的话,会出现两种情况

  1. 如果对此置之不理,继续把应用生成的文件放在Documents目录下,那么这些文件会被备份到iTunes或者iCloud。如果这些文件很大,那么用户可能需要为了同步消耗不少流量,然后苹果可能会因此拒绝你的应用上架,这是一个悲剧。
  2. 如果开发者照Apple说的干,把应用生成的文件放在Caches目录下,那么苹果不会拒绝你的应用,很happy。但是iOS 5会在磁盘空间紧张的时候删除Caches目录下的文件,这对用户来说可能是一个更大的悲剧。

如何应对新的文件存储策略?

开发者在这时陷入了两难的境地,但是到了iOS 5.0.1的时候,开发者多了第三种选择:

  • 继续把文件存储在Documents目录下,但是标记这些文件为不需要备份。详情请参考 technote (QA1719)

原文如下:

Q: My app has a number of files that need to be stored on the device permanently for my app to function properly offline. However, those files do not contain user data and don\’t need to be backed up. How should I store those files in iOS 5?

A: Starting in iOS 5.0.1 a new \”do not back up\” file attribute has been introduced allowing developers to clearly specify which files should be backed up, which files are local caches only and subject to purge, and which files should not be backed up but should also not be purged. In addition, setting this attribute on a folder will prevent the folder and all of its contents from being backed up.

代码示例

给文件加上\”do not back up\”属性的代码如下,需要注意这个是iOS 5.0.1才有效,低于这个版本就别费劲了。


#include 
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
    const char* filePath = [[URL path] fileSystemRepresentation];
 
    const char* attrName = \"com.apple.MobileBackup\";
    u_int8_t attrValue = 1;
 
    int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
    return result == 0;
}

实战three20的TTTableViewController自定义单元格

three20中的TTTableCaptionItem实际使用效果是,左侧是较小字体的标题,右侧是大号字体的文本,如下图所示

但是在实际使用中,我希望左侧的字体能变大,右侧字体变小,所以我参照wiki的介绍 在TTTableViewController定制单元格 来进行调整,按文中介绍,我需要实现:

  1. 自定义的tableItem
  2. 自定义的tableItemCell
  3. 自定义的datasource,以便支持新增的tableItem

但是考虑到three20的代码库中已经有个半成品的TTTableRightCaptionItem,所以我只需要在这个基础上加工一下。

实现TTTableTextCaptionItem

我将这个自定义的类命名为TTTableTextCaptionItem以示区别

TTTableTextCaptionItem

TTTableTextCaptionItem.h


#import 

@interface TTTableTextCaptionItem : TTTableRightCaptionItem {
}

@end

TTTableTextCaptionItem.m


#import "TTTableTextCaptionItem.h"

@implementation TTTableTextCaptionItem
@end

TTTableTextCaptionItemCell

TTTableTextCaptionItemCell.h


#import 

@interface TTTableTextCaptionItemCell : TTTableRightCaptionItemCell {
}
@end

TTTableTextCaptionItemCell.m


#import "TTTableTextCaptionItem.h"
#import "TTTableTextCaptionItemCell.h"

static const CGFloat kKeySpacing = 12;
static const CGFloat kKeyWidth = 75;

@implementation TTTableTextCaptionItemCell

///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark UIView


///////////////////////////////////////////////////////////////////////////////////////////////////
- (void)layoutSubviews {
    [super layoutSubviews];
    
    self.detailTextLabel.frame = CGRectMake(kTableCellHPadding, kTableCellVPadding,
                                      kKeyWidth, self.textLabel.font.ttLineHeight);
    
    CGFloat valueWidth = self.contentView.width - (kTableCellHPadding*2 + kKeyWidth + kKeySpacing);
    CGFloat innerHeight = self.contentView.height - kTableCellVPadding*2;
    self.textLabel.frame = CGRectMake(kTableCellHPadding + kKeyWidth + kKeySpacing,
                                            kTableCellVPadding,
                                            valueWidth, innerHeight);
}

///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark TTTableViewCell


///////////////////////////////////////////////////////////////////////////////////////////////////
- (void)setObject:(id)object {
    if (_item != object) {
        [super setObject:object];
        
        TTTableTextCaptionItem* item = object;
        self.textLabel.text = item.text;
        self.detailTextLabel.text = item.caption;
    }
}

@end

如何使用TTTableTextCaptionItem

首先实现一个自定义的DataSource,然后在后续代码中使用这个DataSource

#import "TTTableTextCaptionItemCell.h"
#import "TTTableTextCaptionItem.h"

@interface MyCustomDataSource : TTSectionedDataSource
@end

@implementation MyCustomDataSource

- (Class)tableView:(UITableView*)tableView cellClassForObject:(id)object {
    if ([object isKindOfClass:[TTTableTextCaptionItem class]]) {
        return [TTTableTextCaptionItemCell class];
    } else {
        return [super tableView:tableView cellClassForObject:object];
    }
}

@end

代码下载

我把代码放到github上,方便大家参考 TTTableTextCaptionItem.git

如何将TTURLRequest和OAuthConsumer搭配使用

TTURLRequest是three20开发框架提供的一个url请求类,它是NSURLRequest类的扩展,有如下优点:

  • post数据方便,只需要构建一个参数的dictionary就可以了,像get方法一样简单
  • 支持磁盘缓存,而NSURLRequest仅支持内存缓存
  • 因为TTTableViewController + TTURLRequestModel的存在,搭配使用效果良好

在ios的开发中我有时也用到了OAuthConsumer进行oauth授权,利用OAuthConsumer的fetcher类请求远程数据,所以我想TTURLRequest和OAuthConsumer能不能搭配使用,这样就能用上three20相关便利方法加载远程数据。于是做了点简单的测试,果然成功了:)大体思路是:

  • 先利用OAuthConsumer根据提交的参数计算出oauth的Authorization认证头
  • 把Authorization头加到TTURLRequest
  • 由TTURLRequest提交数据。

直接上代码说话:

计算Authorization头

利用OAuthConsumer计算出Authorization头,为之后的请求做准备


//start
OAConsumer * consumer = [[OAConsumer alloc] initWithKey:yourConsumerKey secret:yourSecret];
NSURL * url = [NSURL URLWithString:@"http://your-api-host/your-method"];
OAToken * authToken = [[OAToken alloc] initWithKey:yourAuthToken secret:yourAuthSecrent];
OAMutableURLRequest * request = [[OAMutableURLRequest alloc] initWithURL:url 
                                                                consumer:consumer 
                                                                   token:authToken 
                                                                   realm:nil 
                                                       signatureProvider:[[[OAPlaintextSignatureProvider alloc] init] autorelease]];
[request setHTTPMethod:@"POST"];

NSMutableArray * params = [NSMutableArray array];
OARequestParameter * p1 = [[OARequestParameter alloc] initWithName:@"param1" value:@"i'm param1"];
[params addObject:p1];

[request setParameters:params];
[request prepare];

TTDINFO(@"Authorization is %@", [request valueForHTTPHeaderField:@"Authorization"]);

使用TTURLRequest请求数据

将Authorization头附加到TTURLRequest,然后请求远程接口


TTURLRequest* req = [TTURLRequest requestWithURL:request.URL.absoluteString delegate:self];
req.response = [[[TTURLDataResponse alloc] init] autorelease];
req.httpMethod = @"POST";
req.cachePolicy = TTURLRequestCachePolicyNone;
[req setValue:[request valueForHTTPHeaderField:@"Authorization"] forHTTPHeaderField:@"Authorization"];
[req send];

这个方法不需要对TTURLRequest进行修改,简单有效。

git flow使用经验小记

我在半年前开始在公司内推广使用git flow,控制版本发布流程,到目前为止效果令人满意。

但是实际使用过程中有一些小小的意外流程,完全照搬git flow的模型不太容易处理好。好在git本身就很灵活,碰到问题基本上都有办法绕过去。下面是我总结的一些特例情况下的处理办法。

git-flow

测试/共享单独一个feature

有时候我们需要将一个feature独立测试,或者share给多人一块开发,那么可以将这个feature推到远程git库上,这可以利用git flow的publish功能搞定:

git flow feature publish my_cool_feature

这会将 feature/my_cool_feature 分支push到远程git库,多人开发或者单独测试毫无压力。

feature在development分支测试完成,准备release的时候有另外一个未经测试的feature合并进来

已经完成测试的development被未经测试的提交污染了,这时候可以先本地回滚development分支,然后再进行git flow的release流程,例如:

git checkout development 
git reset --hard 5cbadfe885d1eb514b3f07b3f269ca1a7f261e21   #假设测试通过的git rev是这个
git flow release start v1.0.1
git flow release finish v1.0.1

development上有个feature需要测试比较长时间,影响了一些耗时较短的feature发布

development分支上有个feature测试时间比较长一直释放不了,怎么办?—— 果断采用hotfix功能

git br -m feature/another_cool_feature hotfix/another_cool_feature

把耗时短的feature直接转换为hotfix,然后采用git flow的hotfix流程可以直接合并到master分支发布。

一个关于three20开发框架的wiki

我最近花了不少时间做iphone上的app,由于是刚刚接触objective-c,需要从头学习的东西比较多。我从使用facebook开源的three20开发框架来学习ios应用开发,这种方式不能说是最好的学习方式,但是我也从中获得了不少乐趣。

关于three20的中文资料不是太多,所以我搜集整理翻译了一些内容,放到了wiki上:

three20  wiki

本人才疏学浅,翻译的内容难免出错或者词不达意,欢迎批评指正。

设置自动重连的ssh代理通道

我目前常用的翻墙办法就是拿ssh搭个代理通道,然后chrome + switch!插件一起配合,这就算翻墙了。这法子只要拿个机器跑一小脚本,比如:


ssh -D 7070 -qnN [username]@[server]

但是ssh通道如果闲置了一段时间,就会自动断连,等我需要用到代理的时候往往又得蛋疼的重新跑一遍,非常麻烦。所以我刻苦学习前辈的经验,找到一个解决办法,在mac或linux下都可使用,分享如下:

  • 把ssh配置为免密码登录,这个一搜一大把,略过不提
  • 在/etc/inittab的最后一行加上:
    
    tunl:345:respawn:/usr/bin/ssh -D 7070 -qnN [username]@[server] > /dev/null 2>&1
    
  • 让修改的inittab马上生效
    sudo init q
  • 在/root/.ssh/config里加上几行
    
    Host *
      ServerAliveInterval 60

然后这个ssh通道就会自动重连了。

Update

  • 增加了一个ssh配置,要不然这个进程虽然在,但是通道已经连不上了
  • .ssh/config的配置是关键,/etc/inittab的配置只是让服务器开机即启动ssh通道

code-prettify — wordpress语法高亮插件

最近基于google-code-prettify实现了一个代码高亮的wordpress插件 — code-pretttify,测试了下效果还不错,所以我马上把全站的代码高亮插件换成了这个。

这里是code-prettify的项目主页,我会把这个plugin的最新情况更新在这里。

code-prettify的特点

  • 完全由javascript完成代码高亮,不占用服务器资源
  • 使用简单,只要用<pre><code>和</code></pre>包住代码,发布即可,这是wordpress编辑器自带的quicktag。
  • 没有添加额外的hook,所以不会和别的wordpress插件产生冲突,绿色无污染
  • 载入速度很快,因为js是在页面尾部加载的,不会堵塞页面的载入!

代码高亮效果演示

eg:


<pre><code>
class BigBang {
    function blah() {
        echo "Hello ooso.net";
    }
}
</code></pre>

Effect:


class BigBang {
    function blah() {
        echo "Hello ooso.net";
    }
}

测试objective-c的code prettify效果

TTImageView *thumb = [[[TTImageView alloc] initWithFrame:CGRectMake(30, 30, 0, 0)] autorelease];
[thumb setAutoresizesToImage:YES];
[thumb setURL:@"http://farm4.static.flickr.com/3163/3110335722_7a906f9d8b_m.jpg"];
[self.view addSubview:thumb];

code-prettify下载

暂时把代码托管到github.com/volca/code-prettify,所以你可以直接使用git下载代码,或者直接从github下载现成的zip包。

代码非常简单,如果你有合适修正,可以试着用github推给我。

Update

另外安装了一个全新的wordpress测试,发现一些bug,因为wordpress默认对文本进行格式修正,在代码后面添加了很多br以及p标签,导致插件失效,所以我不得已用php替换了一些内容保证插件的正常运行。

如何让WPTouch和WP Super Cache同时工作

为了让blog在手机上看起来更舒服,所以我安装了一个wordpress插件叫WPTouch。安装成功之后的效果图如下:

wptouch

我原来还安装了一个叫WP Super Cache的插件,如果这个插件开启的话,在手机上就看不到上图的效果,应该是手机也访问了页面的缓存。简单的解决步骤如下:

    1. 登录到wordpress后台
    2. 选择设置 -> WP Super Cache
    3. 在Advance mode下勾选Mobile device support并保存
    4. 按照提示点击”Update Mod_Rewrite Rules”

这样就生效了。

BTW:以上步骤实际上是更新了文件.htaccess以及wp-content/wp-cache-config.php