Category Archives: Coding

ObjC中URL编码

NSString类已经中提供了现成的API:

/* Adds all percent escapes necessary to convert the receiver in to a legal URL string.
  Uses the given encoding to determine the correct percent escapes (returning nil if
the given encoding cannot encode a particular character).  See
CFURLCreateStringByAddingPercentEscapes in CFURL.h for more complex transformations
*/
- (NSString *)stringByAddingPercentEscapesUsingEncoding:(NSStringEncoding)enc;

但是非常重要的一点苹果没注明:该API并不会将”& + -”等需要转义的字符编码。
如若将需要传递的某个param的内容通过该API来转义就可能坑爹了!!你内容中的&=不会被转义,服务端就会把原来的内容根据&和=拆开成一片一片了!!

解决方法是使用介绍中提到的CFURLCreateStringByAddingPercentEscapes

@implementation NSString (URLEscaped)
- (NSString *)URLEscaped {
	CFStringRef escaped = CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)self, NULL, (CFStringRef)@"!*'();:@&=+$,/?%#[]",
			 kCFStringEncodingUTF8);
	NSString *out = [NSString stringWithString:(NSString *)escaped];
	CFRelease(escaped);
	return [[out copy] autorelease];
}

需要注意的是不能将这个函数用来 encode 整个URL,而是params的内容


写这篇文章是因为自己也掉了这个大坑,而且半年后才注意到。惨吖,不知道收集的10多w条数据中有多少是内容破坏的 – -

LessLyrics 重启

原谅4月份时候的大放厥词 “LessLyrics歌词秀后续计划

今天写这文章时才发现居然又过去了5个月。这几个月里辞去了百度的工作,和朋友们开始了全新的创业之路,断断续续的也就选择性的忘记了这个升级计划。

当然还有2个原因,计划的0.8版本中无缝滚动动画效果依赖非常精确的iTunes播放时间,但是能取到最小的间隔是1秒,所以处理起一些细节会比较麻烦。但是呢从当时的预览视频可以看到其实当时完成度已经很高了,在我开心的向datou炫耀这个超级华丽无缝歌词滚动动画效果,结果datou研究了很久后,说了句。不好。

虽然不愿意承认这么帅气的效果被否决,但仔细想确实有很大的问题。无缝滚动效果会导致整个界面一直处于动画中,当前高亮播放中的歌词也一直在逐渐向上滚动,会导致使用者为了fo这行歌词一直让眼球向上扫描,接着目光聚集到下面的歌词,然后又继续向上扫。看久了就会觉得挺累。

昨天在我们的app内测qq群中有用户问要显示歌词的软件。我赶紧推荐了LessLyrics,结果人家居然说,居然说,说:“这个app早就知道了!!我想要iPhone版本的”

于是我爽到现在,于是决定必须立刻重新启动LessLyrics软件计划了!

新的0.8版本没增加太多的功能,因为迫不及待要发布新版本收集下反馈。这次完全重写了app的设计结构和渲染模块(现在看1年前刚学习mac开发的代码真的很戳),接下来的添加新功能就方便了。

不用期待,几小时内发布。

哈哈哈哈哈哈哈哈哈哈哈哈哈哈。

下载请移步至 项目页面

CrazyText – My First MacRuby App

Crazy Text screenshot

前不久听说MacRuby 0.10 发布,并支持了Mac App Store 的发布.

于是边学习Ruby边学习MacRuby边学习cocoa 写了这么一个东西.

A MacOSX app written in Macruby to create some funny  effects for your texts.

提交时候提示MacRuby.framework内的几个link位置不对. 忘记什么提示了,需要手动修复几个link文件的地址.

对了1 :源码请移步 github-> https://github.com/xhan/CrazyText

对了2:

可以在appstore下载哦

部分UI仍然使用 objc编写(有些类和方法实在不知道如何用ruby实现)

个人感觉macruby 的学习曲线还真蛮高的,会cocoa,会ruby,还得会MacRuby,非常坑人,而且调试也非常又难度.

UIViewController 的内存管理

在iOS3.0后,UIViewController多了一个叫做viewDidUnLoad的方法.不少人都不清楚这个方法的具体意义,苹果的文档也就一句”Called when the controller’s view is released from memory” 简单的解释了下,并要求你把IBOutlet绑定的视图给清空,为什么呢?

