The importance of algorithms
Hello all, Im a developer on Growl, and have been heavily involved with the development of 1.3, the upcoming 1.4, and now the under development 2.0. So, for my first real post on posterous, and my first post to the Growl Space, I thought I would talk a bit about what I’ve been working on recently for Growl. While Growl 1.4 is being finished up by other developers, I have been hard at work on Growl 2.0. I won’t spoil anything too big about whats being done, but lets just say that there are a ton of big changes for the better. One of those that and end user likely wouldn’t notice, but is a huge upgrade, is a new positioning system for displays. (warning, this is long, and slightly technical)
The old system is a giant, tangled nightmare. A few of the main points about why it is so bad, and why it had to go now that I had the timing to do it. Display lifecycle, from creation to death is this convoluted nightmare, display plugins create and hang onto a bridge, which creates the window controllers. When the notification gets taken down, the window controller asks the bridge for a reference back to the display, and lets the display know it was taken down, and the window controller than let go of its reference to the bridge.
On top of this, the bridge was designed with the idea that there could be (although this never happened) multiple window controllers for a single displayed note. This meant that if there had been multiple controllers, and even if they did leave the screen at the same time, you could get a nasty race condition on the life cycle of the bridge.
From there, the window controller asked the positioning controller for a space reservation, which in order to check available space has to loop through every single note looking for intersection, but wait, theres more. If the position controller doesn’t find its ideal place, than it moves by one pixel at a time, checking the entire array again in the primary expansion direction till it finds a space (by looping through to check every rect already reserved), or it runs out of screen. Then, it moves back to the head of the next row/column and tries all over again.
Overall, CPU’s are fairly good at this, its just a bunch of floating point ops using a rect intersection function. Not a big deal, unless when you don’t find space, instead of simply queuing that window controller till a space is cleared, the window controller itself re-fires its function that asks for a space every few seconds. This means that if the screen fills with notes, and none are coming down, or they aren’t coming down fast enough, you build up a ton of notifications, all re-firing their request for space every few seconds. This adds up in a bad way in a hurry.
So, what can we do to fix this mess? First and foremost, clean up the display lifecycle, eliminate the bridge entirely, most of its features were unused, and the remaining bits were glue between the plugin, and the window controller. Display plugins now directly create their window controller, and tell it to start display (Queuing of course puts it into a queue to wait for the current window to come down, and coalescing looks for a note using the same ID to update before creating a new one). And finally, make the top level of the positioning system hang onto the window controllers, and the queue of notes waiting for space.
Positioning. A nightmare of looping through all the rects that are already reserved. A comment in the code said that it might be more efficient to find some way of sorting this array, but that it would be non trivial. But maybe there is another solution besides an array of rects. I didn’t know a method for representing 2D space efficiently, so I went searching.
Enter the QuadTree, a 4 child version of a tree, with various types, mostly used to represent various 2d space systems. I decided to go with the region system, where each node is either fully occupied, fully unoccupied, or subdivided into 4 quadrents. Finding out if a space is free is a simple recursive call on the root node, as is removing and adding occupied spaces. Now we have a system for tracking occupied rects, and its fairly generic. Rather than continuing to pile code into this quad tree, we layer on top of that a system that knows how to expand reservations in columns from any of the 4 corners (rows and additional starting points were eliminated for simplicities sake, as they were not to our knowledge ever used).
Rather than have our positioning and column tracker be bogged down with knowledge of what it is tracking rects and columns for, we make a controller that bridges the gap, and understands the concept of our display controllers, and holds onto them, and queues them when there isn’t space, and knows what methods to ask for what size of space the controller needs. This controller can also have reference to multiple position controllers, one for each display, or even more. The position controller can be started with any amount of space we want to give it. This opens up the possibility for multiple regions of displays on a single screen if we so want.
The new system is not without flaws though. QuadTree’s work “best” when they are sized in equal powers of 2 for width and height. For simplicities sake, I chose to represent it as a rectangle sized to the screen, which means every quadrant, and every quadrant in that quadrant and so on are sized to the aspect ratio of the screen. This brings inaccuracies in positioning, albeit minor if you subdivide far enough, but if you subdivide too far, you wind up with many extra nodes to track reserved spaces, and hundreds of MB of memory. We subdivide so that the dimensions of each child is less than 1x1 pix, but no further, which is not quite far enough for the accuracy we need, but at the same time, the Column system has a built in fudge factor of 1 extra pix of width to compensate. What memory we do use, also frees up just fine as we remove notes, or even as we add notes at times, because we can call consolidate, which recursively causes any extraneous nodes that no longer need children to free up their children.
This new system still uses the +/- 1 pixel approach, however, the structure should let us in a future version improve on this further without disrupting anything above the positioning/column controller level. This could be done potentially by a recursive call into the QuadTree to find the next empty space in the direction we are searching before moving and checking again.
One last point about the new system, from the reservation and column system down can relatively easily be transported into another application for use in doing similar work of managing a space, and stuff that needs to go in it, and expand via columns as it comes in (It could also be fairly easily be switched to doing rows instead of columns). We intend on putting it in the 2.0 version of the framework to back Mist, as the Mist positioning controller, while not as bad as the one in Growl.app was, in some ways, it is worse in others.
In school for Computer Science, I often was annoyed by all the pure theory and data structures and algorithms without seemingly practical purposes. Dont think that just because most major languages/frameworks have arrays, sets, and heaps and their related searches and sorts, that you can ignore that stuff. It has important uses, as does architecture, and understanding what owns an object, when, and why. Also important is to understand when a system is getting out of hand, and needs to either be refactored, or started over.
I will possibly be writing more technical posts in the future. Growl 2.0 will have a lot of new features for users, and developers, as well as many performance enhancements and improvements in architecture.
分类、提醒和标记
我们究竟需要怎样的 GTD 应用?
- 分类的困惑
我最早喜欢的 GTD 应用莫过于 Things,Things for iPhone 在介绍之初所说的是将您的要做的事情进行分类整理以便能够在将来容易的进行查看,应用中提供了诸如 Project 和 Tag 甚至是 Area 的功能可以将所有的 TODO 事项进行不同层次的整理和归类。
听上去不错的功能在实际使用中又如何呢?在我的 Things 中有 5 个 Project,每个里面都有 5 个以上的 TODO 待办,当我把一切都整理妥当以后发现要做事情却无从下手,因为虽然分类可以很好的整理归类 TODO 却无法告诉你你接下来要做什么,其作用更多的是在便于查看和整理上而非指导你完成 TODO 上。Things 的意图是让使用者自己标记 Today 项目,简而言之当你把所有 TODO 项目整理好以后,在 NEXT 中选取今天要做的事情然后按照先后去完成。
从这个角度而言,将所有未完成的 TODO 集中放置在一个列表中,按照先后顺序进行排序然后执行是有必要的,在排序方面我觉得没有哪个 TODO 应用可以和 Clear 相比,直观便捷。可能有人觉得它最多称得上是一个 List,但其实使用颜色进行优先级暗示 TODO 的重要性在执行的时候效率显得很高。
Steps 做到了在 Open 中显示所有待办的条目,可惜的是却无法进行排序,你只能在分类中进行,还是那句话,在执行的时候你需要的就是一张经过排序 List 并照着其中条目的先后顺序去完成,而不是在不同分类之间切换,考虑接下去要做什么。
- DUEDATE 和 BADGE 的误用
之前提到在执行时只需要一张 List,但是其中包含了所有的 TODO 有些是今天必须完成的,需要着重标记怎么办?这就需要用到 DUEDATE。
很多人使用 DUEDATE 来标记 TODO 并不是因为需要在那一天做这个事情,而往往是 Deadline 的标记,这也就是我认为目前大多数 TODO 应用没有很好利用 DUEDATE,它们仅仅是将 TODO 上打上一个时间戳,到时候提醒你而已。如果是一个需要花一段时间完成的项目,DUEDATE 作为过期提醒的话也就失去了意义。
解决办法是将 DUEDATE 分为两类,Deadline 和 Done for day,Done for day 也就是之前说到的 Today,今天做今天完成,相对于其他事项优先级最高。而 Deadline 的作用则是从开始之日起(通常为创建 TODO 的日子)到 Deadline 之间,提醒你这项 TODO 你这段时间一直要做的事情(可以将其看作是每日例行或者是需要每天重复提醒的事项)。
刚提到了提醒,提醒往往和 BADGE 相关,很多应用程序将你未完成的项目数量或者当天需要完成的项目数量在程序图标上进行标记。对于这点,我十分同意 Steps 作者的一段话 :
这样使用 BADGE 并没有什么意义,因为它只能告诉你还需要做几件事情,你仍然需要打开程序并逐条查看列表然后去完成,通常数字越大越会让用户不愿意去打开它,BADGE 应该作为 NOTIFICATION 的补充,告诉你有几件事情忘了,是提醒的手段之一。
这段时间我一直同时在使用 Reminders、Things、Clear 和 Steps,考虑为什么我们有时候需要多个 TODO 应用,在记录某些事情的时候为什么我们会使用这个而不是其他几个。当然以上只是从分类和执行两方面来谈的,还有许多其他功能诸如同步功能、事项备注等等其他和 TODO 相关的内容。但是不管怎么说,与其说我们需要的是一个便于分类和整理的 TODO 应用,更不如说我们需要的是一个能够让我们高效执行 TODO 的应用,毕竟这才是大多数人使用 GTD 的原因。
以上仅仅是从我个人的需求而言,不知道你需要怎样的 TODO 应用呢?
Reminders 的最大优点在于同步,同时结合和 List 和 Calendar 的功能,并且在查看和记录上的操作都非常高效,目前独有的位置提醒在某些特殊事件的提醒上可以说还没有其他应用程序可以相比。
Things 作为项目管理和大量 TODO 的分类和整理非常好,当有多个项目在进行时可以有条不紊的完成,Today 功能在一定程度上明确了今天应该做哪些事项,但需要手动进行 Today 的标记。其提醒功能虽然有限,但是在创建循环事件的时候功能却特别强大,对于 Done for Day 的事项使用起来效率特别高。
Clear 在简化功能的同时却带来了极其高效的交互,输入排序等非常便捷。在建立短期 List 特别是 Today List 的时候非常方便和高效,配合其音效,在使用时和其他应用有着截然不同的感受。然而其不支持 DUEDATE 并且分类功能也有限,因此不适合作为 Reminder 或者项目分类来使用。
Steps 的优势在于单屏幕的导航操作,不需要在不同分类之前切换浏览。所有的事项都集中于 Open 之中,在执行时十分直观。不足之处在于无法进行所有项目的排序,影响了使用效率。其使用圆圈进行标示的做法相对传统的方框和钩也显得别有新意。
Things for iPhone from CulturedCode
圆形头像会丢失比较多的头像细节,我会考虑在设置中增加选项。另外目前刷新方式是停留在刷新处的,你喜欢每次刷新以后都滚动到最顶端的嘛?(注:你可以点击状态栏迅速滚动到最顶端)
未来的版本中会加入此功能
在 1.2 中已经修复,请等待后续更新。
Clear for iPhone - Available Now!
Textie Messaging: Keeping Your Address Book Private
We learned yesterday that some folks in Silicon Valley think that having apps upload a bald copy of your address book to their servers is an “industry best practice” for spurring quick viral growth. To us it sounds like an unnecessary and serious disrespect of address book privacy.
We hope…
1M to 11M Users in 2011
18 months ago, Pulse was created as a class project at Stanford University. We did not imagine that we would come as far as we have today. In 2011 alone, we went from just under 1 million users to over 11 million users. Here’s a quick visualization of how Pulse has grown in the last year. Thank you for joining Pulse and supporting us through our journey!
We plan to continue this growth in 2012 and we’re hiring more team members to make that happen.
- The Pulse Team
关于 Block Callback 的 API 设计
从 iOS 4 引入的 Block 语法普遍被认为是实现 Callback 的一种更为方便的模式,相比 Delegate + Protocol 需要声明和实现一大堆方法而言,Block 的实现更为灵活,然而在实际使用中需要注意 Block 的几点不同:
1、Block 中的操作应该是独立的计算块和操作块
2、Block 将会自动 Retain 其中的对象(__block 修饰的对象除外)
3、对于可能需要被取消的异步操作而言,回调不宜使用 Block
假设在一个 ViewController 里面需要进行一些异步的数据或者网络操作,我们希望在回调方法中根据操作结果更新界面,这也是一个非常普遍的模式。而很合理的一点,当 ViewController 销毁时我们希望取消这些操作以回收系统资源,这时最好的方法并不是 Block 而是传统的 Delegate + Protocol 模式,使用 Block 将会使得 ViewController 自身的 RetainCount +1,导致无法在销毁时取消不必要的系统开销,如果使用 __block 修饰符则可能导致回调时奔溃的结果。
对于以上以及类似的情况使用传统方式则是一种更为稳妥的手段,在自身销毁时取消相关的操作并将相应的 delegate 设为 nil 即可。然而对于 Block 的实现方法仍然可以通过 Notification 规避将自身至于 block 之中,但过多的 notification 使用将会使得程序流程显的混乱不堪,对于回调一般需要在 delegate、block、notification 和 kvo 四者中选择最合适的方法,才能使得 API 设计显的合理、可靠、优雅。
The New Twitter (R.I.P. Tweetie)
Twitter today unveiled a new UI, debuting first in updated versions of its official iPhone, Android, and web clients. Coming soon: an updated version of their iPad app. (No mention of the Mac app — a sign of the times.)
This is more than an update. It’s a serious rethinking of the entire concept of Twitter. The old official Twitter app for the iPhone started life as Tweetie, a third-party app by the talented Loren Brichter. Twitter bought Tweetie and hired Brichter back in April 2010. Until today’s update, Twitter’s iPhone app was very much just a rebranded and updated version of Tweetie.
Not any more.1
Let me state up front that I was a big fan of Tweetie right from the get-go, when it debuted in November 2008. Tweetie was remarkably innovative. It was one of the first Twitter clients that supported loading additional (older) tweets when you reached the end of a list. It’s hard to imagine a Twitter client without that feature today. Brichter invented the pull-to-refresh gesture that’s now used by dozens of iOS apps.2
But more than anything, what I liked about Tweetie was its simplicity and clarity of purpose. If anything, Tweetie offered a better, more clearly-defined presentation of Twitter than even the Twitter.com website itself. Tweetie presented three main things:
The main timeline, showing the tweets of those people you follow. Replies/mentions, showing tweets where you are mentioned or addressed. Direct messages, showing private messages in a IM-style threaded views. That’s what Tweetie presented you with, and that, to me, is what Twitter is all about. The app fit my mental model of the service. Three things: what I subscribe to, people mentioning me, and direct messages.
That’s not how everyone views Twitter. Maybe not even most people, I don’t know. There are other excellent Twitter clients that take a different view of the service. As of today, Twitter’s own client does too.
There are four main tabs in the new Twitter app for iPhone, Android, and the mobile web app:
“Home”, showing the tweets of the people you follow. So far so good. This is just a new and perhaps better name for the main timeline. The birdhouse icon is delightful, just right.
“Connect”, which leads to a second tab bar: “Interactions” and “Mentions”. So what was previously just mentions is now two things: mentions and interactions. Interactions are your mentions plus events like when people start following you, or favorite or retweet one of your tweets. This is not terrible, per se, but it adds complexity. A tab view within a tab view is often a bad sign — a second level of hierarchy makes things at least twice as complex. Interactions and Mentions aren’t two different things — the Interactions view includes your mentions. It’s more like Mentions Plus Other Events vs. Mentions Only. Instead of two different views, this should simply be an On/Off setting: “Show interactions with mentions”, with a small-print description of the sort of events that will be included.
And the Mentions tab is slightly worse on the iPhone app than on the mobile web app. Here’s a screenshot of the Connect view on the mobile web app; here’s the same view from the iPhone app. At least in the mobile version (and I presume, the Android version, which also puts the main tab bar at the top) the hierarchy is clear: “Interactions/Mentions” is obviously a sub-view of the main tab bar. But in the iPhone version, there’s no visual indication as to which tab bar is a sub-view of the other.
Why is this section called “Connect” anyway? What is getting connected here? That it was (apparently) hard to come up with a name for the parent tab of the Interactions/Mentions view is another sign that this was not an improvement over the simplicity and obviousness of the old just plain Mentions tab.
“Discover”. Uh-oh. Gone are direct messages as a standalone top-level tab. Replacing them is Discover, with a “#” as the icon. What is this? I see, in order, “Stories” I don’t care about, “Trends” I don’t care about, and suggestions for people to follow. I have no interest in anything in this tab other than, perhaps, the suggestions for people to follow, but that certainly isn’t a feature that deserves one of only four top-level tabs in the app. As we move from left to right in these tabs, we see the former clarity and simplicity of Tweetie devolve into complication and conceptual mushiness.
Presumably, this Discover tab is the successor to the late and unlamented dickbar — where sponsors will be able to pay Twitter to promote products and services.
“Me”. Oh boy. Stashed into this tab are your profile, your direct messages, your Twitter Lists, and the interface for switching to other Twitter accounts. This tab is the conceptual carpet under which Twitter swept everything that didn’t fit under “Home”, “Connect”, or “Discover”.
This new treatment of multiple accounts deserves attention. In Tweetie, there was a left-to-right column-view hierarchical layout, a design pattern used throughout iOS. Consider Mail, left-to-right: list of accounts, list of mailboxes in an account, list of messages in a mailbox, an individual message. If you only have one email account, Mail is smart enough to omit the list of the accounts from the hierarchy.
Tweetie followed this same pattern. Left-most, a list of the Twitter accounts you’d set up within the app. To the right, a list of tweets for an account. If you only configured one account — common for most Twitter users, surely — Tweetie omitted the list of accounts. But if you did configure multiple accounts, the interface for switching was spatial: go back to the left, using a button at the top left corner of the screen.
Now, to switch accounts in the new Twitter app, you go to the rightmost tab at the bottom — bottom right instead of top left, which alone I find disorienting. Then scroll down to “Switch accounts”, which, when tapped, flips the screen — the animation you get in widget apps like Weather or Stocks when you tap the “i” button to configure them. The concept here is that the “front” face of the app is where you use a Twitter account, and the “back” face is a list of accounts you can switch to. That’s not completely broken conceptually, but it seems unnatural on iOS.
If you frequently use direct messages and/or multiple accounts, I don’t see how you can view this new Twitter app as anything but a step back. There are gestural shortcuts for accessing these things: swipe left on the Me tab bar button to jump to the account switcher (the direction of the gesture corresponds to the direction of the flip as the screen spins to show you the “back”), swipe up on the Me button to jump to direct messages (this too, corresponds to the direction of the animation — the direct message view comes up from the bottom of the screen). But it’s wrong to need to know gestural shortcuts to access frequently-used features. Gestural shortcuts are like keyboard shortcuts — a nice convenience for power users, but not something that you should need to know merely to feel efficient within the app.3
The grab-bag nature of the Me tab isn’t wholly out of place on the iPhone. It’s a common design for apps with a tab bar at the bottom, but which offer more features than the iPhone has room for tabs. The standard design is to put a “More” tab at the bottom right, with a “…” icon, which shows a list of less-frequently used items. And there should be an edit button that allows you to drag-and-drop items to decide which ones are stashed away in More and which get the permanent spots on the toolbar itself. See the Music app (née iPod) for a canonical example.
The problem here isn’t necessarily that they’ve stashed direct messages within the Me tab and given a new feature, Discover, its former spot on the tab bar. It’s that you can’t change it back, if, say, you frequently use direct messages and never use Discover.
There are all sorts of little fit-and-finish problems. Swipe to delete a DM conversation and the red Delete button draws with a clipping error over the background gutter. Swipe to delete an individual direct message and it’s even worse. Go to Me → Settings → Advanced, then click the back button to back to Settings. The “Done” button in the top left corner jitters around nervously before settling into place.
Tweetie was an iPhone app. It was an attempt to do Twitter as best as it could be done on the iPhone (and iPad for that matter). It wasn’t for everyone. There were (and are) other ways to do Twitter in an iPhone-optimized way.
But this, today’s new Twitter, is something else. It’s an attempt at a best way to do Twitter that is as consistent as possible across multiple platforms, ranging from the iPhone to Android to the mobile and desktop web. I don’t want an iPhone app that’s constrained by the restrictions of a mobile web app. The whole reason I prefer native apps is that I like experiences that far exceed what can be done in a web app. This is a native app that looks and feels like it was designed and polished according to the norms of web apps, not other native iPhone apps.
So, bottom line, I don’t care for the new Twitter app much at all. But I switched to Tweetbot on my iPhone months ago. And I stopped using the Twitter website for much of anything years ago. Native Twitter clients, all the way. So I’m not angry, or even aggrieved. I am, though, a little sad — and more than a little worried.
Sad, because Tweetie was truly a great app, and today’s Twitter is no Tweetie. I wouldn’t hesitate to hold Tweetie up as one of the best iPhone apps ever made, period. It was every bit as polished and clever — if not more so — than Apple’s own apps. No app is perfect, no app will please everyone, but Tweetie came damn close.
Worried, because the flip side of the disintegrating quality of Twitter’s official client software is their growing ambivalence toward third-party clients. Everything’s going to be just fine so long as great apps like Tweetbot, Tweetlogix, Twitterrific, Echofon and so forth are able serve as unhindered Twitter clients. The question is how long that will be.
What also worries me is that these changes suggest not only a difference in opinion regarding how a Twitter client should work, but also regarding just what the point is of Twitter as a service. The Twitter service I signed up for is one where people tweet 140-character posts, you follow those people whose tweets you tend to enjoy, and that’s it. The Twitter service this new UI presents is about a whole lot more — mass-market spoonfed “trending topics” and sponsored content. It’s trying to make Twitter work for people who don’t see the appeal of what Twitter was supposed to be. It all makes sense if you think of the label under the “#” tab as reading “Dickbar” instead of “Discover”.
Twitter 4.0 for iPhone lacks the surprise, delight, and attention to detail of a deserving successor to Tweetie, offering instead a least common denominator experience that no one deserves.
Worth noting: Loren Brichter’s last day working at Twitter was last month. ↩
Who do we bribe at Apple to get pull-to-refresh into Mail? ↩
Speaking of gestures, the new Twitter app has gotten rid of one of the best gestural shortcuts I’ve ever seen. In Tweetie, Brichter had a gestural shortcut that allowed you jump back to the root of a deeply nested series of screens. Let’s say you were in your main timeline, and you tapped on a tweet. Now you’re one screen to the right, on a tweet detail. Then, within that tweet, you tap a @username. One more screen to the right, on that user’s profile page. Then you decide to view a list of that user’s previous tweets — another screen to the right. And then you tap on a URL within one of those tweets to read a linked web page. One more screen to the right. Now, at this point, if you want to go all the way back to your main timeline, you’d need to tap a series of back buttons in the top left corner. Back back back back back back back (with apologies to Chris Berman). Brichter’s shortcut allowed you to swipe on any of those back buttons to jump all the way back to the beginning. So you could go as many levels deep as you wanted and you’d be just one swipe away from going all the way back.
That shortcut is gone now. ↩
We learned yesterday that some folks in Silicon Valley think that having apps upload a bald copy of your address book to their servers is an “industry best practice” for spurring quick viral growth. To us it sounds like an unnecessary and serious disrespect of address book privacy.