office API sample 

Boolean CFReadStreamSetClient(CFReadStreamRef stream, CFOptionFlags streamEvents, CFReadStreamClientCallBack clientCB, CFStreamClientContext *clientContext);

callback function define 

typedef void (*CFReadStreamClientCallBack)(CFReadStreamRef stream, CFStreamEventType type, void *clientCallBackInfo);

how to use? 

1 define use typedef style 

void AFReadStreamCallBack

(

 CFReadStreamRef aStream,

 CFStreamEventType eventType,

 void* inClientInfo

 );

void AFReadStreamCallBack

(

 CFReadStreamRef aStream,

 CFStreamEventType eventType,

 void* inClientInfo

 )

{

//do something

}

2 setup callback 

    CFStreamClientContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};

    CFReadStreamSetClient(

                          stream,

                          kCFStreamEventNone|kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered|kCFStreamEventOpenCompleted,

                          AFReadStreamCallBack,

                          &context);

    CFReadStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);


design callback sample code


#import "AppDelegate.h"

typedef void (*PF)(int x);

void func(int x)

{

    NSLog(@"func x*x=%d",x*x);

    

}

void caller(PF pf,int a,int b)

{

    int x = a+b;

    pf(x);

}

@implementation AppDelegate


-(void)caller:(PF)pf a:(int)a b:(int) b

{

    int x = a+b;

    pf(x);

}

- (void)dealloc

{

    [super dealloc];

}


- (void)applicationDidFinishLaunching:(NSNotification *)aNotification

{

    PF p1  = func;

    caller(p1,9,10);

    [self caller:p1 a:199 b:8];

    // Insert code here to initialize your application

}


caller 实现私有,指定义callback样式,不实现,具体实现是使用时候根据业务逻辑实现

C 语言的callback 和ObjC里面的protocol 是不是十分类似?



Enhanced by Zemanta

国内App市场"烂透了"?

| No Comments | No TrackBacks
来自 http://www.it-times.com.cn/

  4月26日,世界知识产权日,所有登录网易邮箱的用户都会看到这样一幅画面:左边一个艺术家打扮的人画出了一只叼着橄榄枝的洁白鸽子,右边一个身上打着补丁、乞丐状的人边张望"艺术家"的作品,边在画布相同位置描出一只嘴里叼野花、眼歪鼻斜的企鹅。图上配文是:"与其山寨百分之一的灵感,不如复制百分之百的汗水"。看来,网易与腾讯之间围绕新闻客户端抄袭的"掐架"还会继续下去。
屏幕快照 2012-04-28 上午10.07.59.png 
  与国内形成鲜明对比的是,国外App业态健康,团队才13个人的图片分享应用Instagram被Facebook以10亿美元收购,私人图片分享服务Path估值也迅速上升到2.5亿美元,App开发是一门依靠创新能实现巨大利润的生意。"国内的创意型中小团队却不可能被大公司收购,后者反而会很快山寨一个类似应用,很快你就死了。"资深iOS程序开发者iApp4Me创始人郝培强表示。

  IT时报记者 忻云

  1 用户界面抄袭"分分钟搞定"

  如果你在对比最近涉嫌抄袭的两个客户端后发现外观"太像了",并且感到愤怒,那你一定是第一天踏入这个圈子的人。"微书架"App创始人许锡标对于这种UI(用户界面)设计雷同的现象早已见怪不怪。

  "人家设计师辛辛苦苦设计的图片,用不了半个小时,就可以被某些别有用心的人利用。"许锡标说,他以大热的美国应用Path为例,演示了十分简单的抄袭过程,下载Path,导出ipa文件,把ipa文件解压,就能看到Path所有的图片文件:"这些图片文件一般是png文件,直接打开是看不了的。这时,需要再下载一个修复png的软件,批量修复一下,就能看到所有图片文件。这样就把Path的整个UI给盗了。就这么简单。"

  易传媒移动互联网软件工程师小柯称,安卓应用的山寨更简单,将APK文件名改为RAR后解压,应用中的图片都在RES文件夹内,"连代码什么的都可以看到。"

  Fresh-Ideas工作室的开发者王均最近就认为其作品"全国空气污染指数"安卓版中一张原创图片被某公司改也不改地直接用于另一款应用。"那张坐标轴图片由多种色彩按特殊配色方案混搭而成,是我们团队反复琢磨的产物,相信天下不可能找到一模一样的。所以我很奇怪,下载对方应用打开图片文件夹后,发现根本就是用我的原始图片。"怪只怪,安卓的图片太容易复制。

  iApp4Me创始人郝培强说,就算初创设计者做一些加密也没有用,对方觉得你的UI好,完全可以将图片截屏。由此带来的"盗版"速度非常快,Instagram被Facebook收购后,图片分享类应用被大为看好,Path立即成为国内山寨的下一个热点,目前已有10多个类Path应用出现在国内市场。

  软件抄袭自PC时代就有,但在移动App上如此密集地发生,主要是屏幕相对缩小后,用户体验的要求更高,通常用户下载App后首先看"应用是否好看",这方面的重视度远超PC端。"在PC上即使一款软件操作起来不太舒服,用鼠标也比较容易克服,软件间的差别感受没那么大。但在手机屏上一个优秀的UI,一个舒服的按键位置对用户吸引力巨大,对复制者的诱惑也同样增长。"郝培强表示。

  

