<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Wangling &#187; development</title>
	<atom:link href="http://wangling.me/category/development/feed/" rel="self" type="application/rss+xml" />
	<link>http://wangling.me</link>
	<description>I&#039;m Wang Ling. I&#039;m wangling you.</description>
	<lastBuildDate>Tue, 15 May 2012 14:17:55 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>Three Things Apple Didn’t Tell You About In-App Purchase</title>
		<link>http://wangling.me/2012/04/three-things-apple-didnt-tell-you-about-in-app-purchase/</link>
		<comments>http://wangling.me/2012/04/three-things-apple-didnt-tell-you-about-in-app-purchase/#comments</comments>
		<pubDate>Tue, 17 Apr 2012 16:38:39 +0000</pubDate>
		<dc:creator>an0</dc:creator>
				<category><![CDATA[development]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[App Store]]></category>
		<category><![CDATA[IAP]]></category>

		<guid isPermaLink="false">http://wangling.me/?p=827</guid>
		<description><![CDATA[You read once again In-App Purchase Programming Guide and Technical Note TN2259 completely, yet still can not make the sandbox work. Now may be the time for a break to get something fresh: Bundle ID is case-sensitive. If it is com.moke.app in the iTunes Connect but com.moke.App in App-Info.plist — that is default Bundle ID [...]]]></description>
			<content:encoded><![CDATA[<p>You read once again <a href="https://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/StoreKitGuide/Introduction/Introduction.html">In-App Purchase Programming Guide</a> and <a href="http://developer.apple.com/library/ios/#technotes/tn2259/_index.html">Technical Note TN2259</a> completely, yet still can not make the sandbox work. Now may be the time for a break to get something fresh:</p>
<ol>
<li>Bundle ID is case-sensitive. If it is com.moke.app in the iTunes Connect but com.moke.App in App-Info.plist — that is default Bundle ID set in your app if you named your Xcode project &#8220;App&#8221; — IAP will not work.</li>
<li>&#8220;Waiting for Screenshot&#8221; is not a good status. At least you should upload a placeholder screenshot and let the product status be &#8220;Ready to Submit&#8221;.</li>
<li>No new Development Provisioning Profile is required. Though <a href="http://developer.apple.com/library/ios/#technotes/tn2259/_index.html">Technical Note TN2259</a> tells you to &#8220;Create, download, and install a new Development Provisioning Profile that uses your App ID enabled for In-App Purchase as seen in…&#8221;, it is not required if you are using a Development Provisioning Profile &#8220;Managed by Xcode&#8221; — it works for IAP apps even though it has a different App ID. Of course, it works for development installations only and you need setup Distribution Provisioning Profiles to distribute any apps no matter they have IAP or not.</li>
</ol>
<p>One more thing. It&#8217;s not Apple&#8217;s One More Thing. In fact, it is one more thing that Apple does not like. If you are using a jailbroken device, make sure you uninstall AppSync from Cydia before testing IAP.</p>
<div class="social">			<a href="https://twitter.com/share?url=http%3A%2F%2Fwangling.me%2F2012%2F04%2Fthree-things-apple-didnt-tell-you-about-in-app-purchase%2F" class="twitter-share-button" data-url="http%3A%2F%2Fwangling.me%2F2012%2F04%2Fthree-things-apple-didnt-tell-you-about-in-app-purchase%2F" data-text="Three Things Apple Didn’t Tell You About In-App Purchase" data-via="an0" data-count="none">Tweet</a>
			<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>			<script type="text/javascript" charset="utf-8">
(function(){
  var _w = 86 , _h = 18;
  var param = {
    url:"http%3A%2F%2Fwangling.me%2F2012%2F04%2Fthree-things-apple-didnt-tell-you-about-in-app-purchase%2F",
    type:'6',
    count:'', /**是否显示分享数，1显示(可选)*/
    appkey:'', /**您申请的应用appkey,显示分享来源(可选)*/
    title:'Three Things Apple Didn’t Tell You About In-App Purchase', /**分享的文字内容(可选，默认为所在页面的title)*/
    pic:'', /**分享图片的路径(可选)*/
    ralateUid:'1676354212', /**关联用户的UID，分享微博会@该用户(可选)*/
    rnd:new Date().valueOf()
  }
  var temp = [];
  for( var p in param ){
    temp.push(p + '=' + encodeURIComponent( param[p] || '' ) )
  }
  document.write('<iframe allowTransparency="true" frameborder="0" scrolling="no" src="http://hits.sinajs.cn/A1/weiboshare.html?' + temp.join('&') + '" width="'+ _w+'" height="'+_h+'" style="margin-left:5px;"></iframe>')
})()
</script></div>]]></content:encoded>
			<wfw:commentRss>http://wangling.me/2012/04/three-things-apple-didnt-tell-you-about-in-app-purchase/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Some Taps You Don&#8217;t Want</title>
		<link>http://wangling.me/2012/03/some-taps-you-dont-want/</link>
		<comments>http://wangling.me/2012/03/some-taps-you-dont-want/#comments</comments>
		<pubDate>Thu, 22 Mar 2012 22:25:57 +0000</pubDate>
		<dc:creator>an0</dc:creator>
				<category><![CDATA[development]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[UIButton]]></category>

		<guid isPermaLink="false">http://wangling.me/?p=820</guid>
		<description><![CDATA[UIButton is one of the most simple and commonly used UIControl. We use it so often in the way it is intended to be used that we dismiss it as a perfectly meek boy until one day it bites our fingers when we tap it. Today is such a day. I have a UIButton that [...]]]></description>
			<content:encoded><![CDATA[<p><span class="code">UIButton</span> is one of the most simple and commonly used <span class="code">UIControl</span>. We use it so often in the way it is intended to be used that we dismiss it as a perfectly meek boy until one day it bites our fingers when we tap it.</p>
<p>Today is such a day.</p>
<p>I have a <span class="code">UIButton</span> that when tapped will trigger a custom view transition with animations. Animations mean duration — it takes some time to complete. Though the transition is still in progress, the button does not refuse more taps — it just loves taps!</p>
<p>I guess you see the problem here: What happens if the button is tapped again during the view transition?</p>
<p>Another view transition is triggered, of course, and I don&rsquo;t want it.</p>
<p>So I must filter out taps during the view transition.</p>
<p>Notice that you can&rsquo;t reproduce this problem by double tapping a button that triggers a standard view transition such as pushing a view controller using <span class="code">pushViewController<span style="color: #002200;">:</span>animated<span style="color: #002200;">:</span></span> or presenting a view controller using <span class="code">presentViewController<span style="color: #002200;">:</span>animated<span style="color: #002200;">:</span>completion<span style="color: #002200;">:</span></span>. I guess it is because the view the button is on is being covered during these standard view transitions so the button can not receive taps.</p>
<div class="social">			<a href="https://twitter.com/share?url=http%3A%2F%2Fwangling.me%2F2012%2F03%2Fsome-taps-you-dont-want%2F" class="twitter-share-button" data-url="http%3A%2F%2Fwangling.me%2F2012%2F03%2Fsome-taps-you-dont-want%2F" data-text="Some Taps You Don&#8217;t Want" data-via="an0" data-count="none">Tweet</a>
			<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>			<script type="text/javascript" charset="utf-8">
(function(){
  var _w = 86 , _h = 18;
  var param = {
    url:"http%3A%2F%2Fwangling.me%2F2012%2F03%2Fsome-taps-you-dont-want%2F",
    type:'6',
    count:'', /**是否显示分享数，1显示(可选)*/
    appkey:'', /**您申请的应用appkey,显示分享来源(可选)*/
    title:'Some Taps You Don&#8217;t Want', /**分享的文字内容(可选，默认为所在页面的title)*/
    pic:'', /**分享图片的路径(可选)*/
    ralateUid:'1676354212', /**关联用户的UID，分享微博会@该用户(可选)*/
    rnd:new Date().valueOf()
  }
  var temp = [];
  for( var p in param ){
    temp.push(p + '=' + encodeURIComponent( param[p] || '' ) )
  }
  document.write('<iframe allowTransparency="true" frameborder="0" scrolling="no" src="http://hits.sinajs.cn/A1/weiboshare.html?' + temp.join('&') + '" width="'+ _w+'" height="'+_h+'" style="margin-left:5px;"></iframe>')
})()
</script></div>]]></content:encoded>
			<wfw:commentRss>http://wangling.me/2012/03/some-taps-you-dont-want/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Zenburn Theme for Xcode 4</title>
		<link>http://wangling.me/2011/09/zenburn-theme-for-xcode-4/</link>
		<comments>http://wangling.me/2011/09/zenburn-theme-for-xcode-4/#comments</comments>
		<pubDate>Fri, 23 Sep 2011 14:22:36 +0000</pubDate>
		<dc:creator>an0</dc:creator>
				<category><![CDATA[中文]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Xcode]]></category>

		<guid isPermaLink="false">http://wangling.me/?p=730</guid>
		<description><![CDATA[如果默认主题让你眼睛疲倦的话，试试 Zenburn。暗色系，低对比度，适合长时间编码。]]></description>
			<content:encoded><![CDATA[<p>长久对着 Xcode 写代码，默认 Theme 的白亮和高对比度让我的眼睛很疲倦。自带的 Midnight Theme 太艳俗，而且对比度依然很高。我在 Emacs 里用 <a href="http://slinky.imukuppi.org/zenburnpage/">Zenburn</a>，感觉不错，于是就自己动手 port 到 Xcode 里，并针对性的做了微调。</p>
<div class="thumbnail"><a href="https://skitch.com/an00na/f5c3e/zenburn-for-xcode"><img src="https://img.skitch.com/20110923-tudq2cm22x6bmmahqhtuh6f6qq.preview.jpg" alt="Zenburn for Xcode" /></a></div>
<p>喜欢的话，可从 <a href="https://github.com/an0/Zenburn-for-Xcode">GitHub</a> 上获取。</p>
<div class="social">			<a href="https://twitter.com/share?url=http%3A%2F%2Fwangling.me%2F2011%2F09%2Fzenburn-theme-for-xcode-4%2F" class="twitter-share-button" data-url="http%3A%2F%2Fwangling.me%2F2011%2F09%2Fzenburn-theme-for-xcode-4%2F" data-text="Zenburn Theme for Xcode 4: 如果默认主题让你眼睛疲倦的话，试试 Zenburn。暗色系，低对比度，适合长时间编码。" data-via="an0" data-count="none">Tweet</a>
			<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>			<script type="text/javascript" charset="utf-8">
(function(){
  var _w = 86 , _h = 18;
  var param = {
    url:"http%3A%2F%2Fwangling.me%2F2011%2F09%2Fzenburn-theme-for-xcode-4%2F",
    type:'6',
    count:'', /**是否显示分享数，1显示(可选)*/
    appkey:'', /**您申请的应用appkey,显示分享来源(可选)*/
    title:'Zenburn Theme for Xcode 4: 如果默认主题让你眼睛疲倦的话，试试 Zenburn。暗色系，低对比度，适合长时间编码。', /**分享的文字内容(可选，默认为所在页面的title)*/
    pic:'', /**分享图片的路径(可选)*/
    ralateUid:'1676354212', /**关联用户的UID，分享微博会@该用户(可选)*/
    rnd:new Date().valueOf()
  }
  var temp = [];
  for( var p in param ){
    temp.push(p + '=' + encodeURIComponent( param[p] || '' ) )
  }
  document.write('<iframe allowTransparency="true" frameborder="0" scrolling="no" src="http://hits.sinajs.cn/A1/weiboshare.html?' + temp.join('&') + '" width="'+ _w+'" height="'+_h+'" style="margin-left:5px;"></iframe>')
})()
</script></div>]]></content:encoded>
			<wfw:commentRss>http://wangling.me/2011/09/zenburn-theme-for-xcode-4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Bugs of NSFetchedResultsControllerDelegate Template Code</title>
		<link>http://wangling.me/2011/09/bugs-of-nsfetchedresultscontrollerdelegate-template-code/</link>
		<comments>http://wangling.me/2011/09/bugs-of-nsfetchedresultscontrollerdelegate-template-code/#comments</comments>
		<pubDate>Thu, 01 Sep 2011 15:22:09 +0000</pubDate>
		<dc:creator>an0</dc:creator>
				<category><![CDATA[development]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[NSFetchedResultsController]]></category>
		<category><![CDATA[UITableView]]></category>
		<category><![CDATA[UITableViewCell]]></category>

		<guid isPermaLink="false">http://wangling.me/?p=682</guid>
		<description><![CDATA[This is the template code of controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: given by Apple: - &#40;void&#41;controller:&#40;NSFetchedResultsController *&#41;controller didChangeObject:&#40;id&#41;anObject atIndexPath:&#40;NSIndexPath *&#41;indexPath forChangeType:&#40;NSFetchedResultsChangeType&#41;type newIndexPath:&#40;NSIndexPath *&#41;newIndexPath &#123; &#160; &#160; UITableView *tableView = self.tableView; &#160; &#160; &#160; &#160; &#160; &#160; switch&#40;type&#41; &#123;&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; case NSFetchedResultsChangeInsert: &#160; &#160; &#160; &#160; &#160; &#160; &#91;tableView insertRowsAtIndexPaths:&#91;NSArray arrayWithObject:newIndexPath&#93; [...]]]></description>
			<content:encoded><![CDATA[<p>This is the template code of <em>controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:</em> given by Apple:<br />
<span class="code"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>controller<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>NSFetchedResultsController <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>controller didChangeObject<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">id</span><span style="color: #002200;">&#41;</span>anObject<br />
atIndexPath<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #400080;">NSIndexPath</span> <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>indexPath forChangeType<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>NSFetchedResultsChangeType<span style="color: #002200;">&#41;</span>type<br />
newIndexPath<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #400080;">NSIndexPath</span> <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>newIndexPath <span style="color: #002200;">&#123;</span><br />
<br />
&nbsp; &nbsp; UITableView <span style="color: #002200;">*</span>tableView <span style="color: #002200;">=</span> self.tableView;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #a61390;">switch</span><span style="color: #002200;">&#40;</span>type<span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #a61390;">case</span> NSFetchedResultsChangeInsert<span style="color: #002200;">:</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>tableView insertRowsAtIndexPaths<span style="color: #002200;">:</span><span style="color: #002200;">&#91;</span><span style="color: #400080;">NSArray</span> arrayWithObject<span style="color: #002200;">:</span>newIndexPath<span style="color: #002200;">&#93;</span> withRowAnimation<span style="color: #002200;">:</span>UITableViewRowAnimationFade<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #a61390;">break</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #a61390;">case</span> NSFetchedResultsChangeDelete<span style="color: #002200;">:</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>tableView deleteRowsAtIndexPaths<span style="color: #002200;">:</span><span style="color: #002200;">&#91;</span><span style="color: #400080;">NSArray</span> arrayWithObject<span style="color: #002200;">:</span>indexPath<span style="color: #002200;">&#93;</span> withRowAnimation<span style="color: #002200;">:</span>UITableViewRowAnimationFade<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #a61390;">break</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #a61390;">case</span> NSFetchedResultsChangeUpdate<span style="color: #002200;">:</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>self configureCell<span style="color: #002200;">:</span><span style="color: #002200;">&#91;</span>tableView cellForRowAtIndexPath<span style="color: #002200;">:</span>indexPath<span style="color: #002200;">&#93;</span> atIndexPath<span style="color: #002200;">:</span>indexPath<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #a61390;">break</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #a61390;">case</span> NSFetchedResultsChangeMove<span style="color: #002200;">:</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>tableView deleteRowsAtIndexPaths<span style="color: #002200;">:</span><span style="color: #002200;">&#91;</span><span style="color: #400080;">NSArray</span> arrayWithObject<span style="color: #002200;">:</span>indexPath<span style="color: #002200;">&#93;</span> withRowAnimation<span style="color: #002200;">:</span>UITableViewRowAnimationFade<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>tableView insertRowsAtIndexPaths<span style="color: #002200;">:</span><span style="color: #002200;">&#91;</span><span style="color: #400080;">NSArray</span> arrayWithObject<span style="color: #002200;">:</span>newIndexPath<span style="color: #002200;">&#93;</span> withRowAnimation<span style="color: #002200;">:</span>UITableViewRowAnimationFade<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #a61390;">break</span>;<br />
<span style="color: #002200;">&#125;</span></span></p>
<p>There are two bugs in the <em>NSFetchedResultsChangeUpdate</em> case:</p>
<ol>
<li><em>newIndexPath</em> may be non-nil and different from <em>indexPath</em>, for example, when some rows are  deleted or inserted while some other rows are updated in the same session. In these cases, one should use the old <em>indexPath</em> to locate the table cell but use the <em>newIndexPath</em> to fetch the object from the <em>NSFetchedResultsController</em> because <em>controller:didChange…</em> and table view is being updated. So the correct code should be:<br />
<span class="code"><span style="color: #002200;">&#91;</span>self configureCell<span style="color: #002200;">:</span><span style="color: #002200;">&#91;</span>tableView cellForRowAtIndexPath<span style="color: #002200;">:</span>indexPath<span style="color: #002200;">&#93;</span> atIndexPath<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>newIndexPath ? newIndexPath <span style="color: #002200;">:</span> indexPath<span style="color: #002200;">&#41;</span><span style="color: #002200;">&#93;</span>;</span>
</li>
<li>
With the code above, you have correctly updated the table cell. However, the cell on screen is still displayed as before. That&#8217;s because even though you&#8217;ve re-configured the cell with updated data, you&#8217;ve not refreshed it. To force a refresh, you need to call the table cell&#8217;s <em>setNeedsLayout</em> or/and <em>setNeedsDisplay</em> depending on your cell&#8217;s implementation. Refer to <a href="http://stackoverflow.com/questions/1326408/is-nsfetchedresultscontrollerdelegate-changeupdate-behavior-broken">Stack Overflow</a> for the discussion. You can also resort to <em>reloadRowsAtIndexPaths:withRowAnimation:</em> of course, albeit it seems a bit overkill to me.
</li>
</ol>
<div class="social">			<a href="https://twitter.com/share?url=http%3A%2F%2Fwangling.me%2F2011%2F09%2Fbugs-of-nsfetchedresultscontrollerdelegate-template-code%2F" class="twitter-share-button" data-url="http%3A%2F%2Fwangling.me%2F2011%2F09%2Fbugs-of-nsfetchedresultscontrollerdelegate-template-code%2F" data-text="Bugs of NSFetchedResultsControllerDelegate Template Code" data-via="an0" data-count="none">Tweet</a>
			<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>			<script type="text/javascript" charset="utf-8">
(function(){
  var _w = 86 , _h = 18;
  var param = {
    url:"http%3A%2F%2Fwangling.me%2F2011%2F09%2Fbugs-of-nsfetchedresultscontrollerdelegate-template-code%2F",
    type:'6',
    count:'', /**是否显示分享数，1显示(可选)*/
    appkey:'', /**您申请的应用appkey,显示分享来源(可选)*/
    title:'Bugs of NSFetchedResultsControllerDelegate Template Code', /**分享的文字内容(可选，默认为所在页面的title)*/
    pic:'', /**分享图片的路径(可选)*/
    ralateUid:'1676354212', /**关联用户的UID，分享微博会@该用户(可选)*/
    rnd:new Date().valueOf()
  }
  var temp = [];
  for( var p in param ){
    temp.push(p + '=' + encodeURIComponent( param[p] || '' ) )
  }
  document.write('<iframe allowTransparency="true" frameborder="0" scrolling="no" src="http://hits.sinajs.cn/A1/weiboshare.html?' + temp.join('&') + '" width="'+ _w+'" height="'+_h+'" style="margin-left:5px;"></iframe>')
})()
</script></div>]]></content:encoded>
			<wfw:commentRss>http://wangling.me/2011/09/bugs-of-nsfetchedresultscontrollerdelegate-template-code/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>搞定新浪微博 API 之 Upload</title>
		<link>http://wangling.me/2011/08/sina-weibo-api-upload/</link>
		<comments>http://wangling.me/2011/08/sina-weibo-api-upload/#comments</comments>
		<pubDate>Tue, 09 Aug 2011 22:21:28 +0000</pubDate>
		<dc:creator>an0</dc:creator>
				<category><![CDATA[中文]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[微博]]></category>

		<guid isPermaLink="false">http://wangling.me/?p=585</guid>
		<description><![CDATA[新浪微博很火，开放平台很火，开发者很“火”。 开发者火是因为新浪微博开放平台对开发者很不友好，其 API 从实现到文档都很粗糙。 API 实现不标准还能忍，但是文档不说明清楚，要开发者自己摸索就离谱了。1看来新浪尚未有暇顾及第三方开发者，开放平台现在也只是“开门放出来”而已。结果就是开发者在很多毫无意义的事情上折腾，浪费时间！ 我写这一系列文章的目的就是避免后来者遭同样的罪。 这是第一篇，关于 upload API 的 OAuth 验证失败问题。去论坛搜索一下 upload 就知道有多少人深受其害了。2 Upload API 的 OAuth 之所以难搞，部分是因为其 HTTP 请求格式的特殊性，主要是因为新浪微博那匪夷所思的实现。 Upload API 的特殊性在于其请求“采用 multipart/form-data 编码方式提交”3。根据 OAuth 1.0 协议，Content-Type 为 multipart/form-data 的 HTTP 请求，其 entity body 不参与 OAuth 签名。照此，upload API 的 OAuth 应该比其他普通 API 更简单，因为只有 OAuth 参数（oauth_ 开头的一系列特别参数）参与签名。所以，标准的 Signature Base String 应该是： POSThttp%3A%2F%2Fapi.t.sina.com.cn%2Fstatuses%2Fupload.jsonoauth_consumer_key%3Dxxxxxxxxxx%26oauth_nonce%3D15492994958798014939%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1312912324%26oauth_token%3Dyyyyyyyyyy%26oauth_version%3D1.0 与其对应的标准的 [...]]]></description>
			<content:encoded><![CDATA[<p>新浪微博很火，开放平台很火，开发者很“火”。</p>
<p>开发者火是因为新浪微博开放平台对开发者很不友好，其 API 从实现到文档都很粗糙。</p>
<p>API 实现不标准还能忍，但是文档不说明清楚，要开发者自己摸索就离谱了。<sup><a href="http://wangling.me/2011/08/sina-weibo-api-upload/#footnote_0_585" id="identifier_0_585" class="footnote-link footnote-identifier-link" title="官方论坛没什么帮助，哭的多，救的少。">1</a></sup>看来新浪尚未有暇顾及第三方开发者，开放平台现在也只是“开门放出来”而已。结果就是开发者在很多毫无意义的事情上折腾，浪费时间！</p>
<p>我写这一系列文章的目的就是避免后来者遭同样的罪。</p>
<p>这是第一篇，关于 <a href="http://open.weibo.com/wiki/Statuses/upload">upload API</a> 的 OAuth 验证失败问题。去论坛搜索一下 upload 就知道有多少人深受其害了。<sup><a href="http://wangling.me/2011/08/sina-weibo-api-upload/#footnote_1_585" id="identifier_1_585" class="footnote-link footnote-identifier-link" title="由于没做 UTF-8 编码或 URL 编码造成的 OAuth 验证失败是开发者自己的错误。">2</a></sup></p>
<p>Upload API 的 OAuth 之所以难搞，部分是因为其 HTTP 请求格式的特殊性，主要是因为新浪微博那匪夷所思的实现。</p>
<p style="word-wrap: break-word">Upload API 的特殊性在于其请求“采用 <a href="http://tools.ietf.org/html/rfc2388">multipart/form-data</a> 编码方式提交”<sup><a href="http://wangling.me/2011/08/sina-weibo-api-upload/#footnote_2_585" id="identifier_2_585" class="footnote-link footnote-identifier-link" title="本文也同样适用于其他采用 multipart/form-data 编码方式提交 HTTP 请求的 API，如 update_profile_image。">3</a></sup>。根据 <a href="http://tools.ietf.org/html/rfc5849">OAuth 1.0</a> 协议，Content-Type 为 multipart/form-data 的 HTTP 请求，其 entity body 不参与 OAuth 签名。照此，upload API 的 OAuth 应该比其他普通 API 更简单，因为只有  OAuth  参数（oauth_ 开头的一系列特别参数）参与签名。所以，标准的 Signature Base String 应该是：<br />
<code>POSThttp%3A%2F%2Fapi.t.sina.com.cn%2Fstatuses%2Fupload.jsonoauth_consumer_key%3Dxxxxxxxxxx%26oauth_nonce%3D15492994958798014939%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1312912324%26oauth_token%3Dyyyyyyyyyy%26oauth_version%3D1.0</code></p>
<p style="word-wrap: break-word">与其对应的标准的 Authorization header 应该是：<br />
<code>OAuth oauth_consumer_key=xxxxxxxxxx, oauth_token=yyyyyyyyyy, oauth_signature_method=HMAC-SHA1, oauth_version=1.0, oauth_nonce=15492994958798014939, oauth_timestamp=1312912324, oauth_signature=2RxNudXqdeSLXSxxRSgpIWUa3HI%3D”</code></p>
<p>然而，迎接你的将是：<br />
<code>40107:Oauth Error: signature_invalid!</code></p>
<p style="word-wrap: break-word">这是因为新浪微博 upload API 的实现要求 Signature Base String 包含除 pic 以外的参数。所以新浪微博需要的 Signature Base String 是这样的：<br />
<code>POSThttp%3A%2F%2Fapi.t.sina.com.cn%2Fstatuses%2Fupload.json</code><strong><code>lat%3D37.78711200</code></strong><code>%26</code><strong><code>long%3D-122.40846000</code></strong><code>%26oauth_consumer_key%3Dxxxxxxxxxx%26oauth_nonce%3D18218585476538551879%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1312912886%26oauth_token%3Dyyyyyyyyyy%26oauth_version%3D1.0%26</code><strong><code>status%3DPic</code></strong></p>
<p>问题来了：status、lat、long 这些参数是在 entity body 中提交的，如前所述，它们不参与 OAuth 签名。为了遵循 OAuth 协议，必须将这些参数加入 URL query string 或 Authorization header。</p>
<p>规范的开发者这么做了，结果“错”了，新浪微博 server 还是返回：<br />
<code>40107:Oauth Error: signature_invalid!</code></p>
<p>事实上，无论你怎么折腾，只要你遵循 OAuth 协议，你一定折腾不出来，因为新浪微博是不遵循 OAuth 协议的。</p>
<p>对于 Content-Type 为 multipart/form-data 的 HTTP 请求，新浪微博要求非 binary 参数（如 upload API 中的 pic 参数）参与 OAuth 签名，同时还要求这些参数是“无由来”的。也就是说，Signature Base String 要包含这些参数，但这些参数不能出现在 URL query string 或 Authorization header。这意味着新浪微博 server 端接收到请求后从 entity body 中提取了这些参数来进行 OAuth 签名验证。再强调一遍，对于 Content-Type 为 multipart/form-data 的 HTTP 请求，这是违反 OAuth 协议的。但在新浪微博上，我们就要是要违反 OAuth 协议才能通过 OAuth 验证。</p>
<p>即便不考虑像我这样耗时耗力的摸索过程，新浪微博的这种非标准实现也给开发者带来了很大麻烦，因为这导致了很多遵循 OAuth 协议实现的 OAuth 库无法直接使用。</p>
<p>在折腾的过程中，我还发现了另一个要命的 bug。如果 pic 的 Content-Disposition header 缺少 filename 参数的话，新浪微博也会返回“<code>40107:Oauth Error: signature_invalid!</code>”，虽然 pic 跟 OAuth 毛关系都没有，虽然根据标准 filename 不是必须的，虽然事实上 filename 在这里是没用的。</p>
<p>最后总结一下搞定新浪微博 upload API 的“正确”方法：</p>
<ol>
<li>所有参数用 multipart/form-data 格式提交，不能出现在 URL query string 或 Authorization header。</li>
<li>除 pic 以外的所有参数进入 Signature Base String 参与 OAuth 签名。</li>
<li>pic 的 Content-Disposition header 必须包含 filename 参数。</li>
</ol>
<div class="footnotes"><ol ><li id="footnote_0_585" class="footnote"><a href="http://forum.open.weibo.com/">官方论坛</a>没什么帮助，哭的多，救的少。</li><li id="footnote_1_585" class="footnote">由于没做 UTF-8 编码或 URL 编码造成的 OAuth 验证失败是开发者自己的错误。</li><li id="footnote_2_585" class="footnote">本文也同样适用于其他采用 multipart/form-data 编码方式提交 HTTP 请求的 API，如 <a href="http://open.weibo.com/wiki/Account/update_profile_image">update_profile_image</a>。</li></ol></div><div class="social">			<a href="https://twitter.com/share?url=http%3A%2F%2Fwangling.me%2F2011%2F08%2Fsina-weibo-api-upload%2F" class="twitter-share-button" data-url="http%3A%2F%2Fwangling.me%2F2011%2F08%2Fsina-weibo-api-upload%2F" data-text="搞定新浪微博 API 之 Upload" data-via="an0" data-count="none">Tweet</a>
			<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>			<script type="text/javascript" charset="utf-8">
(function(){
  var _w = 86 , _h = 18;
  var param = {
    url:"http%3A%2F%2Fwangling.me%2F2011%2F08%2Fsina-weibo-api-upload%2F",
    type:'6',
    count:'', /**是否显示分享数，1显示(可选)*/
    appkey:'', /**您申请的应用appkey,显示分享来源(可选)*/
    title:'搞定新浪微博 API 之 Upload', /**分享的文字内容(可选，默认为所在页面的title)*/
    pic:'', /**分享图片的路径(可选)*/
    ralateUid:'1676354212', /**关联用户的UID，分享微博会@该用户(可选)*/
    rnd:new Date().valueOf()
  }
  var temp = [];
  for( var p in param ){
    temp.push(p + '=' + encodeURIComponent( param[p] || '' ) )
  }
  document.write('<iframe allowTransparency="true" frameborder="0" scrolling="no" src="http://hits.sinajs.cn/A1/weiboshare.html?' + temp.join('&') + '" width="'+ _w+'" height="'+_h+'" style="margin-left:5px;"></iframe>')
})()
</script></div>]]></content:encoded>
			<wfw:commentRss>http://wangling.me/2011/08/sina-weibo-api-upload/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Time Warp in Animation</title>
		<link>http://wangling.me/2011/06/time-warp-in-animation/</link>
		<comments>http://wangling.me/2011/06/time-warp-in-animation/#comments</comments>
		<pubDate>Fri, 01 Jul 2011 00:16:30 +0000</pubDate>
		<dc:creator>an0</dc:creator>
				<category><![CDATA[development]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Core Animation]]></category>

		<guid isPermaLink="false">http://wangling.me/?p=513</guid>
		<description><![CDATA[CAMediaTiming Protocol offers a small set of eight properties. But it is sufficient to do all kinds of time warps, if you know what exactly every property means and how to use it. The most used and simple one is duration: duration Specifies the basic duration of the animation, in seconds. Nothing needs further explanation [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://developer.apple.com/library/ios/#documentation/GraphicsImaging/Reference/CAMediaTiming_protocol/Introduction/Introduction.html">CAMediaTiming Protocol</a> offers a small set of eight properties. But it is sufficient to do all kinds of time warps, if you know what exactly every property means and how to use it.</p>
<p>The most used and simple one is <em>duration</em>:</p>
<blockquote><p>
<strong>duration</strong><br />
Specifies the basic duration of the animation, in seconds.
</p></blockquote>
<p>Nothing needs further explanation here.</p>
<p>The <em>duration</em> you specify may differ from the real duration you experience, depending on the parent time space or <em>speed</em>:</p>
<blockquote><p>
<strong>speed</strong><br />
Specifies how time is mapped to receiver’s time space from the parent time space.
</p></blockquote>
<p>So an animation with <em>duration</em> of 1 second and <em>speed</em> of 1 is the same as an animation with <em>duration</em> of 2 seconds and <em>speed</em> of 2, when mapped to real time space.</p>
<p>The two repeating properties — <em>repeatCount</em> and <em>repeatDuration</em> — are as clear as they are named. So is <em>autoreverses</em>.</p>
<p>Finally, we are talking about the three interesting but not intuitive properties:</p>
<h4>beginTime</h4>
<blockquote><p>
<strong>beginTime</strong><br />
Specifies the begin time of the receiver in relation to its parent object, if applicable.
</p></blockquote>
<p>If an animation is in an animation group, <em>beginTime</em> is the offset from the beginning of its parent object — the animation group. So if <em>beginTime</em> of the animation is 5, it begins 5 seconds after the animation group begins.</p>
<p>If an animation is added directly to a layer, <em>beginTime</em> is still the offset from the beginning of its parent object — the layer. But since the beginning of a layer is in the past<sup><a href="http://wangling.me/2011/06/time-warp-in-animation/#footnote_0_513" id="identifier_0_513" class="footnote-link footnote-identifier-link" title="I guess it is the time when it is added to a layer tree">1</a></sup>, I can not simply set <em>beginTime</em> to 5 to delay the animation 5 seconds, because 5 seconds after the beginning of a layer is probably still a past time. What I usually really want is a delay relative to when the animation is added to the layer — denoted by addTime. So</p>
<p><span class="code">animation.beginTime <span style="color: #002200;">=</span> addTime <span style="color: #002200;">+</span> delay;</span></p>
<p>In order to get <em>addTime</em>, I can use <em>CACurrentMediaTime</em> and <em>convertTime:fromLayer:</em>:</p>
<p><span class="code">addTime <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>layer convertTime<span style="color: #002200;">:</span>CACurrentMediaTime<span style="color: #002200;">&#40;</span><span style="color: #002200;">&#41;</span> fromLayer<span style="color: #002200;">:</span><span style="color: #a61390;">nil</span><span style="color: #002200;">&#93;</span>;</span></p>
<p>If <em>beginTime</em> of the layer itself is to be set, addTime must be calculated after it is set because <em>beginTime</em> shifts the time space of the layer.</p>
<p>These things can be chained up:</p>
<p><span class="code">CFTimeInterval currentTime <span style="color: #002200;">=</span> CACurrentMediaTime<span style="color: #002200;">&#40;</span><span style="color: #002200;">&#41;</span>;<br />
CFTimeInterval currentTimeInSuperLayer <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>superLayer convertTime<span style="color: #002200;">:</span>currentTime fromLayer<span style="color: #002200;">:</span><span style="color: #a61390;">nil</span><span style="color: #002200;">&#93;</span>;<br />
layer.beginTime <span style="color: #002200;">=</span> currentTimeInSuperLayer <span style="color: #002200;">+</span> <span style="color: #2400d9;">2</span>;<br />
CFTimeInterval currentTimeInLayer <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>layer convertTime<span style="color: #002200;">:</span>currentTimeInSuperLayer fromLayer<span style="color: #002200;">:</span>superLayer<span style="color: #002200;">&#93;</span>;<br />
CFTimeInterval addTime <span style="color: #002200;">=</span> currentTimeInLayer;<br />
CAAnimationGroup <span style="color: #002200;">*</span>group <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>CAAnimationGroup animation<span style="color: #002200;">&#93;</span>;<br />
group.beginTime <span style="color: #002200;">=</span> addTime <span style="color: #002200;">+</span> <span style="color: #2400d9;">1</span>;<br />
group.animations <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #400080;">NSArray</span> arrayWithObject<span style="color: #002200;">:</span>anim<span style="color: #002200;">&#93;</span>;<br />
group.duration <span style="color: #002200;">=</span> <span style="color: #2400d9;">2</span>;<br />
anim.beginTime <span style="color: #002200;">=</span> <span style="color: #2400d9;">0.5</span>;<br />
<span style="color: #002200;">&#91;</span>layer addAnimation<span style="color: #002200;">:</span>group forKey<span style="color: #002200;">:</span><span style="color: #a61390;">nil</span><span style="color: #002200;">&#93;</span>;</span></p>
<h4>timeOffset</h4>
<blockquote><p>
<strong>timeOffset</strong><br />
Specifies an additional time offset in active local time.
</p></blockquote>
<p>It’s not clear what it means from the words. But with a very simple example you’ll get it.</p>
<p>Supposing a 3-second animation with states <em>s<sub>0</sub></em>, <em>s<sub>1</sub></em>, <em>s<sub>2</sub></em>, <em>s<sub>3</sub></em>, wherein <em>s<sub>n</sub></em> denotes the <em>state</em> at second <em>n</em>. The normal state sequence without <em>timeOffset</em> is:<br />
<em>s<sub>0</sub></em> ➔ <em>s<sub>1</sub></em> ➔ <em>s<sub>2</sub></em> ➔ <em>s<sub>3</sub></em></p>
<p>With <em>timeOffset</em> set to 1, the state sequence becomes:<br />
<em>s<sub>1</sub></em> ➔ <em>s<sub>2</sub></em> ➔ <em>s<sub>3</sub></em> ➔ <em>s<sub>0</sub></em></p>
<p>With <em>timeOffset</em> set to 2:<br />
<em>s<sub>2</sub></em> ➔ <em>s<sub>3</sub></em> ➔ <em>s<sub>0</sub></em> ➔ <em>s<sub>1</sub></em></p>
<p>Shift and wrap around. That’s it.</p>
<h4>fillMode</h4>
<p><em>fillMode</em> is really <a href="http://developer.apple.com/library/ios/#documentation/GraphicsImaging/Reference/CAMediaTiming_protocol/Introduction/Introduction.html%23//apple_ref/occ/intfm/CAMediaTiming/fillMode">badly documented</a>. I can figure out kCAFillModeRemoved and kCAFillModeForwards but have no idea what kCAFillModeBackwards and kCAFillModeBoth are about.</p>
<p>Fortunately, they turn out very straightforward if you see them in practice.</p>
<p>Supposing an animation in an animation group configured as follow:</p>
<p><span class="code">anim.beginTime <span style="color: #002200;">=</span> <span style="color: #2400d9;">1</span>;<br />
anim.duration <span style="color: #002200;">=</span> <span style="color: #2400d9;">3</span>;<br />
CAAnimationGroup <span style="color: #002200;">*</span>group <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>CAAnimationGroup animation<span style="color: #002200;">&#93;</span>;<br />
group.animations <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #400080;">NSArray</span> arrayWithObject<span style="color: #002200;">:</span>anim<span style="color: #002200;">&#93;</span>;<br />
group.duration <span style="color: #002200;">=</span> <span style="color: #2400d9;">5</span>;</span></p>
<p>Denote initial state of <em>anim</em> as <em>s<sub>i</sub></em> and final state as <em>s<sub>f</sub></em>. Hence in <em>group</em>’s time space:<br />
<em>s<sub>i</sub></em> = <em>s<sub>1</sub></em><br />
<em>s<sub>f</sub></em> = <em>s<sub>4</sub></em></p>
<p>Normally, there is no <em>s<sub>0</sub></em> or <em>s<sub>5</sub></em>, because <em>anim</em> is not in effect during [0, 1) or (4, 5]. And it is what kCAFillModeRemoved means.</p>
<p>With kCAFillModeForwards, the state sequence of <em>anim</em> extends forward to the end of its parent time space, that is:<br />
<em>s<sub>1</sub></em> ➔ <em>s<sub>2</sub></em> ➔ <em>s<sub>3</sub></em> ➔ <em>s<sub>4</sub></em> ➔ <em>s<sub>5</sub></em></p>
<p>But what should <em>s<sub>5</sub></em> be? I believe you can guess it.<br />
<em>s<sub>5</sub></em> = <em>s<sub>f</sub></em> = <em>s<sub>4</sub></em></p>
<p>After <em>anim</em> is completed, it remains. It is what filling forward means. Quite intuitive, if put clearly, isn’t it?</p>
<p>kCAFillModeBackwards is the mirror of kCAFillModeForwards, which we can easily tell from their names. Actually, it is.</p>
<p>If <em>fillMode</em> of <em>anim</em> is set to kCAFillModeBackwards, the state sequence extends backward to the beginning of its parent time space, that is:<br />
<em>s<sub>0</sub> ➔ </em><em>s<sub>1</sub></em> ➔ <em>s<sub>2</sub></em> ➔ <em>s<sub>3</sub></em> ➔ <em>s<sub>4</sub></em><br />
with<br />
<em>s<sub>0</sub></em> = <em>s<sub>i</sub></em> = <em>s<sub>1</sub></em></p>
<p>Before <em>anim</em> begins, it appears. It is, indeed, filling backward.</p>
<p>At this time, kCAFillModeBoth is a nothing mysterious at all. It is just kCAFillModeForwards + kCAFillModeBackwards, filling both way:<br />
<em>s<sub>0</sub> ➔ </em><em>s<sub>1</sub></em> ➔ <em>s<sub>2</sub></em> ➔ <em>s<sub>3</sub></em> ➔ <em>s<sub>4</sub></em> ➔ <em>s<sub>5</sub></em><br />
with<br />
<em>s<sub>0</sub></em> = <em>s<sub>i</sub></em> = <em>s<sub>1</sub></em><br />
<em>s<sub>5</sub></em> = <em>s<sub>f</sub></em> = <em>s<sub>4</sub></em></p>
<p>To do you a further favor, I re-document <em>fillMode</em> below, in the way it should have been documented in the first place:</p>
<blockquote><p>
<strong>fillMode</strong><br />
Determines the receiver’s presentation during its inactive duration.</p>
<p>Discussion<br />
The possible values are described in “Fill Modes”. The default is kCAFillModeRemoved.
</p></blockquote>
<blockquote><p>
Fill Modes<br />
These constants determine how the timed object behaves in its inactive duration. They are used with the fillMode property.</p>
<p>NSString * const kCAFillModeRemoved;<br />
NSString * const kCAFillModeForwards;<br />
NSString * const kCAFillModeBackwards;<br />
NSString * const kCAFillModeBoth;<br />
NSString * const kCAFillModeFrozen;</p>
<p>Constants<br />
kCAFillModeRemoved<br />
The receiver does not appear until it begins and is removed from the presentation when it is completed.</p>
<p>kCAFillModeForwards<br />
The receiver does not appear until it begins but remains visible in its final state when it is completed.</p>
<p>kCAFillModeBackwards<br />
The receiver appears in its initial state before it begins but is removed from the presentation when it is completed.</p>
<p>kCAFillModeBoth<br />
The receiver appears in its initial state before it begins and remains visible in its final state when it is completed.
</p></blockquote>
<p>That’s all. I think I’ve filled your mind quite full. You can get the sample code on <a href="https://github.com/an0/AnimationTimeWarp">github</a>.</p>
<div class="footnotes"><ol ><li id="footnote_0_513" class="footnote">I guess it is the time when it is added to a layer tree</li></ol></div><div class="social">			<a href="https://twitter.com/share?url=http%3A%2F%2Fwangling.me%2F2011%2F06%2Ftime-warp-in-animation%2F" class="twitter-share-button" data-url="http%3A%2F%2Fwangling.me%2F2011%2F06%2Ftime-warp-in-animation%2F" data-text="Time Warp in Animation" data-via="an0" data-count="none">Tweet</a>
			<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>			<script type="text/javascript" charset="utf-8">
(function(){
  var _w = 86 , _h = 18;
  var param = {
    url:"http%3A%2F%2Fwangling.me%2F2011%2F06%2Ftime-warp-in-animation%2F",
    type:'6',
    count:'', /**是否显示分享数，1显示(可选)*/
    appkey:'', /**您申请的应用appkey,显示分享来源(可选)*/
    title:'Time Warp in Animation', /**分享的文字内容(可选，默认为所在页面的title)*/
    pic:'', /**分享图片的路径(可选)*/
    ralateUid:'1676354212', /**关联用户的UID，分享微博会@该用户(可选)*/
    rnd:new Date().valueOf()
  }
  var temp = [];
  for( var p in param ){
    temp.push(p + '=' + encodeURIComponent( param[p] || '' ) )
  }
  document.write('<iframe allowTransparency="true" frameborder="0" scrolling="no" src="http://hits.sinajs.cn/A1/weiboshare.html?' + temp.join('&') + '" width="'+ _w+'" height="'+_h+'" style="margin-left:5px;"></iframe>')
})()
</script></div>]]></content:encoded>
			<wfw:commentRss>http://wangling.me/2011/06/time-warp-in-animation/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Core Animation 101: From and To</title>
		<link>http://wangling.me/2011/06/core-animation-101-from-and-to/</link>
		<comments>http://wangling.me/2011/06/core-animation-101-from-and-to/#comments</comments>
		<pubDate>Mon, 27 Jun 2011 18:53:51 +0000</pubDate>
		<dc:creator>an0</dc:creator>
				<category><![CDATA[development]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Core Animation]]></category>

		<guid isPermaLink="false">http://wangling.me/?p=475</guid>
		<description><![CDATA[Core Animation is cool, beautiful, and easy to use; but not easy to use correctly. First of all, you should RTFM(Read The Fabulous Manual). Secondly, of course, you should watch the nice Session Video 424 of WWDC 2010 and play with the sample code Animation101. Now, I assume you&#8217;ve done all the prerequisite work so [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/CoreAnimation_guide/Introduction/Introduction.html">Core Animation</a> is cool, beautiful, and easy to use; but not easy to use correctly.</p>
<p>First of all, you should <a href="http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/CoreAnimation_guide/Introduction/Introduction.html">RTFM</a>(Read The Fabulous Manual). Secondly, of course, you should watch the nice <a href="https://deimos.apple.com/WebObjects/Core.woa/BrowsePrivately/adc.apple.com.4088182973.04088182975.4092394252?i=2079986024">Session Video 424</a> of WWDC 2010 and play with the sample code Animation101.</p>
<p>Now, I assume you&#8217;ve done all the prerequisite work so you must be familiar with that bouncing ball which is our protagonist.</p>
<p>Bouncing is one of the simplest form of animation, just moving from a position to another position and back. But as you are going to see below, with some extra flexibility requirements, such a straight moving is not so straightforward in code.</p>
<p>Here is bouncing I want:</p>
<ol>
<li>First tap triggers the fall of the ball.</li>
<li>Second tap triggers the rise of the ball.</li>
<li>Second tap can happen half-way along the fall and the bouncing should start immediately at that time and position.</li>
</ol>
<p><video src="http://wangling.me/wp-content/uploads/2011/06/bouncing_ball.mov" width="320" height="480" controls></video></p>
<h4>Method 1 — Property animation</h4>
<p>This is the most straightforward and recommended method. Nothing could go wrong.</p>
<p><span class="code"><span style="color: #a61390;">if</span><span style="color: #002200;">&#40;</span>viewState <span style="color: #002200;">==</span> <span style="color: #2400d9;">0</span><span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span> <span style="color: #11740a; font-style: italic;">// First tap.</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>CATransaction setAnimationDuration<span style="color: #002200;">:</span>2<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; ball.position <span style="color: #002200;">=</span> p2;<br />
<span style="color: #002200;">&#125;</span> &nbsp;<span style="color: #a61390;">else</span> <span style="color: #002200;">&#123;</span> <span style="color: #11740a; font-style: italic;">// Second tap.</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; ball.position <span style="color: #002200;">=</span> p1;<br />
<span style="color: #002200;">&#125;</span></span></p>
<h4>Method 2 — Explicit animation with cancelation via delegate</h4>
<p>Because the second animation may happen half-way during the first animation, I need to cancel the first one.</p>
<p>But when?</p>
<p>To ask this question, you should know <a href="http://developer.apple.com/library/ios/#documentation/GraphicsImaging/Reference/CABasicAnimation_class/Introduction/Introduction.html%23//apple_ref/doc/uid/TP40004496-CH1-SW4">how Core Animation interpolates values</a>.</p>
<p>The relevant rule here is the fifth one:</p>
<blockquote><p>toValue is non-nil. Interpolates between the current value of keyPath in the target layer’s presentation layer and toValue.</p></blockquote>
<p>It is a good one for my purpose &#8211; bouncing from the current presentation position. If only I can keep the current presentation position value.</p>
<p>As soon as an property animation is canceled, the presentation value of the property is gone, and the model value is used instead. So if I cancel the first animation before the second one begins, the start position(the model value, which is never changed and always the initial position) and end position will be the same, hence no visible animation will happen.</p>
<p>Following preceding analysis, I should not cancel the first animation until the second one begins; and I must cancel it no later than the second one ends, otherwise the first one may continue after the second one ends — assuming the first one is longer than the second one.</p>
<p>Fortunately, CAAnimation has two delegate methods reporting animation progress:<br />
–animationDidStart:<br />
–animationDidStop:finished:</p>
<p>Taking them at face value, I think –animationDidStart: offers a perfect cancel point. However, it turns out that it is a bit too early<sup><a href="http://wangling.me/2011/06/core-animation-101-from-and-to/#footnote_0_475" id="identifier_0_475" class="footnote-link footnote-identifier-link" title="I guess it is because Core Animation caches the from value after this delegate method is called.">1</a></sup>, so I have to use –animationDidStop:finished:.</p>
<p><span class="code"><span style="color: #a61390;">if</span><span style="color: #002200;">&#40;</span>viewState <span style="color: #002200;">==</span> <span style="color: #2400d9;">0</span><span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span> <span style="color: #11740a; font-style: italic;">// First tap.</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; CABasicAnimation <span style="color: #002200;">*</span>move <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>CABasicAnimation animationWithKeyPath<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;position&quot;</span><span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; move.removedOnCompletion <span style="color: #002200;">=</span> <span style="color: #a61390;">NO</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; move.fillMode <span style="color: #002200;">=</span> kCAFillModeForwards;<br />
&nbsp; &nbsp; &nbsp; &nbsp; move.duration <span style="color: #002200;">=</span> <span style="color: #2400d9;">2</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; move.toValue <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #400080;">NSValue</span> valueWithCGPoint<span style="color: #002200;">:</span>p2<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>ball addAnimation<span style="color: #002200;">:</span>move forKey<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;move&quot;</span><span style="color: #002200;">&#93;</span>;<br />
<span style="color: #002200;">&#125;</span> &nbsp;<span style="color: #a61390;">else</span> <span style="color: #002200;">&#123;</span> <span style="color: #11740a; font-style: italic;">// Second tap.</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; CABasicAnimation<span style="color: #002200;">*</span> stop <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>CABasicAnimation animationWithKeyPath<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;position&quot;</span><span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>stop setValue<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;stop&quot;</span> forKey<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;tag&quot;</span><span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; stop.delegate <span style="color: #002200;">=</span> self;<br />
&nbsp; &nbsp; &nbsp; &nbsp; stop.toValue <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #400080;">NSValue</span> valueWithCGPoint<span style="color: #002200;">:</span>p1<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>ball addAnimation<span style="color: #002200;">:</span>stop forKey<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;stop&quot;</span><span style="color: #002200;">&#93;</span>;<br />
<span style="color: #002200;">&#125;</span></span></p>
<p><span class="code"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>animationDidStop<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>CAAnimation <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>anim finished<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">BOOL</span><span style="color: #002200;">&#41;</span>flag <span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #400080;">NSString</span> <span style="color: #002200;">*</span>tag <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>anim valueForKey<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;tag&quot;</span><span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span><span style="color: #002200;">&#91;</span>tag isEqualToString<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;stop&quot;</span><span style="color: #002200;">&#93;</span><span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>ball removeAnimationForKey<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;move&quot;</span><span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#125;</span><br />
<span style="color: #002200;">&#125;</span></span></p>
<h4>Method 3 — Explicit animation with overwriting</h4>
<p>As you can see, canceling the first animation <em>after</em> the second one begins is tricky. And to be honest, I&#8217;m not sure what&#8217;s the behavior of two animations of the same property occurring simultaneous.</p>
<p>A better approach is to capture the current presentation value myself and use it as the <strong>fromValue</strong>, and to cancel the first animation <em>before</em> the second one begins. It is much cleaner logically and reliable behaviorally.</p>
<p>There is a nice feature I can use to cancel an animation with another by overwriting — simply assigning the same key as the first one to the second one when adding the second one to the layer.</p>
<p><span class="code"><span style="color: #a61390;">if</span><span style="color: #002200;">&#40;</span>viewState <span style="color: #002200;">==</span> <span style="color: #2400d9;">0</span><span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span> <span style="color: #11740a; font-style: italic;">// First tap.</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; CABasicAnimation <span style="color: #002200;">*</span>move <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>CABasicAnimation animationWithKeyPath<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;position&quot;</span><span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; move.removedOnCompletion <span style="color: #002200;">=</span> <span style="color: #a61390;">NO</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; move.fillMode <span style="color: #002200;">=</span> kCAFillModeForwards;<br />
&nbsp; &nbsp; &nbsp; &nbsp; move.duration <span style="color: #002200;">=</span> <span style="color: #2400d9;">2</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; move.toValue <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #400080;">NSValue</span> valueWithCGPoint<span style="color: #002200;">:</span>p2<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>ball addAnimation<span style="color: #002200;">:</span>move forKey<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;position&quot;</span><span style="color: #002200;">&#93;</span>;<br />
<span style="color: #002200;">&#125;</span> &nbsp;<span style="color: #a61390;">else</span> <span style="color: #002200;">&#123;</span> <span style="color: #11740a; font-style: italic;">// Second tap.</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; CGPoint startPoint <span style="color: #002200;">=</span> <span style="color: #002200;">&#40;</span><span style="color: #002200;">&#40;</span>CALayer <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>ball.presentationLayer<span style="color: #002200;">&#41;</span>.position;<br />
&nbsp; &nbsp; &nbsp; &nbsp; CABasicAnimation<span style="color: #002200;">*</span> stop <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>CABasicAnimation animationWithKeyPath<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;position&quot;</span><span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; stop.fromValue <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #400080;">NSValue</span> valueWithCGPoint<span style="color: #002200;">:</span>startPoint<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; stop.toValue <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #400080;">NSValue</span> valueWithCGPoint<span style="color: #002200;">:</span>p1<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>ball addAnimation<span style="color: #002200;">:</span>stop forKey<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;position&quot;</span><span style="color: #002200;">&#93;</span>;<br />
<span style="color: #002200;">&#125;</span></span></p>
<h4>Method 4 — Fall with property animation and rise with explicit animation</h4>
<p>Though I don&#8217;t know why you would want to use this manner, I want to show you how to properly implement it.</p>
<p>The fall part is exactly the same as that of Method 1, while the rise part is almost the same as that of Method 3. Reasonable enough. However, there are two things you must pay attention to:</p>
<ol>
<li>Property animations carry implicit keys named after their key paths. So the key &#8220;position&#8221; is used for the second animation to overwrite the first one which is a position property animation.</li>
<li>Even though I&#8217;m using explicit animation for the rise part, I still need to reset the model value because presentation changes do not update modal layers while modal changes do update presentation layers.</li>
</ol>
<p><span class="code"><span style="color: #a61390;">if</span><span style="color: #002200;">&#40;</span>viewState <span style="color: #002200;">==</span> <span style="color: #2400d9;">0</span><span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span> <span style="color: #11740a; font-style: italic;">// First tap.</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>CATransaction setAnimationDuration<span style="color: #002200;">:</span>2<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; ball.position <span style="color: #002200;">=</span> p2;<br />
<span style="color: #002200;">&#125;</span> &nbsp;<span style="color: #a61390;">else</span> <span style="color: #002200;">&#123;</span> <span style="color: #11740a; font-style: italic;">// Second tap.</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; CGPoint startPoint <span style="color: #002200;">=</span> <span style="color: #002200;">&#40;</span><span style="color: #002200;">&#40;</span>CALayer <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>ball.presentationLayer<span style="color: #002200;">&#41;</span>.position;<br />
&nbsp; &nbsp; &nbsp; &nbsp; ball.position <span style="color: #002200;">=</span> p1;<br />
&nbsp; &nbsp; &nbsp; &nbsp; CABasicAnimation<span style="color: #002200;">*</span> stop <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>CABasicAnimation animationWithKeyPath<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;position&quot;</span><span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; stop.fromValue <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #400080;">NSValue</span> valueWithCGPoint<span style="color: #002200;">:</span>startPoint<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; stop.toValue <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #400080;">NSValue</span> valueWithCGPoint<span style="color: #002200;">:</span>p1<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>ball addAnimation<span style="color: #002200;">:</span>stop forKey<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;position&quot;</span><span style="color: #002200;">&#93;</span>;<br />
<span style="color: #002200;">&#125;</span></span></p>
<h4>Method 5 — Fall with explicit animation and rise with property animation</h4>
<p>Just to be complete. Still two notice points:</p>
<ol>
<li>The key &#8220;position&#8221; is used for the first animation so that the second one, which is a position property animation, can implicitly overwrite the first one.</li>
<li>I must set the model value of position away from the initial value<sup><a href="http://wangling.me/2011/06/core-animation-101-from-and-to/#footnote_1_475" id="identifier_1_475" class="footnote-link footnote-identifier-link" title="Though I set it to the destination position of fall for consistency, you can set it to any value that is different from the initial value.">2</a></sup>, otherwise when I set the modal value back to the initial value Core Animation will not notice any change of the model value and will not trigger any animation.</li>
</ol>
<p><span class="code"><span style="color: #a61390;">if</span><span style="color: #002200;">&#40;</span>viewState <span style="color: #002200;">==</span> <span style="color: #2400d9;">0</span><span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span> <span style="color: #11740a; font-style: italic;">// First tap.</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; CGPoint startPoint <span style="color: #002200;">=</span> ball.position;<br />
&nbsp; &nbsp; &nbsp; &nbsp; ball.position <span style="color: #002200;">=</span> p2;<br />
&nbsp; &nbsp; &nbsp; &nbsp; CABasicAnimation <span style="color: #002200;">*</span>move <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>CABasicAnimation animationWithKeyPath<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;position&quot;</span><span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; move.removedOnCompletion <span style="color: #002200;">=</span> <span style="color: #a61390;">NO</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; move.fillMode <span style="color: #002200;">=</span> kCAFillModeForwards;<br />
&nbsp; &nbsp; &nbsp; &nbsp; move.duration <span style="color: #002200;">=</span> <span style="color: #2400d9;">2</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; move.fromValue <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #400080;">NSValue</span> valueWithCGPoint<span style="color: #002200;">:</span>startPoint<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; move.toValue <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #400080;">NSValue</span> valueWithCGPoint<span style="color: #002200;">:</span>p2<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>ball addAnimation<span style="color: #002200;">:</span>move forKey<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;position&quot;</span><span style="color: #002200;">&#93;</span>;<br />
<span style="color: #002200;">&#125;</span> &nbsp;<span style="color: #a61390;">else</span> <span style="color: #002200;">&#123;</span> <span style="color: #11740a; font-style: italic;">// Second tap.</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; ball.position <span style="color: #002200;">=</span> p1;<br />
<span style="color: #002200;">&#125;</span></span></p>
<p>You can get the sample code on <a href="https://github.com/an0/AnimationFromTo">github</a>.</p>
<div class="footnotes"><ol ><li id="footnote_0_475" class="footnote">I guess it is because Core Animation caches the <em>from value</em> <strong>after</strong> this delegate method is called.</li><li id="footnote_1_475" class="footnote">Though I set it to the destination position of fall for consistency, you can set it to any value that is different from the initial value.</li></ol></div><div class="social">			<a href="https://twitter.com/share?url=http%3A%2F%2Fwangling.me%2F2011%2F06%2Fcore-animation-101-from-and-to%2F" class="twitter-share-button" data-url="http%3A%2F%2Fwangling.me%2F2011%2F06%2Fcore-animation-101-from-and-to%2F" data-text="Core Animation 101: From and To" data-via="an0" data-count="none">Tweet</a>
			<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>			<script type="text/javascript" charset="utf-8">
(function(){
  var _w = 86 , _h = 18;
  var param = {
    url:"http%3A%2F%2Fwangling.me%2F2011%2F06%2Fcore-animation-101-from-and-to%2F",
    type:'6',
    count:'', /**是否显示分享数，1显示(可选)*/
    appkey:'', /**您申请的应用appkey,显示分享来源(可选)*/
    title:'Core Animation 101: From and To', /**分享的文字内容(可选，默认为所在页面的title)*/
    pic:'', /**分享图片的路径(可选)*/
    ralateUid:'1676354212', /**关联用户的UID，分享微博会@该用户(可选)*/
    rnd:new Date().valueOf()
  }
  var temp = [];
  for( var p in param ){
    temp.push(p + '=' + encodeURIComponent( param[p] || '' ) )
  }
  document.write('<iframe allowTransparency="true" frameborder="0" scrolling="no" src="http://hits.sinajs.cn/A1/weiboshare.html?' + temp.join('&') + '" width="'+ _w+'" height="'+_h+'" style="margin-left:5px;"></iframe>')
})()
</script></div>]]></content:encoded>
			<wfw:commentRss>http://wangling.me/2011/06/core-animation-101-from-and-to/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
<enclosure url="http://wangling.me/wp-content/uploads/2011/06/bouncing_ball.mov" length="90643" type="video/quicktime" />
		</item>
		<item>
		<title>Int Is Difficult</title>
		<link>http://wangling.me/2011/06/int-is-difficult/</link>
		<comments>http://wangling.me/2011/06/int-is-difficult/#comments</comments>
		<pubDate>Sun, 26 Jun 2011 18:22:54 +0000</pubDate>
		<dc:creator>an0</dc:creator>
				<category><![CDATA[development]]></category>

		<guid isPermaLink="false">http://wangling.me/?p=468</guid>
		<description><![CDATA[Int is the simplest data type, isn&#8217;t it? Yes, most of the time. No, not really: What&#8217;s the value of b? It&#8217;s just two lines of code, so don&#8217;t resort to your awesome compiler, simply give me the answer. NSUInteger a = 7; NSInteger b = MAX&#40;4 - a, 1&#41;; What&#8217;s the problem of this [...]]]></description>
			<content:encoded><![CDATA[<p>Int is the simplest data type, isn&#8217;t it?</p>
<p>Yes, most of the time.</p>
<p>No, not really:</p>
<ol>
<li>
What&#8217;s the value of <em>b</em>? It&#8217;s just two lines of code, so don&#8217;t resort to  your awesome compiler, simply give me the answer.<br />
<span class="code">NSUInteger a <span style="color: #002200;">=</span> <span style="color: #2400d9;">7</span>;<br />
NSInteger b <span style="color: #002200;">=</span> MAX<span style="color: #002200;">&#40;</span>4 <span style="color: #002200;">-</span> a, 1<span style="color: #002200;">&#41;</span>;</span>
</li>
<li>
What&#8217;s the problem of this trivial loop? You are allowed to use compiler this time, but be cautious that <strong>sometimes</strong> you don&#8217;t have enough time to <strong>finish</strong> even such a simple loop.<br />
<span class="code"><span style="color: #a61390;">for</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">int</span> i <span style="color: #002200;">=</span> <span style="color: #2400d9;">0</span>; i &lt;<span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>someArray count<span style="color: #002200;">&#93;</span> <span style="color: #002200;">-</span> <span style="color: #2400d9;">1</span>; i<span style="color: #002200;">++</span><span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #11740a; font-style: italic;">// Do something.</span><br />
<span style="color: #002200;">&#125;</span></span>
</li>
</ol>
<p>If you can figure out the answers by yourself, you are AWESOME! If you can&#8217;t, it is OK, really, because int is difficult. Take it easy and check out the discussion <a href="https://devforums.apple.com/message/407756">here</a>(Apple Developer Program member only).</p>
<p>If you can&#8217;t access <a href="https://devforums.apple.com/">Apple Developer Forums</a> but still want to know what&#8217;s going on, here is the revelation from <a href="http://c0x.coding-guidelines.com/6.3.1.8.html">C99</a>:</p>
<blockquote><p>
Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.
</p></blockquote>
<div class="social">			<a href="https://twitter.com/share?url=http%3A%2F%2Fwangling.me%2F2011%2F06%2Fint-is-difficult%2F" class="twitter-share-button" data-url="http%3A%2F%2Fwangling.me%2F2011%2F06%2Fint-is-difficult%2F" data-text="Int Is Difficult" data-via="an0" data-count="none">Tweet</a>
			<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>			<script type="text/javascript" charset="utf-8">
(function(){
  var _w = 86 , _h = 18;
  var param = {
    url:"http%3A%2F%2Fwangling.me%2F2011%2F06%2Fint-is-difficult%2F",
    type:'6',
    count:'', /**是否显示分享数，1显示(可选)*/
    appkey:'', /**您申请的应用appkey,显示分享来源(可选)*/
    title:'Int Is Difficult', /**分享的文字内容(可选，默认为所在页面的title)*/
    pic:'', /**分享图片的路径(可选)*/
    ralateUid:'1676354212', /**关联用户的UID，分享微博会@该用户(可选)*/
    rnd:new Date().valueOf()
  }
  var temp = [];
  for( var p in param ){
    temp.push(p + '=' + encodeURIComponent( param[p] || '' ) )
  }
  document.write('<iframe allowTransparency="true" frameborder="0" scrolling="no" src="http://hits.sinajs.cn/A1/weiboshare.html?' + temp.join('&') + '" width="'+ _w+'" height="'+_h+'" style="margin-left:5px;"></iframe>')
})()
</script></div>]]></content:encoded>
			<wfw:commentRss>http://wangling.me/2011/06/int-is-difficult/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>No Highlight, Please</title>
		<link>http://wangling.me/2010/09/no-highlight-pleas/</link>
		<comments>http://wangling.me/2010/09/no-highlight-pleas/#comments</comments>
		<pubDate>Sat, 11 Sep 2010 00:53:09 +0000</pubDate>
		<dc:creator>an0</dc:creator>
				<category><![CDATA[development]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[UIButton]]></category>
		<category><![CDATA[UITableViewCell]]></category>
		<category><![CDATA[Voodo]]></category>

		<guid isPermaLink="false">http://wangling.me/?p=290</guid>
		<description><![CDATA[There is a little annoying thing in Voodo&#8216;s Task List view from the very beginning. When you simply select a task to view the Task Info, the check box of that row will turn into being-checked from unchecked (or unchecked from checked, depending on the current state). It may confuse users to think that they [...]]]></description>
			<content:encoded><![CDATA[<p>There is a little annoying thing in <a href="http://iwonderphone.com/voodo/">Voodo</a>&#8216;s Task List view from the very beginning. When you simply select a task to view the Task Info, the check box of that row will turn into being-checked from unchecked (or unchecked from checked, depending on the current state). It may confuse users to think that they just inadvertently checked (or unchecked) the task.</p>
<p><img src="http://wangling.me/wp-content/uploads/2010/09/Bad-Highlight.png" alt="" title="Bad Highlight" width="642" height="957" class="aligncenter size-full wp-image-307" /></p>
<p>After some reverse engineering, I found it is because when a <strong>UITableViewCell</strong> gets highlighted it also <em>cleverly</em> highlight all its subviews. It is reasonable in some aspects but unfavorable sometimes. And it is especially annoying and confusing in Voodo.</p>
<p>I noticed this problem the first time I do selection in Task List view, however, I left it aside because</p>
<ol>
<li>It is a very small and transient visual defect;</li>
<li>I see the exactly same problem in many other <strong>UITableView</strong> based apps with <strong>UIButton</strong>s inside <strong>UITableViewCell</strong>s;</li>
<li>I didn&#8217;t find a simple fix;</li>
<li>No one complains about it.</li>
</ol>
<p>Yes, not a single one user has ever complained about this problem to me &#8211; I love you Voodoers, you are so forgiving &#8211; except one, my wife.</p>
<p>Though she just began using <a href="http://iwonderphone.com/voodo/">Voodo</a> for her MBA study<sup><a href="http://wangling.me/2010/09/no-highlight-pleas/#footnote_0_290" id="identifier_0_290" class="footnote-link footnote-identifier-link" title="You have so many things to schedule as an MBA student that you just can&amp;#8217;t simply keep them in your head. So almost all her classmates with an iPhone or iPod touch picked up Voodo when I kindly offered them a Promo Code, and they do use it every day.">1</a></sup>, she found quite some little problems with her picky eyes. Maybe it is because she likes painting so is more visually sensitive.</p>
<p>So I decided to fix it, finally.</p>
<p>I first tried several plain old ways with the hope that new SDK make <strong>UITableViewCell</strong> more easily configurable, to no avail. I searched the whole web, to no avail. OK, I said to my self, I know I can do it, maybe I should try every plausible properties and methods of <strong>UITableViewCell</strong>. Once again, I made it, as every time before when I told myself I wanted it done whatever. Determination works!</p>
<p>It turns out very straightforward. You just need to override the two relevant methods of <strong>UITableViewCell</strong> &#8211; <strong>setHighlighted:animated:</strong> and <strong>setSelected:animated:</strong> as follows:<br />
<span class="code"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>setHighlighted<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">BOOL</span><span style="color: #002200;">&#41;</span>highlighted animated<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">BOOL</span><span style="color: #002200;">&#41;</span>animated <span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>super setHighlighted<span style="color: #002200;">:</span>highlighted animated<span style="color: #002200;">:</span>animated<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #11740a; font-style: italic;">// Don't highlight check button to confuse users.</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; self.checkButton.highlighted <span style="color: #002200;">=</span> <span style="color: #a61390;">NO</span>;&nbsp; &nbsp; &nbsp; <br />
<span style="color: #002200;">&#125;</span><br />
<br />
<span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>setSelected<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">BOOL</span><span style="color: #002200;">&#41;</span>selected animated<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">BOOL</span><span style="color: #002200;">&#41;</span>animated <span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>super setSelected<span style="color: #002200;">:</span>selected animated<span style="color: #002200;">:</span>animated<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #11740a; font-style: italic;">// Don't highlight check button to confuse users.</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; self.checkButton.highlighted <span style="color: #002200;">=</span> <span style="color: #a61390;">NO</span>;<br />
<span style="color: #002200;">&#125;</span></span></p>
<p>Notice that overriding the inanimate peers does not work, maybe because the whole thing occurs during a transition animation.</p>
<p>Hope it is useful to other iOS developers with the same problem. </p>
<div class="footnotes"><ol ><li id="footnote_0_290" class="footnote">You have so many things to schedule as an MBA student that you just can&#8217;t simply keep them in your head. So almost all her classmates with an iPhone or iPod touch picked up <a href="http://iwonderphone.com/voodo/">Voodo</a> when I kindly offered them a Promo Code, and they do use it every day.</li></ol></div><div class="social">			<a href="https://twitter.com/share?url=http%3A%2F%2Fwangling.me%2F2010%2F09%2Fno-highlight-pleas%2F" class="twitter-share-button" data-url="http%3A%2F%2Fwangling.me%2F2010%2F09%2Fno-highlight-pleas%2F" data-text="No Highlight, Please" data-via="an0" data-count="none">Tweet</a>
			<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>			<script type="text/javascript" charset="utf-8">
(function(){
  var _w = 86 , _h = 18;
  var param = {
    url:"http%3A%2F%2Fwangling.me%2F2010%2F09%2Fno-highlight-pleas%2F",
    type:'6',
    count:'', /**是否显示分享数，1显示(可选)*/
    appkey:'', /**您申请的应用appkey,显示分享来源(可选)*/
    title:'No Highlight, Please', /**分享的文字内容(可选，默认为所在页面的title)*/
    pic:'', /**分享图片的路径(可选)*/
    ralateUid:'1676354212', /**关联用户的UID，分享微博会@该用户(可选)*/
    rnd:new Date().valueOf()
  }
  var temp = [];
  for( var p in param ){
    temp.push(p + '=' + encodeURIComponent( param[p] || '' ) )
  }
  document.write('<iframe allowTransparency="true" frameborder="0" scrolling="no" src="http://hits.sinajs.cn/A1/weiboshare.html?' + temp.join('&') + '" width="'+ _w+'" height="'+_h+'" style="margin-left:5px;"></iframe>')
})()
</script></div>]]></content:encoded>
			<wfw:commentRss>http://wangling.me/2010/09/no-highlight-pleas/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Defects of KVO</title>
		<link>http://wangling.me/2010/07/defects-of-kvo/</link>
		<comments>http://wangling.me/2010/07/defects-of-kvo/#comments</comments>
		<pubDate>Sat, 17 Jul 2010 15:12:13 +0000</pubDate>
		<dc:creator>an0</dc:creator>
				<category><![CDATA[development]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Mac]]></category>

		<guid isPermaLink="false">http://wangling.me/?p=272</guid>
		<description><![CDATA[KVO(Key-Value Observing) is a very nice programming facility from Apple. Working together with KVC(Key-Value Coding), it makes the life of Apple developers a lot easier and happier. However, I recently found some annoying problems of KVO while building some reusable programming components1 for Voodo and future iOS projects. Observers can not be queried Observers are [...]]]></description>
			<content:encoded><![CDATA[<p>KVO(<a href="http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html">Key-Value Observing</a>) is a very nice programming facility from Apple. Working together with KVC(<a href="http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/KeyValueCoding/KeyValueCoding.html">Key-Value Coding</a>), it makes the life of Apple developers a lot easier and happier.</p>
<p>However, I recently found some annoying problems of KVO while building some reusable programming components<sup><a href="http://wangling.me/2010/07/defects-of-kvo/#footnote_0_272" id="identifier_0_272" class="footnote-link footnote-identifier-link" title="I&amp;#8217;ll open source as much as possible when ready.">1</a></sup> for <a href="http://iwonderphone.com/voodo/">Voodo</a> and future iOS projects.</p>
<h4>Observers can not be queried</h4>
<p>Observers are stored in dictionaries<sup><a href="http://wangling.me/2010/07/defects-of-kvo/#footnote_1_272" id="identifier_1_272" class="footnote-link footnote-identifier-link" title="See NSKeyValueObserving protocol&amp;#8217;s observationInfo.">2</a></sup>, so it should be very easy to query whether an object A is a registered observer of object B. But one can&#8217;t do that.</p>
<h4>Unregistering an unregistered observer throws exception</h4>
<p>One can not query whether A is an observer of B in the first place, and can neither try to unregister it from B when one wants to ensure that A is unregistered from B. It is just ridiculous! And why is removing a nonexistent observer so fatal while removing a nonexistent entry from a collection is reasonably ignored?<sup><a href="http://wangling.me/2010/07/defects-of-kvo/#footnote_2_272" id="identifier_2_272" class="footnote-link footnote-identifier-link" title="See NSMutableArray&amp;#8217;s removeObject: and NSMutableDictionary&amp;#8217;s removeObjectForKey:">3</a></sup></p>
<h4>Observers/observees don&#8217;t auto-dissolve the KVO relationship in dealloc</h4>
<p>It can be easily done in KVO framework code since it already has the data structure keeping the observer-observee relationship information, and is very reasonable things to do &#8211; when any part of a relationship is gone the relationship is ended. Without the auto-unregistration, developers&#8217; responsibility is unnecessarily heavier. In fact, it is no easy work in cases where observers outlive observees, because an observee needs to notify its observers of its death so the observers can detach themselves from the dying observee.<sup><a href="http://wangling.me/2010/07/defects-of-kvo/#footnote_3_272" id="identifier_3_272" class="footnote-link footnote-identifier-link" title="Normally it is the observers that are doing the KVO relationship management and it is the job of whoever did the connection to do the disconnection, but vice versa">4</a></sup></p>
<h3>All in all</h3>
<p>KVO leaves some extra, tedious, and burdensome relationship management work to us while it could be easily and efficiently done at the root of itself.<sup><a href="http://wangling.me/2010/07/defects-of-kvo/#footnote_4_272" id="identifier_4_272" class="footnote-link footnote-identifier-link" title="Well, as Appler eskimo1 said in devforums, it is harder than one might think because KVO needs to be both thread safe and GC clean, and must be careful to not impact the performance of code that isn&amp;#8217;t using it.">5</a></sup></p>
<p>The KVO relationship management is especially stressful for observers watching many dynamic observees which is not unusual for generic components &#8211; <a href="https://developer.apple.com/iphone/library/documentation/UIKit/Reference/UINavigationController_Class/Reference/Reference.html">UINavigationController</a> and <a href="https://developer.apple.com/iphone/library/documentation/UIKit/Reference/UITabBarController_Class/Reference/Reference.html">UITabBarController</a> both manage a dynamic group of UIViewControllers and may observe some properties of them. It is even worse in my component because not only the observee but also the observed properties of them are dynamic. May Apple save me (and you?).</p>
<div class="footnotes"><ol ><li id="footnote_0_272" class="footnote">I&#8217;ll open source as much as possible when ready.</li><li id="footnote_1_272" class="footnote">See NSKeyValueObserving protocol&#8217;s <a href="https://developer.apple.com/iphone/library/documentation/Cocoa/Reference/Foundation/Protocols/NSKeyValueObserving_Protocol/Reference/Reference.html#//apple_ref/occ/instm/NSObject/observationInfo">observationInfo</a>.</li><li id="footnote_2_272" class="footnote">See NSMutableArray&#8217;s <a href="http://developer.apple.com/iphone/library/documentation/Cocoa/Reference/Foundation/Classes/NSMutableArray_Class/Reference/Reference.html#//apple_ref/occ/instm/NSMutableArray/removeObject:">removeObject:</a> and NSMutableDictionary&#8217;s <a href="http://developer.apple.com/iphone/library/documentation/Cocoa/Reference/Foundation/Classes/NSMutableDictionary_Class/Reference/Reference.html#//apple_ref/occ/instm/NSMutableDictionary/removeObjectForKey:">removeObjectForKey:</a></li><li id="footnote_3_272" class="footnote">Normally it is the observers that are doing the KVO relationship management and it is the job of whoever did the connection to do the disconnection, but vice versa</li><li id="footnote_4_272" class="footnote">Well, as Appler eskimo1 said in <a href="https://devforums.apple.com/message/259690#259690">devforums</a>, it is harder than one might think because <q>KVO needs to be both thread safe and GC clean, and must be careful to not impact the performance of code that isn&#8217;t using it</q>.</li></ol></div><div class="social">			<a href="https://twitter.com/share?url=http%3A%2F%2Fwangling.me%2F2010%2F07%2Fdefects-of-kvo%2F" class="twitter-share-button" data-url="http%3A%2F%2Fwangling.me%2F2010%2F07%2Fdefects-of-kvo%2F" data-text="Defects of KVO" data-via="an0" data-count="none">Tweet</a>
			<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>			<script type="text/javascript" charset="utf-8">
(function(){
  var _w = 86 , _h = 18;
  var param = {
    url:"http%3A%2F%2Fwangling.me%2F2010%2F07%2Fdefects-of-kvo%2F",
    type:'6',
    count:'', /**是否显示分享数，1显示(可选)*/
    appkey:'', /**您申请的应用appkey,显示分享来源(可选)*/
    title:'Defects of KVO', /**分享的文字内容(可选，默认为所在页面的title)*/
    pic:'', /**分享图片的路径(可选)*/
    ralateUid:'1676354212', /**关联用户的UID，分享微博会@该用户(可选)*/
    rnd:new Date().valueOf()
  }
  var temp = [];
  for( var p in param ){
    temp.push(p + '=' + encodeURIComponent( param[p] || '' ) )
  }
  document.write('<iframe allowTransparency="true" frameborder="0" scrolling="no" src="http://hits.sinajs.cn/A1/weiboshare.html?' + temp.join('&') + '" width="'+ _w+'" height="'+_h+'" style="margin-left:5px;"></iframe>')
})()
</script></div>]]></content:encoded>
			<wfw:commentRss>http://wangling.me/2010/07/defects-of-kvo/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

