新坑

Windows Phone上打谱软件的坑挖好了,正在犹豫是否要填。

SGF解析部分来自于Super Go。昨天改掉了一些问题,但更大问题是Super Go实际上是对战类型,这个SGF解析只有最简单的功能。而作为打谱软件,要具备以下功能才行:

1.标记,例如下面这些(图片来自于http://senseis.xmp.net/?HowDiagramsWork):

2.分支。在棋局、死活题、变化等讲解时非常有用。
3.交互。做题时候可以根据落子不同进行解说。就像EidoGo那样。
4.棋谱库。维护棋谱,并可以做出修改。

简而言之就是尽量多支持sgf的Tag。另外本来想直接沿用Super Go的各种数据结构,但一来为了支持上面这些功能,二来如果都用了人家的自己就写点UI算什么?所以重写ing。现在大概就是这样:

如果决定填坑的话,保守估计一个月的业余时间要搭进去。

p.s:著名的XGoo(在移动平台的知名度不亚于Stonebase之于PC)最近出了iOS版本!这是个值得让人去买个pad的软件,不夸张。

让AudioPlayerAgent支持动态播放列表(Windows Phone)

最近做的是有关背景音乐播放(调用系统播放器)的活儿,其中牵扯到Microsoft.Phone.BackgroundAudio.BackgroundAudioPlayer播放本地或远程音频时候的播放列表改动问题。如果希望播放系统Music + Videos中存储的音乐,那么直接在UI利用Microsoft.Xna.Framework.Media传递和播放音乐即可。但如果是BackgroundAudioPlayer和AudioPlayerAgent就不太好办。

可能有人会说这种问题还用得着单写一篇文章吗,AudioPlayerAgent当然支持动态列表了。但很可惜MSDN的官方How-To的例子就是硬编码的列表(还是本地音频),不然也不会在App Hub Forum上出现这样的问题(12)。

一般来说将播放列表传递给Agent有这几种方式:

  • IsolatedStorageSettings
  • 静态方法或属性(作为BackgroundAgent,实例化它没有意义)
  • IsolatedStorageFile

实验之后就会发现,前两种方法并不好用:虽然在UI线程中设置了列表或者通过静态方法、属性传递了列表,但启动后的Agent拿不到这些数据。就算是单写一个存储播放列表数据的静态类也是一样。那么留给我们的方法只有通过文件传递。

上面提到的那个帖子中,一位微软员工Mark Chamberlain给出的方案也是如此(全文请看原帖):

This is how I did it:
In background audio agent code:
OnUserAction.Play/SkipNext/SkipPrevious: Check if BackgroundAudioPlayer.Instance.Track == null, if yes then load playlist from IsoStorage into m_playlist and start playing first track, if no then play current/next/previous song from m_playlist
In the album viewmodel code (each time a user selects an album a new viewmodel gets created for this app):
OnInitialization:…
Another developer who reviewed this suggested that instead of calling ..Instance.Close(), which also releases all the background audio resources, a better approach is to set BackgroundAudioPlayer.Instance.Track = null, which stops and clears, but keeps the background resources allocated.

基本思路即UI存好文件,Agent初始化时候载入。任何和播放相关操作前播放列表如果为null则再次载入。

具体到我手头的例子,文件格式我直接用的Xml序列化,写起来简单。由于需要和UI保持实时,那么每修改一次播放列表(添加、删除、改动)都要保存,同时在Agent一边,任何和播放相关操作前则不仅检查是否列表为null,而是直接全部重新载入。类似于:

protected override void OnUserAction(BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param) {
    var allTracks = TrackManager.GetAllTracks();
    _playList = new List();
    foreach ( TrackItem trackItem in allTracks ) {
    _playList.Add( 
            new AudioTrack( null, trackItem.Title, trackItem.Album, null, null, trackItem.Url, EnabledPlayerControls.Pause ) );
    }
    //...
}

代码写出来非常愚蠢,但目前为止找不到更好的方法。

有关dynamic_cast<>的可用性(基础差害死人)

一直以来,有关dynamic_cast<>这个转换所读到的书和材料消化后,都觉得结论是“拥有带有继承关系的对象指针转换,安全,会抛异常”。但事实上真的如此吗?看下面的代码:

class B1{virtual void foo(){}};
class B2{};
class D{};
D* pD=dynamic_cast(new B1()); // (1)
cout<<*(int*)(&pD)<(new B2()); // (2)
cout<<*(int*)(&pD2)<

可以看到B1、B2和D三个类没什么继承关系,但(1)处通过编译,(2)处提示:

cannot dynamic_cast `(((main()::B2*)operator new(1u)), (((*)  main()::B2()), ))' (of type `class main()::B2*') to type `class main()::D*' (source type is not polymorphic) 

即来源类型非多态。所以实际上并非是“有继承关系的对象指针”可用dynamic_cast<>转换,而是有vtable的类都可以转——只不过没继承关系的话转出来为NULL。
那么再想一下,既然提示了“多态”,也就是说关键点不在“继承”这里,所以形如下面的代码:

class X {};
class Y : X {};
Y* y = dynamic_cast(new X()); // failed!

虽然两个类有继承关系,但必然通不过编译,因为基类子类并未表现出多态,编译器不会多费劲来生成vtable。在文章多继承的虚指针中提到了dynamic_cast的内部实现:

void *FindCompleteObject (void *ptr )
{
     const _s_RTTICompleteObjectLocator *pCompleteLocator =GetCompleteObjectLocator (ptr );
     ptr =ptr -pCompleteLocator ->offset ;
     if (pCompleteLocator ->cdOffset ) ptr -=*(ptr -pCompleteLocator ->cdOffset );
     return ptr ;
}
void *__RTCastToVoid (void *ptr )
{
     if (!ptr ) return NULL ;
     return FindCompleteObject (ptr );
}

可以看到只有vtable存在,寻找Complete Object Locator的行为才有意义——虚表的第一项就是RTTI Complete Object Locator(RTTI Info),其中包含RTTI类型描述(Type Descriptor)、RTTI继承关系(Class Hierarchy)[2]。故言C++的RTTI特性也是以vtable的存在为基础,没有虚函数或其他virtual特性,RTTI不生效。
当然一般使用中其实理解成dynamic_cast<>用于向下转换带有继承关系的指针也没错,上面提到的这两个特例应该很少会出现在实际代码中吧。但此处犯错足见俺基础太差,离C++对象模型的深入理解还差得远。
p.s:真想把之前这类因为基础差犯2B错误的文章集结起来。最近几个月就写了不少。

参考资料

  1. 多继承的虚指针,lw02nju
  2. 浅议 Dynamic_cast 和 RTTI,RocZhang

Objective-C学习笔记(4-7)

目录

  1. 构造和拷贝构造
  2. 动态特性和RTTI
  3. 内存管理和标准C区别
  4. 监视方式
  5. 数据持久化
  6. RTTI和动态特性2
  7. 容器类、Block和NSPredicate

04监视方式

Protocol

用protocol实现的delegate。其实我觉得这个就是观察者,只不过默认情况下是单一的观察者。具体方法不总结了。

有什么方法可以一次性存放多个delegate?这样就可以用数组方法一口气通知完:

[array makeObjectsPerformSelector:@selector(doSomething:) withObject:aObject];

如果写N个变量第一比较罗嗦第二也不知道运行时会有多少个观察者。但问题在于NSArray和NSMutableArray不能存指针之外的对象,protocol实例不认。

KVO

KVO,也就是Key-Value Observer,是除了protocol和notification之外的又一种属性监视方式

监视别人的类重载一个方法,用于观察值变化

- (void) observeValueForKeyPath:(NSString*)aPath ofObject:(id)anObject change:(NSDictionary*)aChange context:(void*)aContext {
           NSLog( @"%@ has observered key %@ change in %@, from %@ to %@",
                [self class], aPath, anObject, [aChange objectForKey:@"old"], [aChange objectForKey:@"new"] );
}

被监视的类不用特意修改。

发送消息时,需要用NSObject里面某Category已经定义好的add/removeObserver增删observer即可

Coder* me = [Coder personWithName:@"Si Wei" andAge:25];
    [me setLanguage:@"C#"];
    Computer* computer = [[Computer alloc] init];
    [computer addObserver:me forKeyPath:@"workload" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionNew context:nil];
    [computer setWorkload:10];
    [computer setWorkload:50];
    [computer removeObserver:me forKeyPath:@"workload"];
    [computer release]; Read more >>>

微软的Playful Programming??

这几天在刷PSV奖杯,结果看到这么个东西:微软的Visual Studio Achievements扩展

作用就是在程序员完成一些“成就”后给予提示和记录,获取相应的称号。例如:

  • 在一个类里面声明100个成员变量(Field Master)
  • 一行代码写300个字符(Scroll Bar Wizard)
  • 使用“Close All But This”10次(Obsessive Compulsive Disorder)
  • ……

看来以后可以加班刷成就啦!