iOS Music app widget

| No Comments | No TrackBacks
iOS 5.0 增加 MPNowPlayingInfoCenter

Use a now playing info center to set now-playing information for media being played by your app.

The system displays now-playing information on the device lock screen and in the multimedia controls in the multitasking UI. If the user directs playback of your media to Apple TV via AirPlay, the now-playing information appears on the television screen. If the user connects a device to an iPod accessory, such as in a car, the accessory may display now-playing information. 

支持lock screen, AirPlay ,iPod accessory, 欢喜

看看sample code

-(void)updateInfoCenter:(UIImage*)cover

{

    MPNowPlayingInfoCenter *infoCenter=[MPNowPlayingInfoCenter defaultCenter];

    NSMutableDictionary *info=[[NSMutableDictionary alloc] initWithCapacity:2];

    [info setObject:currentSong.Artist forKey:MPMediaItemPropertyArtist];

    [info setObject:[currentSong nameStr] forKey:MPMediaItemPropertyTitle];

    MPMediaItemArtwork *itemwork=[[MPMediaItemArtwork alloc] initWithImage:cover];

    [info setObject:itemwork forKey:MPMediaItemPropertyArtwork];

    [infoCenter setNowPlayingInfo:info];

}

官方说明

Enriching Your App for AirPlay

Provide Audio Metadata

Your audio may be playing on a big-screen home theater system or on a sound system with an LCD display. Your app gives a better user experience if you provide metadata that can be shown on the AirPlay device's display, such as the artist name, song title, and album art.

Add metadata by passing a dictionary into the setNowPlayingInfo method of MPNowPlayingInfoCenter. The MPNowPlayingInfoCenter class is part of the MediaPlayerframework, but works with all playback frameworks, including MediaPlayerAVFoundation, and AudioQueue.

In addition to providing the usual song information strings, you should also pass in the playback rate, elapsed time, and media item duration. The playback device can use the duration and playback rate to create a progress bar. Update the elapsed time and playback rate whenever the playback rate changes.

看看效果,是不是比只是显示app的名字好多了。

IMG_0865.PNG

Enhanced by Zemanta

[推荐]学习Core Audio

| No Comments | No TrackBacks
Learning Core Audio.png
Audio can affect the human brain in the most powerful and profound ways. Using Apple's Core Audio, you can leverage all that power in your own Mac and iOS software, implementing features ranging from audio capture to real-time effects, MP3 playback to virtual instruments, web radio to VoIP support. The most sophisticated audio programming system ever created, Core Audio is not simple. In Learning Core Audio, top Mac programming author Chris Adamson and legendary Core Audio expert Kevin Avila fully explain this challenging framework, enabling experienced Mac or iOS programmers to make the most of it. In plain language, Adamson and Avila explain what Core Audio can do, how it works, and how it builds on the natural phenomena of sound and the human language of audio. Next, using crystal-clear code examples, they guide you through recording, playback, format conversion, Audio Units, 3D audio MIDI connectivity, and overcoming unique challenges of Core Audio programming for iOS. Coverage includes: mastering Core Audio's surprising style and conventions; recording and playback with Audio Queue; synthesizing audio; perform effects on audio streams; capturing from the mic; mixing multiple streams; managing file streams; converting formats; creating 3D positional audio; using Core MIDI on the Mac; leveraging your Cocoa and Objective-C expertise in Core Audio's C-based environment, and much more. When you've mastered the "black arts" of Core Audio, you can do some serious magic. This book will transform you from an acolyte into a true Core Audio wizard.

OS X 11E35 can't connect

| No Comments | No TrackBacks
[SSD-2:/var/log] yarshure% uname -a
Darwin SSD-2.local 11.4.0 Darwin Kernel Version 11.4.0: Tue Mar 27 17:20:01 PDT 2012; root:xnu-1699.26.7~1/RELEASE_X86_64 x86_64


