SS02 Some Speeches

一些前后呼应的演讲们。

Steve Jobs, Stanford University, 2005
Melinda Gates, Duke University, 2013
Tim Cook, Stanford University, 2019
Tim Cook, Duke University, 2018

另外一个有趣的事情:从标题里认识了叫commencement 的单词。查词典知道这里指的是学位授予仪式,似乎在中文里可以理解成“毕业典礼”(我还没毕业不知道学位授予是否在毕业典礼上)这种在我的中文思维里代表“结束”的东西。但和“结束”恰恰相反,它的本义是“开始”。

SS01 拥抱小熊猫:石之予和她的Turning Red

这是我博客的一个新模块,ShortSharing,简称SS,用简短的话分享一些最近觉得有意思的东西。

这个文章写于2022年5月9日。

几周之前看了Turning Red 。虽然它并没有在中国的院线上映,但国内的讨论度应该还是不低的。不过,似乎很少有人去讨论它的创作团队。

今天放弃了明天截止的作业,在Disney+ 瞎逛的时候看到了一部叫《拥抱小熊猫》的纪录片,讲的是Turning Red 的创作故事,这才知道这是导演石之予第一次导一个“大制作”的电影。在维基百科查了一下才知道,她的历程也挺有意思,曾经给很多动画公司(包括Pixar)投递简历都被拒了。

内容具体讲的什么就不说了,但我感觉值得一看。如果你看过Turning Red,欢迎你去Disney+ 上收看这个纪录片。