先看下UIViewController从创建view到展示的流程的几个函数

-init
-initWithNibName:bundle:

这两个方法都是初始化一个vc,但请注意view不是这时候载入的

-loadView
-viewDidLoad

当一个视图准备展现时,vc首先会判断view是否已经创建,否则便通过之前指定的xib文件来初始化view,以及绑定其他关系(若没有指定xib文件,则默认会搜索和vc同名的xib,比如myNameViewController就会搜索 myNameViewController.xib文件)

若是没有xib文件,你就可以在loadview中自己手动创建这个viewControoler需要的视图.
接下来就是调用到 -viewDidLoad,许多人喜欢在这里做些其他事情,比如做个http请求,建立个数组啥的, 这里若不处理正确, -viewDidUnload 激活时内存就容易泄露了,稍后提到.

-view()appear
-view()disappear

这几个方法就不解释了

-viewDidUnload

该方法在收到内存警告,同时该视图并不在当前界面显示时候会被调用,此时该controller的view已经被释放并赋值为nil.
接下来你要做的是

  1. 把实例变量的子视图释放(IBOulet的,以及自己添加的),.
  2. 其他实例变量,比如之前在-viewDidLoaded中实例的数据数组,http请求释放掉.

因为当该viewController再次被激活准备显示时(比如navigationControler返回到上一级),vc发现自己的view为空后会重复之前的流程直到把view给创建起来,若没将自己额外添加的子视图,各种类实例变量释放,这里便会重新再次创建.

于是,内存泄露了.

Read UTF8 code at specify position from an NSString

如何获取nsstring制定某个位置的utf8编码的字符呢?

我也不知道,所以在stackoverflow问了下.

很快就有人回复了解决方案.

在贴代码之前写介绍下几个编码格式:

UTF8 和 UTF16均为字符编码方式.

UTF-8使用一至四個位元組為每個字符編碼:

  1. 128個US-ASCII字符只需一個位元組編碼(Unicode範圍由U+0000至U+007F)。
  2. 帶有附加符号拉丁文希臘文西里爾字母亞美尼亞語希伯來文阿拉伯文敘利亞文它拿字母則需要二個位元組編碼(Unicode範圍由U+0080至U+07FF)。
  3. 其他基本多文種平面(BMP)中的字元(這包含了大部分常用字)使用三個位元組編碼。
  4. 其他極少使用的Unicode 輔助平面的字元使用四位元組編碼。

UTF-16Unicode的其中一個使用方式。UTF是Unicode/UCS Transformation Format,即把Unicode轉做某種格式的意思。

其編碼方法是:

1如果字符編碼U小於0×10000,也就是十進制的0到65535之內,則直接使用兩字節表示;

2如果字符編碼U大於0×10000,由於UNICODE編碼範圍最大為0x10FFFF,從0×10000到0x10FFFF之間 共有0xFFFFF個編碼,也就是需要20個bit就可以標示這些編碼。用U’表示從0-0xFFFFF之間的值,將其前 10 bit作為高位和16 bit的數值0xD800進行 邏輯or 操作,將後10 bit作為低位和0xDC00做 邏輯or 操作,這樣組成的 4個byte就構成了U的編碼。

而NSString使用的就是unicode存储的, 唯一一个获取unichar的方法叫做 -characterAtIndex: , 但是问题是 unichar 其实是个 unsigned short,也就是2个字节,所以它并不能展现所有的字符.

终极解决方法:

@interface NSString (UTF8)
- (NSRange) rangeOfUTFCodePoint:(NSUInteger)number;
@end
@implementation NSString (UTF8)
- (NSRange) rangeOfUTFCodePoint:(NSUInteger)number
{
    if (number >= [self length]) {
        return NSMakeRange(NSNotFound, NSNotFound);
    }
    NSUInteger codeUnit = 0;
    NSRange result;
    for(NSUInteger ix = 0; ix <= number; ix++)
    {
        result = [self rangeOfComposedCharacterSequenceAtIndex:codeUnit];
        codeUnit += result.length;
    }
    return result;
}
@end

关于豆瓣电台osx版

突发奇想为什么不做个豆瓣电台的osx版本呢

于是就有了这个开源的项目 http://ixhan.com/project/douban-fm-osx/