Sat Mar 31 11:33:39 2012 : PPTP connecting to server 'mxxx.com' (ipaddr)...
Sat Mar 31 11:33:40 2012 : PPTP connection established.
Sat Mar 31 11:33:40 2012 : PPTP set port-mapping for en0, interface: 4, protocol: 0, privatePort: 0
Sat Mar 31 11:33:40 2012 : Using interface ppp0
Sat Mar 31 11:33:40 2012 : Connect: ppp0 <--> socket[34:17]
Sat Mar 31 11:33:40 2012 : PPTP port-mapping for en0, interfaceIndex: 0, Protocol: None, Private Port: 0, Public Address: 0, Public Port: 0, TTL: 0.
Sat Mar 31 11:33:40 2012 : PPTP port-mapping for en0 inconsistent. is Connected: 1, Previous interface: 4, Current interface 0
Sat Mar 31 11:34:10 2012 : PPTP error when reading socket : EOF
Sat Mar 31 11:34:10 2012 : PPTP error when reading header : read -1, expected 12 bytes
Sat Mar 31 11:34:10 2012 : PPTP hangup
Sat Mar 31 11:34:10 2012 : Connection terminated.
Sat Mar 31 11:34:10 2012 : PPTP clearing port-mapping for en0
Sat Mar 31 11:34:10 2012 : PPTP disconnecting...
Sat Mar 31 11:34:10 2012 : PPTP disconnected

iOSCon2012 会议讲稿

| No Comments | No TrackBacks

new iPad 牛排

| No Comments | No TrackBacks
毫无疑问new iPad 内存是1GB,但是我们怎么获取这个数值呢?
做iOS app开发的程序员,估计很少有人知道SYSCTL(8)
我们看看SYSCTL(8)是干什么的?

[SSD-2:~] yarshure% man sysctl
sysctl -- get or set kernel state
此处省略x 行
HISTORY
     sysctl first appeared in 4.4BSD.

BSD                             March 23, 2012                             BSD

输出保函
[SSD-2:~] yarshure% sysctl -a | cut -d. -f1|sort -u
appleprofile
audit
debug
hw 这个我们比较关系,硬件参数
kern
machdep
net
security
user
vfs
vm

hw.machine = iPad3,1 这个大家都知道
hw.model = J1AP 开发代号?
hw.ncpu = 2 我是双核的
hw.byteorder = 1234 CPU字节序
hw.physmem = 1035976704 真机内存 1035976704/1024/1024=987M 近似1G
hw.usermem = 907300864 用户态内存 907300864/1024/1024 = 865M 和上面差122M,这个是kernel 占用的?还是预留给GPU用的呢?
hw.pagesize = 4096  page 大小
hw.epoch = 1
hw.vectorunit = 0
hw.busfrequency = 250000000 238ZHz
hw.cpufrequency = 1000000000 1GHz
hw.cachelinesize = 32  不熟悉这
hw.l1icachesize = 32768 Gives the size in bytes of the processor's cache lines.
hw.l1dcachesize = 32768 L1 cache size
hw.l2settings = 1
hw.l2cachesize = 1048576 L2 cache size
hw.tbfrequency = 24000000 This gives the time base frequency used by the OS and is the basis of all timing services.
hw.memsize = 1035976704 ==hw.physmem 
hw.availcpu = 2 可使用的cpu数量

完善输出可以下载下面的文件

xnu 代码关于sysctl 的一些说明

转:http://www.dbanotes.net/review/China_Android_Market_UE.html

by Fenng@dbanotes.net

近一段时间在发布 丁香园用药助手 Android 版的过程中把国内几个重要的 Android Market 用了个遍,每次要发布新版本的时候都要感慨一下:几乎所有的 Android Market 后台的用户体验都不怎么好。

信息各有一套

国内所有的 Android Market 和 Google 官方 Android Market 都是不"兼容"的,无论是产品的描述信息以及应用类别划分,每一家都是自成一套。比如,软件截图,各有各自的要求,尺寸、格式如果不一致的话,还要针对性的单独人工处理,对开发者的工作量无形中增加了许多。对产品的描述也是千奇百怪,有的支持富文本编辑,有的只支持普通文本。有的更新软件要求写更新内容,有的则不提示填写,如果自己想写的话,需要修改整个 App 的描述信息。再比如分类信息,丁香园用药助手在有的 Market 上只能列入到「生活」类,而在另一个 Market 或许就要被迫列入「其他」,因为实在找不到和健康或是医疗相关的类目。

版本控制问题

只有少数一两家对版本控制还算有点意识,多数都没有相对靠谱的版本控制机制。有放任自流派:开发者在后台任意更新版本;也有关卡审核派:让你处于一个无法修改也无法撤销的"审核中"状态,一旦发现错误,想更正只能等下一个版本。至于审核周期,也是千奇百怪,有的立等可取,有的需要人工联系「我们发布了一个新版本,抽空给审核一下吧」,也有的长达一周。

后台可访问性

有的后台速度慢(这是很难让我想通的问题),甚至应用截图都不能正常显示;而上传的接口,也有很多细节问题,比较离谱的是有几家居然不提示上传进度,整个上传过程中只能凭感觉,等待,刚好丁香园用药助手的软件包还比较大,有的时候遇到传输中断,简直令人抓狂。