同时推荐一下导演石之予曾经的一个短片作品,叫《包宝宝》(Bao,也非常有意思。

Swift UI Bug: 首次调用sheet时不会取用最新的@State 属性

也许是机器人也许是真人,但是从统计数字来看每天都有一些朋友到访我的博客。其中真人的很大一部分应该都引流自几年前我发布的算法竞赛题目的题解。(即使现在看来,只有那么一两篇题解是写的比较好的)

不过从这篇文章开始,本低水平算法和生活博客就会涉及一个新的领域——Apple 平台的开发了。这样做对我的好处很多,记录下来自己在学开发的过程中遇到的问题和心得,对于自己的理解和掌握有很大帮助。在这个过程中可以遇到在做相类似事情的朋友,可以得到很多指正的机会。同时鉴于Swift(现在我并没有学Objective-C) 作为一个比较新且受众不那么大的语言,这些博客也可以为Swift 学习提供一定的中文资源。即使大部分我写到的东西会和之前的算法内容一样,低水平。

最近在完成一个Swift 项目时遇到一个非常神奇的问题。在经过和几个Swift 交流群的朋友讨论以及一些信息搜索之后,基本上可以确认是一个Swift UI 的bug。

考虑这样一个场景:

你有一个Swift UI的视图,它有一个@State 的属性number。你用Swift UI给出了1个文本框和2个按钮。文本框用于显示number 的值,按下A按钮时,number 会加1。按下B按钮时,会呼出1个sheet,这个sheet中只有一个显示number 的值的文本框,其实是和前面的那个文本框一样。

你可以查看下面这个GIF 来理解这个视图的作用:

正常运作的Swift UI

你可能在想,这里面哪里有什么bug呢?当点击增加按钮的时候,number 被增加了,并且number 的这个改变也同步到了视图和sheet 的文本框里。所以,这样的写法里没有触发Swift UI 的bug。

上面这个视图的代码如下:

struct TestView: View {
    @State private var number:Int = 0
    @State private var showSheet = false
    var body: some View {
        VStack{
            Button(action: {//一个按钮,用于给number增加1
                number = self.number + 1
            }){
                Text("A:Add 1 to Number")
                    .font(.largeTitle)
            }
            
            Button(action:{//一个按钮,用于呼出一个显示number数值的sheet
                showSheet.toggle()
            }){
                Text("B:Show Number")
                    .font(.largeTitle)
            }
            
            Text("Number = \(number)")//用于实时显示number数值的文本框
                .font(.largeTitle)
        }
        .sheet(isPresented: $showSheet){
            Text("Number = \(number)")
                .font(.largeTitle)
        }
    }
}

但当你尝试删除这个视图本身的文本框(而不是sheet里的文本框)时,这个bug 就会被触发了。删除原视图的文本框后演示如下,请仔细观察。可以稍微等待,直到GIF 重新开始播放:

Swift UI 的bug 被触发

发现什么了吗?

在我连续点击3次增加number 的按钮之后,初次唤醒sheet 时,sheet 中显示的number 仍然是0! 也就是number 的初始值,而不是它当时的真实值(3)。初次唤醒sheet 之后,当我们再对number 进行更改时,它的改变才会同步到sheet 上。

引起这个区别的,仅仅是我们删除了原视图中的文本框。但文本框本身对于number 是只读的,不会对number 进行任何的修改。

也就是说,如果原视图中没有对某个@State 属性的调用,那么,第一次呼出sheet 时,传入sheet 的这个属性会是它的初始值,而不是它实时的真实值。

虽然我对于Swift UI 的机制并没有非常深入的了解,不能非常准确地解释这个bug 出现的原因,但是可以大致猜测:如果原视图里没有调用@State 属性的视图组件,那么在首次唤醒sheet 之前,@State 属性的变化不会通知到Swift UI 。这个联系会在初次调用sheet 之后才建立起来。

搜索相关的资料,发现早有关于这个问题的讨论。这个问题在iOS 14之后开始出现,并且直到现在(2022年5月)也没有修复。相关的帖子可以参考:

Sheet sees the variable change from the second time | Apple Developer Forums

SwiftUI @State and .sheet() ios13 vs ios14 – Stack Overflow

那么如何解决这个问题?

首先,我们使用上面帖子中回复者提供的方法,你可以用@Binding 的方式从原视图向sheet 传递值。(前提是你要把sheet 单独写成一个View 的struct 而不是一个闭包)

如果你不想把sheet 单独写成一个视图,就想用闭包的方式来描述它,另外的解决方法是:根据我上面对bug 机制的猜测,由于原视图里没有对@State 属性的调用,才导致了首次呼出sheet 的时候使用的值不是最新。我们可以在原视图里添加一些对@State 属性的引用,例如:

struct TestView: View {
    @State private var number:Int = 0
    @State private var showSheet = false
    var body: some View {
        VStack{
            Button(action: {//一个按钮,用于给number增加1
                number = self.number + 1
            }){
                Text("A:Add 1 to Number")
                    .font(.largeTitle)
            }
            
            Button(action:{//一个按钮,用于呼出一个显示number数值的sheet
                showSheet.toggle()
            }){
                Text("B:Show Number")
                    .font(.largeTitle)
            }
            
            if(number == -1){
                Text("Hello World!")
            }
//            Text("Number = \(number)")//用于实时显示number数值的文本框
//                .font(.largeTitle)
        }
        .sheet(isPresented: $showSheet){
            Text("Number = \(number)")
                .font(.largeTitle)
        }
    }
}

上述代码的第22至24行:

if(number == -1){
    Text("Hello World!")
}

通过一个if 语句对number 进行了调用。但从这个视图的操作逻辑来看,number == -1 是永远不可能成立的,所以这个if 不会对视图造成任何影响,也就是说这个“Hello World”文本框永远不可能被显示。

你也可以用类似的if 语句,使用在你的视图中永远不可能成立的判断语句,对可能传入sheet 的@State 属性进行调用,从而避免Swift UI 的这个bug。

在增加了这个if 判断之后,即使原视图中没有调用number 的文本框,sheet 也能正确地取到number 的值了。

使用if 规避这个Swift UI bug 后的效果

希望Apple 能尽早修复这个bug 。毕竟改用@Binding 有些麻烦,而且在代码里掺入这些if 比较影响可读性。

既然这个问题已经出现了一年多,而且已经有许多相关的讨论,那么很可能已经有人向Apple 提出过这个问题了。(BTW,怎么看到关于Swift UI 的issue?)期待一下一个月之后的WWDC22 能解决这个问题。(如果没有,我就再去反馈一次)

WWDC22

whole streets of memory

回家有几天了。其实街道也还是那么熟悉。除了很多地面都已经翻新了之外。

一切也都很熟悉。

我又去了以前每个下午两点都会去的车站。想起去年的上半年,有那么多天,我提前预约好饮料,在某店配送范围的最边缘,等骑手送过来。有好几次提前了,有好几次迟到了。提前的时候我睡不好午觉,迟到的时候当然我也上学迟到了。

后来不用官方的小程序,在美团上面点单,那么配送范围会更广一些,可以让骑手送到学校附近的复印店里。我去取的时候给店里付一元钱就行了。

这个操作贡献了我全年80%以上的饮料账单。因为上大学之后附近都没有我想喝的饮品店。

到处都贴着四川天府健康通的二维码。其实小程序应该是可以带参数打开的,没有必要提供两个二维码,让用户先扫第一个再扫第二个。一来,不会从“最近使用”里找小程序的人需要扫两次很麻烦,二来扫一个码的时候很可能误扫到另外一个。即使微信提供了一屏中出现多个二维码时候的选择功能,但假如两个二维码没有同时出现,在其中一个进入摄像头的时候微信就会把它扫到了。何况同时出现的时候需要再选择一下,体验增加了步骤。当然天府健康通其他部分还是蛮不错的。

吃完晚饭在市中心逛了逛。很多门面都易主了。很多回忆涌上来。马家巷门口的快乐柠檬,一两年前换成了一点点,现在已经换成了一家水果店,而一点点往旁边挪了几家门店的位置。以前卖鸡排的地方,现在变成了蜜雪冰城。

大概是去年的这个时候,舍友们聚了一次餐,在中心医院附近的那家吉布鲁自助餐厅。现在那家店也换成了卖衣服的以纯。

还有好多好多。比如以前卖过假冒全新机的一家手机店。比如小学旁边的爱达乐和琴行。比如一中旁边的一家小作坊奶茶店,还有它对面的那家我吃过夜宵的烧烤店,都是那时玩的不错的几个一中朋友带我去过的。都没了。

但也有些看起来不怎么赚钱的店还在。比如南街的一家牛奶店,以前我在午托班看到了这家牛奶店的宣传单,说是巴氏杀菌法,自养牧场。看到宣传单上的图片我感觉很好喝,离我的南街小学也不远,放学之后我就拉着外公带我去买。买了几回。也不便宜。不知道后来为什么就没买过了。很难相信这家店依然坚挺。

还有一家,南街小学旁边的汽修店。我初中的时候有一个德国人在和店家对话,两个人互相不能理解,陷入僵局。我正好路过,充当了一下翻译。老板娘的儿子在清华上学,她希望这个德国人加一下儿子微信,好让他们练习英语。我告诉那个德国人之后,他说他是英语老师。我好像说That’s great还是啥的,就告诉了老板娘这个事情。他们就加了微信。事后我想起来,他说他是英语老师的意思可能是,他的工作就是教英语(为什么不教德语?),也许老板娘最好不要期望他免费的劳动。不知道后来他们聊的怎么样。但我也是那一次才知道老板娘儿子在清华。

也就随便写写。没有什么主题。明天要早起。所以今晚早睡身体好。

19岁

晚了十几分钟,已经到生日的第二天了。

好像很久都没有像今天这样过过生日了。本来想把肉吃爽没想到要么没来得及要么水喝太多了。

想起去年今日。回学校路上耳机里放起的《摄影艺术》很应景。

遂作画一幅。

.

对自己的一种感觉越来越明显,那就是,自己越来越不争强好胜越来越不锐利了。

排名或者先进这种东西,很久之前就没有很努力去争取过了。当然也因为能力有限。

不过对于某些感兴趣并擅长领域的争强,在小学来讲我是非常在意的。这种争强和争强成功的成就感让我对这个领域更加有兴趣更加有动力。

可是这么几年过去了,单纯对于成就的追求并没有那么高了。对兴趣上的追求,yy的时候经常我会激动到情不自已热泪盈眶,但还是有各种各样的限制导致不能施展。

高考完后

现在是2021年6月9日上午1:49。

其实高考没有想象中那么紧张。考完也没有想象中那样喜悦。即使第一天考完之后我就在想象写完英语作文的我在考场上对着监考老师傻笑了。

考完和几位同学去唱了歌。没有和寝室的几位一起。

今天听说了********的悲惨遭遇,*******************************。

******。

在学校晚宴,语文老师拿来去年12月31日的我们写给今天(8日)的自己的信。自己翻阅了一下,实在佩服当时的自己想得实在太周全了。所有的内容都符合现状。

今晚简单用文字记录一下吧。有时间再补充图片。

是个难忘的日子。虽然没有什么心情上的太大起伏。

晚安。

/文章于2021年6月20日上午10:46修改