后发现Du Song同学的FanRadio: Free Music For Mac 已做的无懈可击,同时twitter上朋友告诉我dashboard已经支持后台的音乐播放了。

有点多此一举的感觉。= =

不过个人还是喜欢一个原生的播放器浮在桌面上的感觉拉。

原计划0.2版本中会提供解析当前歌手名,歌曲名并可以同步更新至adium的状态,以及歌词显示功能。

坏消息是今天发现从flash中获取到内容相当困难。

好消息是通过抓包发现了豆瓣电台的接口,相当简单。可以做不少好玩的东西去。

栽大了之Objc过度释放对象

事情是这样的,4个月前我写了份实现类似下拉框选择操作的界面.
一个月前,发现了这个View 在dealloc 会crash掉.
多次调试无果,上 devForum.apple.com 询问也无人问津.
今天决定再次调试下,还是没找到哪里出问题.
最后决定求助 cocoachina 上的现场观众
最后开始一行一行的注释代码做终极调试.

离谱的事情发现了,只要我创建一个名为 mainText 的 UILabel ,在dealloc 中程序就会crash ,改成其他名字无事.难道是apple的bug? 新写了个view测试了,还是没问题.

接着接着就发现了在dealloc方法中:
[mainText release] ,mainText = nil;
// NSLog(@"release %@",bgView);
[bgView release];
// NSLog(@"release %@",originView);
[originView release];
// NSLog(@"release %@",labelArray);
[labelArray release];
// NSLog(@"release %@",mainText);
[mainText release];

该死的,不知道当时那根神经错了,居然释放了两次,当然出错咯.问题也解决了.

所以在确认释放对象的情况下一定要写成:
[instance release],instance = nil ;

个人经验是,对于objective-c的内存管理都是得经过磨练才出来的,前期多犯错误是好事情.
当然我认识一个朋友 ,他不释放任何对象,因为反正关闭iPhone后系统会处理的.这种觉悟不是大部分程序员有的,大家还是稳步前进咯.

游戏开发牛书推荐

在看一本叫 Programming Game AI By Examples 的书
(本来打算买实体书的,后来想想不能破例,还是down了本电子书 “)
看完第二章后彻底折服了 ,写的这么好的书真是不多见的
这才是真正的设计模式 ,寥寥几行代码就把整个世界给建模了
结构异常清晰,细节异常周到 堪称完美的作品
自己曾设计了不少不成型的东西,这次能见大师级别的代码确实收获颇多。
觉得一下子成长了不少。
而且最兴奋的是发现许多设计上的结构在许多地方都是雷同的
包括 Objective-c ,Box2d ,之前还一直觉得 box2d 使用起来还真是繁琐
创建一个对象还得先 ref 后才能创建

简单的说设计模式就是通过规范的设计和规范的代码(当然带来的结果是繁琐的代码会多了不少)带给清晰的结构和逻辑。在代码和功能越来越多时便显的尤其重要了。

在虚拟机里面把书上的例子全跑了一遍,决心花半年时间把所有东西消化,这些demo太吸引人了。

我的终极目标是做一个架构师,不只是用户,程序员也会使用我的产品。努力奋斗,会来的。:)

需要电子书的朋友网上自行搜索把,很多资源的,官网并提供源码和例程下载。

小试Unity游戏引擎

大四第一学期曾在学校的数字媒体实验室工作了很短时间,第一次看到现场手绘,maya做动画,zbrush绘制高模,倍感荣幸。

其中还有部分人在把弄一个游戏引擎(忘记叫什么名字了),可以让机器人在里面蹦蹦跳跳的走路。

那一刻让我想起第一次玩准3D游戏「生化危机II」的场景,震撼,鸡东的整个人在颤抖。

随着年龄的增长,对游戏的感觉也从狂热渐渐变成消磨时间,甚至忘记了小时候的梦想。

第一次游戏行业距离我这么近,却因为心有余而力不足放弃了。

回到标题上,自从 Unity 推出了免费版后,蠢蠢欲动,今天终于下载把玩了下。

一打开默认有个island的项目,场景做的很细腻,令我惊讶的是点击运行无需等待即可在场景中测试,很棒。

一遍看教程一遍开始了第一个测试项目。专业词汇挺多,没法像看程式书那样一下子就消化一堆。