界面信息混乱

第一次注册后提交软件的时候要把整个流程跑通还是比较难的,提示和术语都要理解半天才知道是怎么回事,文案差异性太大。登录到后台后,一个典型的情况是多数 Market 从后台找不到发布后的应用在前台的链接,比如应用汇。而有的 Market 更加离谱的是,后台还是传统的表格形式的展示,比如魅族。

数据不够准确

几乎所有的 Android 市场,统计信息都不是特别准确,有些甚至下载统计数天都不更新,有的甚至后台就不做数据统计,下载多少要前台页面自己去看。想得到靠谱的 Android 下载数据,哦,你实际上得不到靠谱的 Android 下载数据,如果想统计应用打开的数据,最好是早点启用类似友盟这样的应用统计服务。

流程足够复杂

流程复杂这个主要是针对联通、移动、联想这些富有官僚气息的 Market 来说的,比如用户资质信息最多的有上百条信息要填写,你就折腾吧,没有几个小时,没有公司上下配合(还要营业执照副本什么的)你根本搞不定,在你提交应用之前你准会崩溃。所以,有些时候,对这样的市场不得不放弃,即使有用户真的要从这些渠道下载你的应用,也没办法。

结束语

其实,倒也不只是国内的 Android 市场对开发者的用户体验差,Google 官方的菜市场也不咋地。每天在网上看到网友数落这个网站用户体验差,那个网站用户体验差什么的,其实如果你去用一下这些Android软件市场,就知道用户体验差其实是没有底线的。

前几天参加移动开发者大会,发现 App Market 俨然已是各大互联网公司的标配,都在纷纷的推出自己的 Market ,恐怕以后还会更乱。据悉,已经有创业团队在开发一次性提交到多个 Market 的工具了,不知道什么时候能看到。很明显,这也是吃力不讨好的事儿。

也可能是每家 Android Market 都在拼前台的用户体验呢吧,真心期待国内 Android Market 能早日关注一下针对开发者和维护者的用户体验问题,这也是每个 Android 开发者期待的,让开发者有更多精力做应该做的事情。

--EOF--

Instagram 架构分析笔记

| No Comments | No TrackBacks

转 http://www.dbanotes.net/arch/instagram.html

by Fenng@dbanotes.net

Instagram 团队上个月才迎来第 7 名员工,是的,7个人的团队。作为 iPhone 上最火爆的图片类工具,instagram 用户数量已经超过 1400 万,图片数量超过 1.5 亿张。不得不说,这真他妈是个业界奇迹。

几天前,只有三个人的 Instagram 工程师团队发布了一篇文章:What Powers Instagram: Hundreds of Instances, Dozens of Technologies,披露了 Instagram 架构的一些信息,足够勾起大多数人的好奇心。读罢做点笔记,各种线索还是有一定参考价值的。能打开原文的建议直接读原文。

Instragram.png

Instagram 开发团队奉行的三个核心原则:

  • Keep it very simple (极简主义)
  • Don't re-invent the wheel (不重复发明轮子)
  • Go with proven and solid technologies when you can(能用就用靠谱的技术)

OS/主机

操作系统的选择,在Amazon EC2上跑 Ubuntu Linux 11.04 (Natty Narwhal) ,这个版本经过验证在 EC2 上够稳定。因为只有三名工程师,只有三名工程师,所以自己部署机器到 IDC 是不靠谱的事情。幸好有亚马逊。

负载均衡

此前曾用过两台 Nginx 做 DNS 轮询承载前端请求,这样做会有副作用,现在已经迁移到Amazon的ELB(Elastic Load Balancer),起了三个 Nginx 实例,在 ELB 层停掉了 SSL , 以缓解 CPU 压力。DNS 服务使用 Amazon Route53 服务。

应用服务器

启用了 25 个 Django 实例,运行在 High-CPU Extra-Large 类型的服务器实例上,之所以用 High-CPU Extra-Large 实例是因为应用请求是 CPU 密集型而非 IO 密集型。

使用 Gunicorn 作为 WSGI 服务器。过去曾用过 Apache 下的 mod_wsgi 模块,不过发现 Gunicorn 更容易配置并且节省 CPU 资源。使用 Fabric 加速部署。

数据存储

用户信息、图片元数据、标签等大部分数据存储在 PostgreSQL 中。主要的 Shard 数据库集群有 12个节点。

实践中发现 Amazon 的网络磁盘系统单位时间内寻道能力不行,所以有必要将数据尽量放到内存中。创建了软 RAID 以提升 IO 能力,使用的 Mdadm 工具进行 RAID 管理。

管理内存中的数据,vmtouch 这个小工具值得推荐。

PostgreSQL 设置为 Master-Replica 方式,流复制模式。利用 EBS 的快照进行数据库备份。使用 XFS 文件系统,以便和快照服务充分配合。 使用 repmgr 这个小工具做 PostgreSQL 复制管理器器。

连接池管理,用了 PgbouncerChristophe Pettus 的文章包含了不少 PostgreSQL 数据库的信息。

TB 级别的海量图片存储在 Amazon S3 上,CDN 采用的也是 Amazon 的服务,CloudFront。

Instagram 也是 Redis 的重度用户,Feed 以及 Session 信息都用 Redis 处理,Redis 也是以 Master-Replica 方式部署。在 Replica 节点上进行数据备份。

使用了 Apache Solr 承担 Geo-search API 的工作,Solr 简单的 JSON 接口也不错。

缓存使用了 6 个 Memcached 实例,库使用 pylibmc 和 libmemcached。亚马逊也提供缓存服务-Elastic Cache service ,Instagram 也有尝试,不过不便宜。

任务队列/发布通知

队列服务使用 Gearman ,通知系统则使用 pyapns 来实现。

监控

前面提及的服务器实例数量加起来,的确有100多个,有效的监控是相当有必要的。使用 Munin 作为主要监控工具 , 也写了不少定制插件,外部监控用 Pingdom 的服务。通知服务使用 PagerDuty

对于 Python 的错误报告,使用 Disqus 团队开源的 Sentry 来处理。

几个感想

0)轻装上阵说起来容易,做起来非常难。这也是 Instagram 团队目前最令人着迷的地方;

1)Python 社区已经足够成熟,各个环节上都已经有不错的解决方案了。

2)如果要问我最大的一个感慨,我要说:Amazon 真是一家伟大的公司,甚至比 Google 还伟大

--EOF--


Enhanced by Zemanta

NSString 和hex code 互转

| No Comments | No TrackBacks

NSString *artist=@"王杰";

NSString *song=@"什么时候才能够";


转换后

Artist=8B737067&Title=C04E484EF66519504D62FD801F59

算法

-(NSString*)toUnichar:(NSString*)src

{

    NSMutableString *arX=[NSMutableString string];

    for (int i = 0 ; i<[src length]; i++) {

        unichar ch= [src characterAtIndex:i];

        unichar b = ((((unsigned long)(ch) & 0xff00) >> 8) | (((unsigned long)(ch) & 0x00ff) << 8));

        [arX appendFormat:@"%X",b];

    }

    return arX;

}

反转

NSString *str=@"32003000310032003C68B1838E7FF397504E27595659ED70E8954C6BF266A86350830A00ED70E8950F5CF48B92634C889C69"

2012格莱美音乐大奖热门歌曲推荐


算法

-(NSString*)toString:(NSString*)uniStr

{

    NSMutableString *outStr = [NSMutableString string];

    int len=[uniStr length]/4;

    unichar *oux=alloca(len*sizeof(unichar));

    unichar *p = oux;

   

    for (int i=0; i<[uniStr length]/4; i++) {

        

        NSString *str=[uniStr substringWithRange:NSMakeRange(i*4, 4)];

        

        unichar a =toInde([str characterAtIndex:0]);//3 3c

        unichar b =toInde([str characterAtIndex:1]);//2

        unichar c =toInde([str characterAtIndex:2]) ;//0 68

        unichar d =toInde([str characterAtIndex:3]);//0

        

        

        unichar x = c*16*16*16+d*16*16+ a*16+b;//十进制 3c68->683c

        

        memcpy(p, &x, 2);

        printf("%c,%x",*p,x);

        p++;

        

    }

    NSString *x=[[NSString alloc] initWithCharacters:oux length:len];

    

    return outStr;

}

int toInde(unichar x)

{

    int i;

    

    switch (x) {

        case 48:

            i=0;

            break;

        case 49:

            i=1;

            break;

        case 50:

            i=2;

            break;

        case 51:

            i=3;

            break;

        case 52:

            i=4;

            break;

        case 53:

            i=5;

            break;

        case 54:

            i=6;

            break;

        case 55:

            i=7;

            break;

        case 56:

            i=8;

            break;

        case 57:

            i=9;

            break;

        case 65:

            i=10;

            break;

        case 66:

            i=11;

            break;

        case 67:

            i=12;

            break;

        case 68:

            i=13;

            break;

        case 69:

            i=14;

            break;

        case 70:

            i=15;

            break;

        default:

            break;

    }

    return  i;

}



好久不写C,后面的的函数写的比较...

再奉送一个将NSString 转换成UTF8 str

-(NSString*)toUtf8Char:(NSString*)src