最后还是不费力的做好了第一个场景,放了个对象在自己的小岛上跑了一圈,incredible! 简直想象不到眼前的居然是我做的。hohoho

暂时对Unity的感觉是,很简单,很高效,而且做出来的效果比我想象中好很多,顺便说说它使用的脚本语言 Unity JavaScript ,但是我怎么看也和我的老朋友 ActionScript3 更相似些 ,好处是可以少浪费时间看新语法书了。

附xhan’s 终极防御要塞。

bird-view

这里是更多图片

Read more »

让iPhoneApp发送带图片附件的邮件

更新(Nov 9,2009):

在3.0系统中,sdk 提供了 MFMailComposeViewController 来显示发件界面,并提供附件功能。

if ([MFMailComposeViewController canSendMail])
{
MFMailComposeViewController *mcvc = [[[MFMailComposeViewController alloc] init] autorelease];
mcvc.mailComposeDelegate = self;
[mcvc setSubject:@"Here's a great photo!"];
NSString *body = @”<h1>Check this out</h1><p>I selected this image from the <code><b>UIImagePickerController</b></code>.</p>”;
[mcvc setMessageBody:body isHTML:YES];
[mcvc addAttachmentData:UIImageJPEGRepresentation(image, 1.0f) mimeType:@"image/jpeg" fileName:@"pickerimage.jpg"];
[self presentModalViewController:mcvc animated:YES];
}

在程序中如何启动系统的Email程序并在内容中添加图片附件?
以下是代码:

- (NSString *) base64EncodingWithLineLength:(unsigned int) lineLength data:(NSData *)imgData {
static const char *encodingTable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const unsigned char *bytes = [imgData bytes];
NSMutableString *result = [NSMutableString stringWithCapacity:[imgData length]];
unsigned long ixtext = 0;
unsigned long lentext = [imgData length];
long ctremaining = 0;
unsigned char inbuf[3], outbuf[4];
short i = 0;
short charsonline = 0, ctcopy = 0;
unsigned long ix = 0;
while( YES ) {
ctremaining = lentext - ixtext;
if( ctremaining &lt;= 0 ) break;
for( i = 0; i &lt; 3; i++ ) {
ix = ixtext + i;
if( ix &lt; lentext ) inbuf[i] = bytes[ix]; 			else inbuf [i] = 0; 		} 		 		outbuf [0] = (inbuf [0] &amp; 0xFC) &gt;&gt; 2;
outbuf [1] = ((inbuf [0] &amp; 0x03) &lt;&lt; 4) | ((inbuf [1] &amp; 0xF0) &gt;&gt; 4);
outbuf [2] = ((inbuf [1] &amp; 0x0F) &lt;&lt; 2) | ((inbuf [2] &amp; 0xC0) &gt;&gt; 6);
outbuf [3] = inbuf [2] &amp; 0x3F;
ctcopy = 4;
switch( ctremaining ) {
case 1:
ctcopy = 2;
break;
case 2:
ctcopy = 3;
break;
}
for( i = 0; i &lt; ctcopy; i++ )
[result appendFormat:@"%c", encodingTable[outbuf[i]]];
for( i = ctcopy; i &lt; 4; i++ ) 			[result appendFormat:@"%c",'=']; 		 		ixtext += 3; 		charsonline += 4; 		 		if( lineLength &gt; 0 ) {
if (charsonline &gt;= lineLength) {
charsonline = 0;
[result appendString:@"\n"];
}
}
}
return result;
}
- (void) emailButtonPressed:(id)sender {
NSString *body = @"";
NSData *imageData = nil ;
NSString* dataStr = nil ;
for (PhotoItem* item in _photoBoardView.itemsSelected) {
imageData = UIImageJPEGRepresentation(item.photo.image.image,0.9);
dataStr = [self base64EncodingWithLineLength:0 data:imageData];
body = [body stringByAppendingFormat:@"<strong><img src="data:image/jpg;base64,%@" alt=" image" /></strong>",dataStr];
}
body = [body stringByAppendingString:@""];
NSString *encoded = [body stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *atitle = [[@"" stringByAppendingFormat:@"title: %@", @"Image "] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString * urlString = [@"" stringByAppendingFormat:@"mailto:%@?subject=%@&amp;body=%@", @"",atitle, encoded];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]];
}