{

    

    NSMutableString *arX=[NSMutableString string];

    NSData *data=[src dataUsingEncoding:NSUTF8StringEncoding];

    const char *p= [data bytes];

    for (int i = 0 ; i<[data length]; i++) {

        Byte ch = *p;

        NSLog(@"%02X,%X",*p,ch);

        p++;


        [arX appendFormat:@"%X",ch];

    }

    return arX;

}

@"梁静茹丝路"转换后为

"E6A281 E99D99 E88CB9E4B89DE8B7AF"
顺便推荐一款内码查询器UnicodeChecker,官方网站 http://earthlingsoft.net
Enhanced by Zemanta

about TableView in Mac OS X

| No Comments | No TrackBacks
问题来源 Mac OS X 直到Lion 才开始支持View base TableView
 那么如果应用程序要支持10.6-,是绝对不可以使用官方的API.那么要快速实现TableView Cell自定义非常复杂。

可选方案: 1 IKImageBrowserView/IKImageBrowserCell 自定义显示比较麻烦,苹果就没想这个事情
     2 NSCollectionView/NSCollectionViewItem 同上,自定义显示不够友好

官方没有合适的,自己想偷懒,那么找一找,是不是有开源项目呢?

2 PXListView  https://github.com/Perspx/PXListView  http://perspx.com/archives/making-list-views-really-fast/ 作者关于List view 的思考
3 TwUI, Twitter on Mac https://github.com/twitter/twui  这个不介绍了,看github介绍吧
4 others may be

后续:
TwUI 使用下来不错,但是实践中遇到TUITextField 输入框和中文输入法不兼容
被迫创建多个TUINSView, 结构上感觉不是很干净,没办法,不想hack TwUI输入发兼容问题。


转一篇
原文 http://www.xuzhe.com/?p=601
首先,我说的是 iOS 开发,不是 Mac OS。

其次,这次解决的三个 BAD_ACCESS 都是由于 iOS 程序在收到 Memory Warning 后,非当前 UIViewController 执行 viewDidUnload 后出现的问题。

这类崩溃在真机上比较难测,因为是随机出现的,而且看 Crash Log 经常会看不出是哪行代码 BAD_ACCESS 了。所以这就需要善用模拟器提供的 "Hardware -> Simulate Memory Warning" 功能了。

同时为了能够方便的 Debug 出僵尸指针是谁,我在 Xcode 里加入以下三个环境变量:

NSZombieEnabled NSAutoreleaseFreedObjectCheckEnabled NSDebugEnabled

Xcode 4 的话编辑一下 Scheme,加到 Environment Variables 里即可(如果以前没加过的话)。

做完准备工作就能比较方便的进行 Debug 了。可以每切换一次 UIViewController 就模拟一次内存警告,看会不会崩溃。

接下来说说我这次遇到的三个问题。
1、一个低级错误

由于我们有时会几个人同时改一个文件,结果就发生了A用完一个实例以后顺手给释放掉了,但是却把B写在 viewDidUnload 里的代码给留下了。自然也就过度释放了。

这类过度释放的错误比较容易发现,只要改代码的时候稍微在原有文件里做一下搜索就能避免。

2、关于 Interface Builder 里的实例生命周期有多长的问题。

开发过 Mac OS 应用的同学都知道在开发 Mac 应用的时候,IB 里的实例 IBOutlet 到代码里是不用 retain 的。但是在 iOS 里却是要 retain 的。

这有什么区别呢?

如果你用的是 UIView 的话,的确,在 iOS 里不做 retain 也不要紧。因为 UIViewController 里的 UIView 本质上都是被 addSubview 到别的 UIView(UIWindow)里去的。但如果你在 IB 里放的是一个 NSObject,问题就出来了。

这也就是我遇到的问题。iOS 会把这个 NSObject 释放掉!

找这个问题花了我不少功夫,因为这个 NSObject 我只是在 IB 里连了连线,压根儿就没在代码里出现......解决方法其实很简单,在代码里加上

@property (nonatomic, retain) NSObject *someObject;

就好了(注意内存释放)。

3、BCTabBarController 带来的噩梦

在解决了以上问题后,整个程序仍然在满世界地不停的崩溃。这让我也很崩溃。咱好歹也算对 iOS 内存管理略有心得,搞个程序崩成这样实在是说不过去。

继续找下去发现问题出在一个第三方的开源控件:BCTabBarController 上。

用一个水准不高的第三方控件的结果就是给开发带来一定方便的同时,也带来了更多的麻烦(我不是在指责 BCTabBarController 的水准,因为要在苹果有限的公开 API 上完全自己来模拟原生 UITabBarController 的行为,的确是有难度的)。

这次的问题出在 BC 调用其管理的 UIViewController 的 viewWillAppear: 函数时,忽略了该 UIViewController 可能已经 viewDidUnload 过了。一上来就直接调用 viewWillAppear: ,结果当然不是崩掉就是逻辑乱掉。

解决方法是在 BC 调用 [viewController viewWillAppear:NO]; 之前加一句 [viewController view]; ,确保 viewDidLoad 先于 viewWillAppear: 被调用即可。

注:loadView 和 viewDidLoad 都不应该自己直接去调用,用 [viewController view]; 这种方法来让 view 自行载入的好处是,如果 view 已经被载入了,这一步操作不会让所有 view 再被载入一次。

现已发现的问题就是这么多,要在 iOS 上写个没有内存泄露又不会崩溃的程序实在是有难度。
SSD:~ yarshure$ ps -ef|grep AirPlay|grep -v  grep|awk '{print $2}'|xargs lsof -p |grep IPv4
?\x9e?  342 yarshure   16u    IPv4 0xffffff8016b0c320       0t0      TCP 192.168.69.56:49883->114.80.128.121:http (ESTABLISHED)
?\x9e?  342 yarshure   19u    IPv4 0xffffff8020d9ec00       0t0      TCP 192.168.69.56:50005->58.215.45.192:http (ESTABLISHED)
?\x9e?  342 yarshure   21u    IPv4 0xffffff8020d9fa40       0t0      TCP 192.168.69.56:49994->114.80.128.121:http (ESTABLISHED)
?\x9e?  342 yarshure   23u    IPv4 0xffffff8026da8de0       0t0      TCP 192.168.69.56:50008->114.80.128.121:http (ESTABLISHED)
?\x9e?  342 yarshure   24u    IPv4 0xffffff8020da16c0       0t0      TCP 192.168.69.56:50009->58.215.45.192:http (ESTABLISHED)



截图就不更新了,体验的赶紧下载尝鲜吧。

AirPlay.png
AirPlay.zip  下载链接,点击前面的那个
1 支持专辑和Artist 图片效果
2 目前只用pre, pause,n 操作
3 显示当前播的音乐专辑,演唱者,歌曲名字
4 目前不支持切换电台,下周会实现
   可以手动切电台http://restful.airplayme.com/radios/selected.json 查看电台id
   然后手动替换 defaultstation.plist 文件中id 项目,重启即可

2011.12.06 晚上版本
preview.png
下载地址:


1 界面美化
2 简单指出选电台
2011.12.07 更新
1 优化版本 胶片效果完善
2 修改启动时不显示界面问题

channel.png


早期功能版本
1 频道选择
2 歌曲/艺人/专辑搜索
3 歌词显示
4 seek 功能

airplay_func_ver.png



Enhanced by Zemanta

无题

| No Comments | No TrackBacks
晚上和哥哥姐姐在IM上聊了很多
总想写很多事情,但是不知道如何写起。

将来的某一天会下吧。

UsingBlocksAsContextInfo - Demonstrates how to implement Objective-C "blocks" passed in as the 'contextInfo' to NSAlert, helping to handle the alert result.


// This is a quick tip on how to use the context info as a block parameter.

// You can use this type of pattern for any methods that have a delegate/selector/contextInfo pattern.

- (void)btnShowAlertClicked:(id)sender  {

    NSAlert *alert = [NSAlert alertWithMessageText:@"Alert Message"

                                     defaultButton:@"Default Button"

                                   alternateButton:@"Alternate Button"

                                       otherButton:@"Other Button"

                         informativeTextWithFormat:@"Informative Text"];

    

    BOOL someLocalVariable = YES;

    

    // We create a block that can easily access local variables to this method.

    // This is much easier than trying to package them all up into a contextInfo object

    void (^blockCallback)(NSInteger) = ^(NSInteger returnCode) {

        // Inside the block callback we can easily access locals

        if (someLocalVariable) {

            if (returnCode == NSAlertDefaultReturn) {

                [button setTitle:@"Default Return Button Clicked!"];

            } else {

                [button setTitle:@"Something else clicked...try again."];

            }

        }

    };

    

    // We copy the block, since it needs to stay alive for longer than the current scope

    [alert beginSheetModalForWindow:self.window

                      modalDelegate:[self class]

                     didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:)

                        contextInfo:Block_copy(blockCallback)];

}


+ (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void (^)(NSInteger returnCode))continuationHandler {

    continuationHandler(returnCode);

    // The block must always be retained before the first call. This is the matching release

    Block_release(continuationHandler);

}


| No Comments | No TrackBacks

static void _processData(AppDelegate *self, NSInteger i) {

    // Notice that the _window can be accessed here, even though it is a private ivar

    // A compiler error (or warning) will happen if this method was outside the @implementation scope of AppDelegate.

    NSLog(@"Processing %d in window %@", i, self->_window);

}


- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {

    for (NSInteger i = 0; i < 200; i++) {

        _processData(self, i);

    }

}

@金色

| No Comments | No TrackBacks
kong_hometown.JPG
@yarshure 2011.09.19 下午  

AppKit 一点笔记(三)

| No Comments | No TrackBacks
程序如何运行?
  入口main函数
  
#import <Cocoa/Cocoa.h>
 
int main(int argc, char *argv[])
{
    return NSApplicationMain(argc,  (const char **) argv);
}
  
应用程序主进程开启
 通过Info.plist 查找 NSMainNibFile,并load之
NSMainNibFile 文件Load完成后,下一步NibFile中"File's owner' 回调用awakeFromNib
10.6 之后支持[super awakeFromNib]

程序启动完成

程序状态管理和维护通过

NSApplication,NSApplicationDelegate 

NSNibLoading,单独load

使用NSBundle 3个方法

  

+ (BOOL)loadNibFile:(NSString *)fileName externalNameTable:(NSDictionary *)context withZone:(NSZone *)zone;

+ (BOOL)loadNibNamed:(NSString *)nibName owner:(id)owner;

- (BOOL)loadNibFile:(NSString *)fileName externalNameTable:(NSDictionary *)context withZone:(NSZone *)zone;


看起来第二个更简单些

  


另外Document base Application NSDocument 类可以使用

- (NSString *)windowNibName;方法加载目标Nib文件


NSWindowController 同样可以使用上面的这个方法


AppKit 一点笔记(二)

| No Comments | No TrackBacks
1 Dock Menu 效果如图
dock_menu.png
实现方法

NSApplicationDelegate

实现

- (NSMenu *)applicationDockMenu:(NSApplication *)sender

{

    return _menu;

}

2 状态条菜单

  需要NSMenuDelegate protocol

@protocol NSMenuDelegate <NSObject>

@optional

- (void)menuNeedsUpdate:(NSMenu*)menu;


- (NSInteger)numberOfItemsInMenu:(NSMenu*)menu;

- (BOOL)menu:(NSMenu*)menu updateItem:(NSMenuItem*)item atIndex:(NSInteger)index shouldCancel:(BOOL)shouldCancel;

- (void)menuWillOpen:(NSMenu *)menu;

- (void)menuDidClose:(NSMenu *)menu;

- (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item;

@end


statbar_menu.png

StatusBar加Icon

- (void)activateStatusMenu:(int)status

{

    NSStatusBar *bar = [NSStatusBar systemStatusBar];

    icon = [bar statusItemWithLength:NSVariableStatusItemLength];

    [icon retain];

NSString* filePath;

switch (status) {

case 0:

filePath = [[NSBundle mainBundle] pathForResource:@"sync_complete" ofType:@"png"];

break;

case 1:

filePath = [[NSBundle mainBundle] pathForResource:@"sync_processing" ofType:@"png"];

break;

case 2:

filePath = [[NSBundle mainBundle] pathForResource:@"sync_error" ofType:@"png"];

break;

default:

break;

}

NSImage* image = [[NSImage alloc] initWithContentsOfFile:filePath];

[icon setImage:image];

[icon setHighlightMode:YES];

[icon setMenu:mainMenu];

}


关于菜单就讲这么多

Find recent content on the main index or look in the archives to find all content.

Recent Comments

  • 1o1efdgdg: 初来乍到,请多多关照。 read more
  • Cisbye Xue: 树欲静而风不止,子欲养而亲不待。 节哀! read more
  • kevin: 楼主,可否给个联系方式,有问题请教你... 我按照你blog给出的命令 : libtool -dynamic -arch_only armv6 -syslibroot [file] -L read more
  • 孔祥波: 1,2,3已经称为过去时 4,5有待继续 6 零花钱是有了 7 iPad2,iPhone4,kindle3都有了 下半年要的计划呢? read more
  • iamdaiyuan: 节哀。好好珍惜身边的人吧 read more
  • xian.suzhou: 另外,我是苏州大学的一名老师,我们现在也在进行iphone输入法的开发。看到前面陈心涛同学给你的留言,想和他讨论一些输入法的问题,是否能提供他的邮箱地址?谢谢了! read more
  • xian.suzhou: 不知楼主会不会看到这个留言了,呵呵,我也想要一个例子,能发发给我一份,谢谢了! read more
  • iphonemaker: 具体怎么使用,能说说嘛,最好给个demo事例,谢谢。 qq:37079765 read more
  • dbanotes: 电子设备都有啥?要不帮我做点零活儿,我帮你解决掉好了 read more
  • meggy.meng: 恩 是需要做点计划 read more

Recent Assets

  • IMG_0865.PNG
  • Learning Core Audio.png
  • channel.png
  • airplay_func_ver.png
  • preview.png
  • AirPlay.png
  • kong_hometown.JPG
  • statbar_menu.png
  • dock_menu.png
  • HK_Plazza.jpg

页面

OpenID accepted here Learn more about OpenID
Powered by Movable Type 4.37