Introduction of Tutorials for THU iOS Club

THU iOS Club 的文档 / 教程 / 资源

点击左上方 菜单图标 调出文章列表,点击右上方 GitHub图标 前往仓库

如果有想分享的技术文章 / 资源,欢迎随时提 Pull Request

面向开发的Markdown学习

Started on 210409

First version on 210504

Author: 杨希杰 Yang Xijie

Bilibili视频教程:https://www.bilibili.com/video/BV1jV411j7mz

说明:所讲的内容包括但不限于:使用markdown的原因、markdown基础语法、对markdown中路径和互联网资源的理解、markdown编写工具、导出、发布……

面向对象:需要进行合作编程开发的同学(主要);没有使用过markdown风格写过文字的同学。教程以使用macOS的同学为主。

Lecture特色:不仅仅讲markdown本身,会从markdown延伸出去,有一些扩展的内容

表达与说明的方式

我们要给别人说明一样东西、一样事情的时候,往往需要至少一种的表达方式:比如用语言、用文字、用图片、视频、音乐等媒体;或者将它们结合起来,比如PPT

但其实一般来说,更方便的保留和传播的一种方式是文字。但如果只有文字,总是会让人觉得有些索然无味。这时,带格式的文字作为一种增强的表达方式出现了。大家最常用的可能就是Microsoft Word。你在Word中敲好文字,然后添加一些格式,比如加粗、斜体、居中、设置段落格式、艺术字和颜色调整;这样文字也变得有表现力了起来。

但是,对于从事开发的开发者们来说,Word并不够高效。虽然它提供的格式能够增强文本的表现力,但这些格式很难添加和管理——至少,你得有Word这款付费的软件才能看到、添加、修改这些格式。另外,Word提供的格式太多了,对于开发者来说,我们需要一些格式来增强文字的表现力,但是可能并不需要太多的格式(比如我们希望某种格式能加粗我们软件说明书中的重要部分,能够有层级结构让我们的说明结构清晰便于浏览;但是我们不需要什么论文管理的功能,也不需要什么艺术字,颜色什么的也不太需要……)

再具体的来说,Word的文件是二进制文件,它的后缀一般是.doc或者.docx:这个文件在机器中的本质是一长串的01,里面不仅存储了你写的文字,也存放了这些文字的样式;而这一些01只有用专门的软件Word才能打开(这其中可能需要解压、按照特定顺序读取等步骤)。

与这种二进制文件相对的呢,是纯文本文件,也就是我们常见的后缀为.txt的文件。这一类文件呢,里面从开始到结束,存储的就是一个个字符:比如我在hello.txt里面写入

Hello, world!
你好,世界!

那么,这个txt文件其中的内容就是H后面跟上e再跟上…再跟上一个英文!再跟上一个换行符\n…再跟上一个中文最后跟上一个文件结束符EOF(End of File)。而这些字符都是可以由计算机系统直接读取并且显示出来的,(当然了,本质上这些符号在计算机内还是以0~1的形式存储的),因此读取要比Word方便(不需要特定的软件)、读取速度要比Word快得多;由于其中不带格式,大小也要比Word文档小得多。

Markdown引言

刚刚我们说了文字是很常见的一种表达方式,用txt文件就可以在计算机中方便的呈现文字;我们也说了格式是可以增强文本的表现力的,比如用Word给这些文字添加格式。但问题是单单txt的表现能力捉急,Word中添加格式的操作成本、存储成本都太高了(后面讲Git还会说,由于.docx文件是二进制文件,其无法用Git进行逐行管理),能不能找到二者的平衡呢?

有,这个东西就是Markdown

Markdown其实是一种写作风格/方式,我们用这种风格/方式写出来的纯文本文件(和.txt是一样的)叫做markdown文件,一般以.md作为文件后缀(而不是.txt)。.md文件虽然是纯文本,但其中用一些特殊的字符(如* # [] () …)给纯文本带来格式(这与html文件是相似的,也可以将markdown看作是简单的html)。这些格式通过某种方式进行渲染(注:渲染:指将计算机中的某些数据呈现在屏幕上)你就可以看到它们代表的格式了!

Markdown举例

说了这么多,我们还没看到过markdown文件呢,来看几个例子吧:

〇 (刚刚说到)开发者在写软件说明书的时候会用到markdown

几乎每个GitHub仓库都有一个markdown文件README.md用来对项目做一个简单的说明。(肯定有同学不知道GitHub是什么,我们暂时把它理解为一些开发者存放他们写好的代码的地方)

GitHub|SwiftFormat

  • Xcode的小插件:用来格式化Swift代码
  • 其中的README.md就是用markdown文件,通过GitHub的网页将其渲染呈现在你面前。

〇 (为了提高写作效率(有时添加格式蛮麻烦的))写作者有的时候也会用这种风格:

GitHub|git flight rules

GitHub|How To Ask Questions The Smart Way

我的这篇讲稿也是用markdown写的(事实上我现在手上的几乎所有文字都是用markdown来写的)

国内的博客网站:CSDN简书都支持markdown格式;用markdown来做学习笔记是很方便的。

〇 代码注释:

比如Xcode支持的markup注释风格(类似,换了个名字hh) CSDN|Swift Markup Formatting Syntax

Markdown语法

下面我们来介绍markdown文件到底怎么写:

说“语法”什么的有点专业了。刚刚我们说到——markdown用一些特殊的字符(如* # [] () …)给纯文本带来格式,其实语法指的就是这些规则。那么下面我们就来学习如何用这些特殊符号来在纯文本中表达格式。

具体语法

GitHub Guides | Mastering Markdown

markdown有不少扩展的语法,我们主要学习它最基础的语法,和一些GitHub支持的语法,因为我们之后写markdown文件可能主要是用来写项目的README.md,而这些项目的代码仓库,我们很可能就在GitHub存放。

Headers 标题

16个井号加一个空格再加上标题的名称

# 一级标题
## 二级标题
……
###### 六级标题

有的同学可能会问,那七级标题呢?嗯,是没有的。事实上,五级标题和六级标题都很少会用到,这些绝对够用了

Emphasis 加粗和斜体

*This text will be italic*
_This will also be italic_

**This text will be bold**
__This will also be bold__

_You **can** combine them_

This text will be italic

This will also be italic

This text will be bold

This will also be bold

You can combine them

一般我们单个使用的时候用*多一些,但是两种格式组合使用的时候就随意了。

Lists 列表

Unordered 无序列表

这里可以缩进的哦

* Item 1
* Item 2
  * Item 2a
  * Item 2b
  • Item 1
  • Item 2
    • Item 2a
    • Item 2b
Ordered 有序列表

这里可以缩进的哦,缩进会重新编号,或者变成小标题(如1.1 2.1.3这样)

1. Item 1
1. Item 2
1. Item 3
   1. Item 3a
   1. Item 3b
  1. Item 1
  2. Item 2
  3. Item 3
    1. Item 3a
    2. Item 3b
Task Lists 任务列表
- [x] this is a complete item
- [ ] this is an incomplete item
  • this is a complete item
  • this is an incomplete item

Links 链接

[Text](url):中括号,后面小括号。小括号里面放链接,中括号里面放说明(必须);不需要说明的话,可以直接写链接

直接写链接会直接被转换为可以点击的链接;也可以在两侧加上<>

[GitHub Official Site](https://github.com)

https://github.com
<https://github.com>

GitHub Official Site

https://github.com

https://github.com

Images 图片

![Text](url):感叹号,中括号,小括号。小括号里面放图片链接,中括号里面放图片说明(不必须;写了的话会在图片下方或某处显示说明,当因为某种原因图片无法加载的时候,显示时也会用说明替代图片)

![GitHub Logo](https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png)

GitHub Logo

关于链接我们之后会详细来说。

Inline Code 行内代码

`Swift`中,`var`定义变量,`let`定义常量

Swift中,var定义变量,let定义常量

Code Block 代码块

用一对三个反撇```包裹,在最开始三个反撇后可以加代码的语言名称或简写以在渲染时获得高亮支持。

```swift

var natural_numbers: Array = [0,1,2,3]

print(natural_numbers.capacity)

```

var natural_numbers: Array<Int> = [0,1,2,3]

print(natural_numbers.capacity)

一般支持的代码高亮会有很多,比如:shellbashccppjavaobjectivecpythonswiftyamlhtmlxml

Blockquotes 引用

As Tim Cook said:

> Fearlessness means taking the frst step, 
> even if you don't know where it will take you.

As Tim Cook said:

Fearlessness means taking the frst step, even if you don't know where it will take you.

——Tim Cook's Commencement Address at Duke University, 2018

我一般会用它来在文章开头写一下文档的更新日期和作者。

Tables 表格

You can create tables by assembling a list of words and dividing them with hyphens - (for the first row), and then separating each column with a pipe |:

First Header | Second Header
------------ | -------------
Content from cell 1 | Content from cell 2
Content in the first column | Content in the second column
First HeaderSecond Header
Content from cell 1Content from cell 2
Content in the first columnContent in the second column

其他GitHub支持的语法

更多Github支持的markdown语法可以查看GitHub Guides | Mastering Markdown

对文件路径的理解

我们在插入链接的时候,会使用:

[Google](www.google.com) is your best friend!

Google is your best friend!

方括号里面放你要呈现的名字,圆括号里面放网页的链接。

更普通的,这个链接还可以换成文件:

点击查看[帮助文档](./doc/help.md)

这个路径是相对路径,表示和当前的md文件在同一个路径下的doc文件夹里面的help.md文件,一般我们都会将md文件放在一个总的文件夹中,这样它们之间就可以使用相对路径相互引用了。

如果是在自己电脑上写md,也可以写绝对路径。但是这样一旦那个文件移动位置,使用绝对路径就找不见那个文件了。使用相对路径的话,由于一般md文件会随着总文件夹跟着其他的文件一起走,所以一般不会出现找不到的问题。

更普通的,还可以换成当前文档中的某个大标题:

点击跳转到这篇markdown的[Intro部分](#intro)

对url资源的理解

在我们插入图片的时候,我们使用了这样的语法:

![图片名称](图片链接)

本质上来说,你获取一张网络上的图片,是在服务器(24h不关机的电脑)的文件系统里面获取了这张图片。但为了能保证一张图有一个唯一的标志,一般一张图都会有一个自己的链接。

打开带图片的网站,右键图片,就可以看到复制图片链接的按钮从而获取图片的链接。

这里的图片链接也可以是刚刚的相对路径,比如:

![Markdown图标](./media/markdown_icon.png)

但这样,你就需要在和你现在写的markdown的同一目录下,放一个media文件夹,里面有这张markdown_icon.png的图片。

有的时候,图片和代码相比起来,是个庞然大物(1KB vs 1MB),这时我们不希望将图片加入到项目中(指不希望项目的体积很大,别人从GitHub上下载项目时花的时间过久),可是在项目说明中还需要加入图片进行说明,这时怎么办呢?最简单的,将图片压缩一下(1MB->100KB)扔进项目文件夹,这是最省心的…但要是图比较多,一般来说,我们会选择某些厂商的对象存储服务,将这些图片资源放到比如阿里云的服务器上,然后这个服务把图片的网络链接给你,你就可以在markdown中使用这个链接了。如果没有那么正式,那么这种服务叫做图床。比如我一直在用的聚合图床,你把图片丢进去,它帮你将图片转为一个链接。但是使用这种服务也有需要注意的地方,如果运营图床的工作室哪天跑路了,你放进去的图片可能就没了。

添加目录

不少平台都支持使用[toc]来添加目录。你只需要输入[toc],平台就会帮你自动渲染生成目录。

但也有一些平台不支持这样,这时你需要使用VS Code中的Markdown All in One插件,使用这个插件提供的自动生成目录功能来添加目录。

其他一些注意事项

有的时候你在markdown明明换行了,可是上传到GitHub上面一看,文字都粘在一起了。这是为什么呢?

英文在分段的时候,不像我们写文章段前空两格,他们会直接在两段之间多空一行。一般的回车只是用来方便阅读,不然一行的内容太长了。

如果你不希望这种方式生效,可以在一行后面加两个空格;但这样很麻烦,还是多加回车吧;最好在每种语法前后都空行,这样也能有效减少语法不被识别的情况

(注:多于2个的连续回车markdown会将它们当作一个回车哦)

Markdown工具介绍

macOS

Typora

Typora官网

所见即所得的markdown编辑软件,全平台;各种意义上都应该是markdown编辑的首选!

主要用来写单独的markdown(比如我今天要写讲稿了,用markdown写的话我就会考虑Typora);也可以开一个文件夹专门放自己的笔记……

VS Code

安装插件Markdown All in One,会支持不少快捷操作。

VS Code作为程序员中口碑最好的编辑器,写写markdown什么的肯定没问题!拖入一个文件夹新建markdown文件就可以编辑了!

一般来说,软件开发的项目中,代码都是和markdown文件在一起的,以后如果有一些需要在VS Code打开的项目,使用VS Code编写README.md或者其他的markdown文件也是很方便的一个选择。

Xcode

Xcode中也可以和添加代码文件一样添加markdown文件

其他

如果你在听了这次lecture之后,打算和我一样以后自己的文稿都用markdown来写,那我觉得你可以考虑一些功能更强大的软件。这些软件基本都是付费的,在App Store搜索markdown就有很多;有些是买断制的,有些是订阅制的;都各自有一些独特的功能,比如发布服务、自动备份、图片上传等等,感兴趣的同学也可以看看知乎等平台的安利文章,自己下载试用版试用。

网络平台

CSDN简书支持Markdown写作。

CSDN对各种语法支持的比较好;简书只支持比较基础的Markdown语法。

CSDN的主要文章都是技术文章;简书可能偏杂文多一些,但也有不少质量很高的技术文章。大家以后自己的学习经历、开发笔记也都可以上传到这些平台让更多人看到。

关于不同风格和扩展的说明

如果我们只是写简单的Markdown文稿,上面的这些语法已经完全够用了。

不过既然能用符号表示格式,那么能不能添加更多的语法进来呢?当然可以,比如在markdown里面使用$y=x^2$表示这是一个LaTeX公式(怎么敲LaTeX可以查看我之前整理的LaTeX整理 | 简易符号 Markdown公式编辑),如果渲染markdown的软件支持,那么它就会被显示为$y=x^2$;比如说很多编辑器都支持[toc]表示插入全文目录(但可惜的是GitHub不支持,一般会用VS Code的插件来创建目录);前面我们讲了表格,语法更多的markdown还可以以某种方式设定表格每列的对齐方式;由于markdown最终会被渲染为网页(html文件),因此你在其中加入html标签也是可以的。这些其他的格式大家都可以去CSDN搜索markdown教程找到,但是在使用的时候也要注意看自己的markdown编辑器是不是支持这些语法哦。

这个CSDN|Markdown语法学习是我最开始学习Markdown整理的语法,里面就有介绍了很多其他的格式;但事实上我写了一年的markdown,有些根本就没用过……所以今天我也只讲了最基础的一些格式。

还有一点,反斜线还有行内代码可以用来输入可能会被当作markdown语法的符号。比如你想在markdown中输入一个星号*,可能你一不小心就发现星号被markdown渲染成了格式。这时你可以用行内代码包裹,`*`,或者用反斜线\*,这样markdown就不会把这些符号当成格式了。

Markdown的导出

有的时候(大多数时候)我们都需要将自己写的markdown发个别人看,这时当然我们希望别人看到的是带格式的,而不是纯文本文件。

如果我们在GitHub上上传了README.md,那么GitHub会帮我们自动把.md文件渲染为带格式的网页方便查看。在CSDN简书等平台发布也是一样的。

不过如果是自己导出的话:

  • 可以导出xxx.md,但这样对方的电脑上需要有markdown编辑软件打开才能看到格式,不过这样的话对方不仅可以查看,也能编辑;
  • 如果只是让别人查看的话,一般我们会考虑导出PDF文件,也就是打印,然后选择保存PDF文件;
  • 也可以直接导出为图片(一些软件支持);
  • 或者我们还可以导出为网页(html),这样对方需要用浏览器才能打开也有些麻烦…不过如果你是导出为网页再部署,那么对方通过链接就可以访问,这就变得很方便了

Markdown的发布

下面的内容有些进阶也没那么重要。我讲这一块呢,是想告诉大家大家现在看到的这份讲稿和这个页面是怎么做出来的。一方面是想让大家了解markdown文件发布的过程,另一方面是希望大家学了Git、了解了GitHub之后也能一起修改现在网页上的这些讲稿。

感兴趣的同学可以认真听一下;因为我不会花很多时间在这上面,也希望其他同学听一听:

有的同学可能在学了markdown后就一直用它来写文章,写的多了就想把它发布出来,比如建一个个人网站,把这些文章放上去。然而markdown是纯文本的,所以要将带格式的网页发布出来,我们需要借助一些工具;这样的工具有很多,下面介绍一个:mdbook

我们可以在Mac上安装Homebrew,然后在终端用命令行输入:

brew install mdbook

之后就可以使用它了!

mdbook可以方便的把一系列md文件组织成一个book,用来发布很多markdown文件的时候很容易。mdbook使用SUMMARY.md这个文件确定展示的边栏;book.toml这个文件来对它生成的网页做一些设置。

我们只需要在准备好md文件,编辑好SUMMARY.mdbook.toml之后,确定自己电脑装好了mdbook,在和book.toml同级的文件夹下执行:

mdbook build

就可以得到一个book的文件夹,将这个文件夹上传到服务器,别人去访问就可以看到mdbook渲染好的页面了。

如何直接在自己的电脑上查看生成的网页的效果呢?在和book.toml同级的文件夹下执行:

mdbook server

那么如何将这个文件夹上传至GitHub呢?我们可以将刚刚得到的book文件夹上传至GitHub,然后开启GitHub Pages的服务。但更方便的是,直接把这个文件夹传上去,使用GitHub Action将这个文件夹在另一个Git分支生成网页,然后用GitHub Pages发布出去;这样做的好处是:每次修改上传,发布都是自动的,这样被人看到你的网页/讲稿就总是最新的。如果大家也想帮忙一起建设THU iOS ClubTutorial,在学了Git之后就可以提Pull Request喽!

文字编辑效率up技巧

最后呢,再教大家一些快速写文档的方法。这种方法在macOS上的大多数文字/代码编辑软件上都适用(包括Xcode)。

〇 双击选中一个词:这里词指一个中文的词语或者一个英文的单词

〇 三击选中一行

〇 ⌥左右:以词为单位移动光标

〇 ⌘上下左右:移动光标到行首/行未/文头/文末

〇 按住⇧:文字选中;⇧上下左右 / ⇧⌥左右 / ⌘⇧上下左右

〇 按住⌥:矩形区域选中

〇 ⌥⌫、⌘⌫;fn⌫:删除词、删除到行首;向后删除 delete;⌘X也是一种删除的方式

Homework

〇 (markdown语法:粗体/斜体)

markdown写出带格式的文字:Hello, world!其中Hello粗体world斜体

〇 (markdown语法:表格)

markdown语法列一个两行五列的表格:表头为周一到周五,表的内容为那天你的早八课程;如果某天没有早八课程,那么对应的格空下来不填写。

〇 (markdown图片插入)

markdown中插入图片时小括号中可以填写图片在互联网上的(),也可以填写在文件管理器(如访达)中的图片markdown文件的()。

〇 (其他markdown语法)

阅览Github支持的markdown语法GitHub Guides | Mastering Markdown,使用删除线语法在右边的文字中只添加符号来表示“Google是你最好的朋友”):Baidu Google is your best friend.(提示:crossed out

Answer

〇 (markdown语法:粗体/斜体)

markdown写出带格式的文字:Hello, world!其中Hello粗体world斜体

**Hello**/__Hello__ 和 _word_/*world* 的任意组合

〇 (markdown语法:表格)

参考:

Monday | Tuesday | Wednesday | Thursday | Friday
- | - | - | - | -
 | | Technical Drawing | | Signal and System

〇 (markdown图片插入)

链接 相对路径

〇 (其他markdown语法)

~~Baidu~~ Google is your best friend.

Git讲座

Started on 210504

First version on 210518

Author: 杨希杰 Yang Xijie

Bilibili视频教程: https://www.bilibili.com/video/BV1eh411v7gt

为什么要用Git

〇 版本控制

比如,你晚上写一篇论文,写好了存起来。结果第二天早上起来,你再拿出来一看,害,写的什么啊。早上修改了一大堆,改完了存起来。结果下午一想,害,早上我改什么啊!昨晚那个写的不是挺好的吗…但这时,你已经没有昨晚存的那一份文稿了,只能凭着记忆把论文再改回昨晚写完之后的样子……

这时,你想:如果有一个工具,把我每次修改的内容都记录下来,那么我每次想反悔的时候,这个工具能帮我把文稿恢复到原来的样子就好了!

〇 合作开发

比如,你写一篇论文,写好了存起来,同时发给了你的实验室同伴和导师。你的同伴看到你有几个单词写错了,帮你改了又发回来;你的导师觉得你有段内容写的逻辑不太清,帮你简单改写了一下又发了回来。在等待两人回复的时间,你又发现论文的参考文献少添加了几个,花了点时间把它们加上。过了一会,你收到了同伴和导师改的两份文件,然后你打开你改好的论文,把你同伴改正的单词挑出来放进去,把你的导师改的那一段复制粘贴过去…

这时,你想:要是有个工具,能帮我把大家所做的更改方便的合并在一起就好了!

Git就是这样的一个工具,只不过,我们一般不同它来管理论文,我们会用Git来管理项目。什么是项目,软件开发的一堆代码文件合在一起算一个项目,一本大家一起来写教程书算是一个项目……我们用Git来对这些项目进行管理、结合代码托管平台(比如GitHub),大家一起合作开发,修改代码或文章。

Git准备

要学习Git,因为Git本身就是命令行程序,所以首先是要对终端Shell命令行有一些认识和理解。但因为大家之后可能在开发中不会很用到终端这一块,所以这里只会简单讲一些浅显的部分。对于之后要进行网络应用开发的同学,之后应该还会有关于服务器、Linux命令行等的讲解。

认识Mac的文件系统

文件是什么呢?就是存储在硬盘里面的一些东西。我们怎么在Finder里面看到更清楚的看到这些文件呢?我们需要对Finder进行一些设置

对访达进行设置

Advanced打开Show all filename extensions

Sidebar打开Hard disks从而看到挂载的磁盘,打开你的家目录(一个房子的图标,我的是yangxijie)从而看到家目录

继续调整访达

在菜单栏的View里面调整:

⌘2:按照列表查看

⌥⌘S:打开边栏

⌥⌘T:打开工具栏

⌥⌘P:打开显示路径

⌘/:打开状态栏

观察根目录/初识目录

什么是根目录?所有的文件和文件夹都存在根目录下。这里根目录就是你的内置硬盘Macintosh HD

Applications:你安装的应用程序都在这个文件夹

Library:用来存应用程序的一些数据。比如微信的聊天记录就在这个里面哦

System:一些系统(macOS)的文件,没事儿别乱碰就好了

User:个人的文件就在这里。其中/User/yangxijie(或者你的用户名)是你的家目录,也可以直接在边栏点击打开。

观察树状结构

通过展开、折叠列表了解文件夹间的层级关系

文件夹路径

观察下方的路径栏,尝试写出当前的路径;知道每个文件夹都有一个路径。

其中:根目录/ == Macintosh HD,家目录~ == /Users/yangxijie(你的用户名)

(之后在终端输入带有空格的路径时记得加上双引号…)

查看文件属性中的路径

⌘I查看文件属性,尝试写出文件的路径

尝试在某个文件夹中写出文件的相对路径:.当前文件夹,..上一级文件夹,...上上级文件夹

隐藏文件

⇧⌘. :显示隐藏文件

.开头的文件夹和文件会被隐藏,一些不以.开头的文件或文件夹也会被隐藏。这是因为有些文件可能比较繁琐或者对用户来说完全没有用处,或者普通用户对其进行操作比较危险,所以创建这个文件/文件夹的时候就带上了.、或者直接由系统将其隐藏将其隐藏不让一般用户看到。

文件名后缀的说明

文件名后缀不过是文件名的一部分,文件名后缀只是为了告诉系统该程序默认由哪个应用程序来打开(txt 文本编辑,jpg 预览,mp4 QuickTime Player),一般后缀能标志一个文件的类型

常见纯文本文件的后缀为:无后缀(如一些配置文件~/.zshrc),文本文件<filename>.txt(一般也会省略.txt变为无后缀),markdown风格文件<filename>.md……

认识终端、Shell、命令行

软件按照使用方式来分可以分成有UIUser Interface,用户界面)的图形界面程序,还有没有UI的命令行程序。从使用的角度来说,,图形界面应用方便实用,那么为什么一些程序使用命令行呢?因为命令行简洁,开发起来容易,功能多样,和系统的联系较近。

我们今天要学习的Git,就是没有UI的命令行程序。命令行程序需要借用终端Shell来使用,我们下面会先讲一下终端和Shell。

终端和Shell

终端,就是Mac上的Terminal.app,或者一些别的终端软件,比如我在用的iTerm 2.app。打开终端之后,我们进入的就是ShellShell的作用是将你输入的命令解释并执行。(演示在iTerm 2.app终端中⌘D开两个Shell)

date
Wed May  5 15:44:23 CST 2021

使用:

which date
/bin/date

查看Shell是如何解析这条命令的。也就是说,打开终端,在Shell里面输入命令date,Shell会将其解析为/bin/date,也就是根目录下bin/文件夹下的date程序,并且调用这个程序;这个date程序的功能就是输出当前的时间。

Shell的工作路径

查看命令前面,会有代表当前路径的地方。这说明,每条命令其实都是在某个文件夹下执行的。比如我们执行mkdir命令创建一个新的文件夹,mkdir会帮助我们在当前目录下创建一个文件夹,名字为new_folder

mkdir new_folder

使用pwd命令可以查看当前的路径。使用cd命令可以更改当前的路径。

常用命令

命令后面会加一些参数,比如在mkdir后面加要创建的文件夹的名字;命令后面也会加一些选项,比如--help -a这些。

which <command> # 查看Shell如何解析这条命令
echo <String> # 原封不动的输出一个字符串
echo $SHELL # 输出一个变量的值,查看当前使用的Shell

pwd # 显示当前的工作路径 pirnt working directory
cd <dir> # 更改路径 change directory

ls # 列出当前路径下的文件和文件夹
ls -a # 列出当前路径下的所有文件和文件夹
open <file> # 使用默认程序打开文件
mkdir <folder_name> # 在当前目录下新建文件夹
touch <filename> # 在当前目录下创建纯文本文件
cat <filename> # 显示某纯文本文件的内容

mv/cp <origin_file> <dest_file> # 重命名/移动/复制文件
rm <file> # 删除文件(谨慎使用)
rm -rf <dir> # 删除文件夹(谨慎使用)

clear # 清除终端屏幕上的内容
man <command> # 查看某个程序的帮助 manual
<command> --help # 查看某个程序的帮助
⌃C # 结束当前命令
↑ # 调出上次执行的命令 还有↓

经过观察,我们发现Shell是用空格区分每个单词的。所以我们在之后新建与开发有关的文件夹/文件的时候不允许使用带有空格的名字,尽量在名字中只使用a-z``A-Z``0-9这62个字符(当然中文名也可以,但是不推荐)。但有时有的文件就是有空格,这时需要在名字两侧添加双引号,或者在空格前面添加反斜杠\

OK 如果只是学Git,这些绝对够了。如果是要使用终端进行开发工作,这些说实话不怎么够。但我们学这些足够了。

Git教程

安装/配置Git

macOS自带Git(也许),我们可以直接使用(什么叫自带呢…相当于Finder、备忘录、日历这些预装软件)。使用下方命令查看Git的版本。

git --version
git version 2.24.3 (Apple Git-128)

如果无法出现版本号,说明你需要安装Xcode或者Command Line Tools,安装的时候会顺带安装Git。推荐之后打算学习iOS软件开发的同学直接从App Store安装Xcode;Mac存储较小的同学或者之后不打算用Xcode的话在Apple Developer下载Command Line Tools安装就好。如果希望使用更新版本的Git,需要使用Homebrew进行安装,这里不多说,因为自带的版本版本已经足够了。

添加Git用户名和邮箱

# 查看
git config user.name
git config user.email

# 修改
git config --global user.name <your name>
git config --global user.email <your email>

由于Git会记录你的每一次操作,这些记录里面需要有操作者的信息,你在你的电脑上用,所以需要给你电脑上的Git配置你的名字和email。这里名字就用全名是比较好的,比如Xijie Yang这样,也可以用简写,比如yxj(并不规范)。这里添加名字和邮箱是方便以后别人看了你写的代码能够找到你…需要写真实的名字和邮箱

Git基础

创建版本库

在某个文件夹下执行:

$ git init
Initialized empty Git repository in /Users/yangxijie/Desktop/git_notes/.git/

该文件夹下会多出来一个.git/的文件夹,这个文件夹就是Git生成的,存储用来管理这个文件夹内文件的信息。我们在之后是不应该对这个文件夹内的东西做任何更改的。(复习:想要在Finder看到点开头的文件或文件夹,使用快捷键 ⇧⌘.)

工作区、暂存区、本地版本库

概述

在你执行git init的文件夹(存在.git/文件夹)中:

工作区(Working Directory):你在Finder/Xcode/VS Code的侧边栏文件管理器看到的所有文件。这些文件你可以任意修改,或者说,你修改的总是工作区的文件。

暂存区(Stage):通过git add命令将文件添加到暂存区。可以多次使用git add命令来记录添加或修改的文件内容。

本地仓库(Repository):通过git commit命令将暂存区的所有添加/修改放入本地仓库/本地版本库。一般来说,当一些比较大的修改完成,我们会使用git commit命令完整记录这次修改,每次commit被称作一个版本,因此将存储这些commit的仓库叫做版本库。以后可以通过git log命令快速查看这些commits或进行版本回退。

也就是说,你每次使用git commit,Git就会帮助你记录这次做的更改,这样你以后想后悔的时候通过commit回退就可以了。就好像打闯关游戏的时候会存档,每次存档之后,如果你打Boss挂了,没事儿,从存档点重来就可以了。

git add

git add <file>

可以添加文件,也可以添加文件夹。也可以按照通配符来添加。

git commit

讨论:我到底应该什么时候commit?commit最重要的作用是,之后你可以通过某次commit版本号查看或者回到这次commit。一般来说,当你觉得改了一些东西的时候,都可以commit一下。也有不少人觉得应该每改一个文件add一次commit一次。都可以,这个看个人习惯。但是注意不要改了好多才commit,因为这样更改的记录就不是那么清晰了。

git commit -m "<message>"
# 或
git commit -m"<message>"

一般<message>使用动词加名词的形式,记录你这次commit修改的主要内容。使用英文或中文都可以,个人推荐使用英文,因为如果你之后开源,英文可以让你方便的将你的项目推向世界。

一般来说,一次commit之后,这次commit就不应该被改变了;类似你这次做的修改已经写入历史书的感觉。但是你commit之后突然发现刚刚的commit实在不行,这时怎么办呢?使用:

git commit -m "<message>" --amend

来修补上一次commit。这样之前的一次commit就会变成你新提交的commit。

git status

查看每个区的状态。

git diff

git diff # worktree & stage (Changes in the working tree not yet staged for the next commit.)
git diff HEAD # worktree & HEAD (Changes in the working tree since your last commit.)
git diff --cached # stage & HEAD (Changes between the index and your last commit.)
git diff <commit> <commit> # between two commits

更好的是,直接在VS CodeXcode中查看(左侧的颜色条),这样更方便。或者采用一些插件查看,比如VS Code的插件git graph

这里可以看到Git判断文件修改是按照行来判断的。你改了一个单词,Git只会记录:这一行被更改,原来为xx,改为了xx;你添加了一段代码,Git只会记录:添加了xx行,内容为xx。

所以:

  • Git一般只能管理文本文件,而且是按照行管理的
  • 二进制文件也可以通过git add让Git来管理,但二进制文件被更改的话,Git只知道文件更改了,无法记录是怎么更改的;而且如果一直使用Git管理二进制文件,.git/文件夹会越来越大,这不利于网络传输。
  • Microsoft Word.docx文件就是二进制文件,一般不会用Git来管理。(除了Git还有很多别的版本管理工具/软件哦,大家感兴趣可以自行了解)

git log

(多次commit之后查看)

git log

log——日志

看到了最开始设置的邮箱和名字,也就是说,谁提交的commit,在log里面是都能看得到的哦。

看到了每次commit都有一串编号,这个编号是根据每次一次commit的文件计算出来的,我们将其称为版本号。之后如果要查看某一次commit,可以通过这些版本号查看(但比较长,写前几位就可以了)

也看到了每次commit附加的message,这样就能知道每次commit都做了什么。

git rm

直接删除文件(⌘⌫或rm命令)——只删除了工作区的文件。这时你使用git commit命令提交这一更改,发现你刚刚的更改(删除)Changes not staged for commit。还记得讲git commit的时候,说要把东西添加到暂存区才能提交吗?

所以我们使用git add <file>命令,将删除的文件添加到暂存区

相当于:如果你需要在工作区(在文件管理器)删除一个文件,而且还要让git不再跟踪记录这个文件,那么需要先在文件管理器中删除,之后再使用git add命令添加这个文件。

这两步合成一步,就是git rm <file>。还是推荐大家使用上面的命令,因为我们在之后实际开发的时候,可能一次commit会对很多文件作出修改,这其中包括新建一些文件、删除一些文件,因此直接git add .就好了,不需要考虑这么多;如果文件是删除了的,那么git add .就会将这个文件识别为被删除的文件。

如果你要将用git add放到暂存区的文件取消暂存,使用git rm --cached <file>命令

如果你想改变工作区的文件,或者让暂存区的文件变成

案例:关于上面所有命令的案例

git init/add/status/diff/commit/status/add/commit/log/diff

舍弃工作区/暂存区的修改

舍弃更改==用某些之前的东西来覆盖

git --help restore # 查看restore命令的帮助文档

git restore == git restore --worktree 默认是覆盖worktree

-s <tree>, --source=<tree>
Restore the working tree files with the content from the given tree. If not specified, the default restore source for the working tree is the index, and the default restore source for the index is HEAD.

-W, --worktree, -S, --staged
Specify the restore location. If neither option is specified, by default the working tree is restored.

git restore

案例:关于git restore

版本回退

首先我们需要明确两点:每一次commit的编号,一方面代表所有文件的状态,另一方面代表这一次commit相比于上一次commit所做的更改。

那么我们如果希望回到之前的某个commit,指的就是直接将工作区所有文件都恢复成那个commit之后的样子。

这时我们需要使用:

git reset --hard <commit>

如果我们希望撤销某次commit所做出的更改,那么我们需要使用:

git revert <commit> # Git帮你提交
git revert -n <commit> # -n: --no-commit,自己提交

reset --hard vs. revert

两种回退的分析:

git reset --hard是非常方便的回退方式。如果你写代码提交了两个commit之后去吃饭,吃完回来一看,觉得刚刚状态不太好写的不行,于是你git reset --hard回到两个版本之前,发现所有的东西都变成了你修改代码前的样子。

git reset --hard会删除commit,所以如果你在某次commit中添加了敏感信息(如账号密码等),那么使用git reset --hard进行恢复是十分明智的选择。

但是要注意,在上面的案例中,如果你已经将两次commit使用git push上传到了Git服务器,那么git reset --hard一定会给团队协作带来混乱(因为你直接删除了两个commit!)。这时怎么办呢?你可以使用两次git revert -ngit commit,这样你会新生成一个commit,这个commit的内容和往前两次commit的内容一样,由于这是新生成的commit,所以push到服务器也完全没有问题。

那么如果把密码git push到服务器了呢?这时Git已经帮不了你了。要不直接在代码托管平台直接删掉项目吧(不推荐),要不就赶紧去改密码吧…

注:其中的版本号<commit>可以通过git log查看。

注:向后回退一个版本可以写为git reset --hard HEAD^HEAD^^表示当前版本向后2个commitHEAD~n表示当前版本向后n个commit

Git进阶 - gitignore

我们因为之后要进行合作开发,或者你想把你的Git仓库分享至网络,我们都需要上传.git/文件夹。这样的话,我们就不希望这个文件夹太大,不然别人下载起来很慢;我们也不希望一些私密的文件上传上去,比如含有某些密码的文件;以及一些乱七八糟的文件,我们也不想让别人看到。因此,在git addgit commit的时候,我们就要注意不要把一些很大的图片、视频、第三方包等大文件,还有一些密码、个人配置文件添加进Git仓库。

我们可以在每次git add的时候都注意不去添加这些文件,但是刚刚说到,最常用的添加文件的方式是git add .,那么如何告诉Git我们不希望添加的那些文件,让Git自动帮我们排除添加这些文件呢?

通过在项目的文件夹中创建一个.gitignore文件,在其中写出你不希望Git追踪的文件和文件夹,之后在使用git add命令时,Git就不会添加这些文件和文件夹进暂存区。

我们在开始某种项目的时候,会先在GitHub的.gitignore模板找一份.gitignore,再在其中添加自己不需要Git追踪的文件。比如要开始Swift项目的开发,我们会先下载Swift的.gitignore模板https://github.com/github/gitignore/blob/master/Swift.gitignore,再在其中添加比如一些自己用来测试的文件夹、存放密码的文件等。

使用*可以代替文件的一部分名称。比如使用*.log让Git不要添加所有的日志文件。

.gitignore添加注释,使用#即可。

通过在最前面添加!可以讲已经忽略的文件再添加回来。比如前面写了*.txt忽略了所有你希望Git忽略的txt文件,但是里面有一份IMPORTANT.txt你希望添加,那么可以在.gitignore后面添加!IMPORTANT.txt。还有一点要注意,如果你之前忽略了一整个文件夹,那么之后是不能再用!添加这个文件夹里面的文件的。可以参考一下下面的文档:

Git Docs | gitignore

An optional prefix "!" which negates the pattern; any matching file excluded by a previous pattern will become included again. It is not possible to re-include a file if a parent directory of that file is excluded. Git doesn’t list excluded directories for performance reasons, so any patterns on contained files have no effect, no matter where they are defined.

在开始写项目代码之前就创建好.gitignore并添加到Git中是一个好的习惯。

Git进阶 - 分支

分支

创建一个分支,你可以在这个分支上开发新功能A,过一段时间,你又想开发新功能B,这时你可以再开一个分支。过了一段时间,你两个新功能都开发好了,就可以将两个分支合在一起,这样你的产品就同时有了两个新的功能。

分支操作

git branch --create(-c) <branch> # 创建分支
git branch # 查看本地分支
git switch <branch> # 切换分支
# 开发好了新功能之后
git switch main # 切换到主分支,因为我们的所有功能都要合并到一个分支上
git merge <branch> # 将开发新功能的分支的内容合并(merge)到main分支上
git branch --delete(-d) <branch> # 删除开发新功能的分支,因为开发好了合并进来原来的开发分支就没有用了

解决冲突

合并的时候可能会产生冲突。这时我们需要自己去解决冲突。比如你和同学一块写UI组件,各自开了一份文件来写,结果你俩把文件名起成了一样的,或者在同一份文件中修改了相同的地方,这时在第一个同学合并之后,第二个同学的合并就会出现冲突。解决冲突的方式很简单,在被Git用箭头标出来的地方进行删改就好了。

分支案例

注:设置默认分支为main

Stack Overflow | How can I create a Git repository with the default branch name other than master?

# 查看Git版本
git --version
# Since git version 2.28.0
git config --global init.defaultBranch main
# 之后使用git init初始化项目的默认分支会变为main
git init

# or for one repo
git init -b main
git branch --move(-m) master main

Git进阶 - 远程仓库

连接远程仓库

远程仓库,就是一个Git服务器。它可以存放你的代码,然后多台电脑一起链接到这个Git服务器,大家就可以开始协作了。如果一个平台有很多的远程仓库,我们一般将其称作代码托管平台。世界知名度最高的代码托管平台是GitHub,大家都在用,我们之后也会用GitHub进行演示,以后大家也许也会使用GitHub进行合作开发。其实还有很多的代码托管平台,比如gitee(码云),GitLab(企业可以自行使用部署,如<git.tsinghua.edu.cn>)

连接这些代码托管平台,你得首先有一个账号,直接注册就好了。如果用的是THU的GitLab,用清华邮箱就可以登陆。

我们之后会用这个账号去将本地的代码推上云端,会将云端的代码下载到本地。但是如果我们每次登陆都要输入密码,这是一件很费力的事情。所以我们需要配置ssh。

简单来说,ssh就是你可以不用输入密码,而是通过一串钥匙码(字符串)来登陆;登陆的时候发过去这段钥匙码,如果服务器一看匹配,那么就让你登陆,这样子。

在GitHub中点击个人头像,Settings,在Account settings中找到SSH keys,点击New SSH key;Title可以随便起,比如把你现在登陆的电脑型号写上去;在下方Key这里填入SSH 公钥

查看SSH 公钥:cat ~/.ssh/id_rsa.pub

如果没有需要先使用命令ssh-keygen -o生成

(具体大家可以参考Git文档

再点击Add SHH key,之后本地和远程仓库之间连接就不需要输入账密了。

将本地仓库上传到代码托管平台

一般来说,一个项目的远程仓库只有一个分支main。你开发新功能的时候会在本地新开一个分支new_feature,开发好了之后将这个分支merge到本地的main分支。如果要上传,则是将本地的main分支上传到远程仓库的main分支。

假设我们现在本地已经有了一个Git仓库,我们现在希望将它上传到GitHub上,让别人也看到我这个仓库里面的文件:

打开GitHub,创建一个新的仓库(Repository),什么都不添加,之后会弹出下面的一段话:

# push an existing repository from the command line:
# 在本地添加一个远程仓库,并且给这个远程仓库起名为`origin`。使用`git remote`命令查看本地仓库关联的远程仓库。
git remote add origin https://github.com/Yang-Xijie/git_lecture.git 
# 将本地仓库的当前分支重命名为main(不本质)
git branch -M main 
# 如果直接执行git push,会出现:fatal: The current branch main has no upstream branch. To push the current branch and set the remote as upstream, use `git push --set-upstream origin main`,这说明我们需要给当前的分支与一个远程的分支相对应。-u是--set-upstream的简写。
git push -u origin main

我们按照提示来做,会发现代码已经上传了上去。

如果我们再在本地修改,提交,(由于我们已经设置了上游的分支)之后就可以使用git push将修改的内容同步到GitHub了!

注:

We recommend every repository include a README, LICENSE, and .gitignore. —— GitHub

其中README.md告诉别人怎么用你写的这个软件,怎么合作开发,使用需要注意什么;LICENSE里面写开源的协议,我们后面会说到;.gitignore我们前面讲过,别把一些乱七八糟的文件或者敏感信息

和他人合作开发一个项目

如果是一个大型软件,几十个代码文件,一个人写肯定不现实,这时就需要大家来合作开发。

有仓库的读写权限

一般来说,如果是一个小团队的项目,每个人都有仓库的读写权的话,我们合作开发的步骤基本和上面自己上传代码到仓库是一样的。

需要注意的是,在我们开发一个功能的时候,他人可能已经写了一些代码commit然后push到了远程仓库。这时我们一般需要将他人的代码merge到自己目前的分支上,保持自己的代码最新,减少之后自己开发完毕push到远端仓库出现冲突的情况,也减少重复开发的情况。

具体做法 首先确保你的分支已经关联了远程仓库的分支,之后使用

git fetch <repo-nickname> # 将远程仓库的内容拉取到本地
git merge # 将本地中远程仓库的内容merge到当前分支

为开源项目添砖加瓦/没有仓库的读写权限

有的项目,会有一些核心的人有着仓库的控制权限;其他人则需要通过这些核心的人的审核,才能对项目作出修改。一般我们将这些有着仓库控制权限的开发者叫做项目维护者(Maintainer),将参与项目开发但是没有直接操控权限的开发者叫做项目贡献者(Contributor)。上面所说的直接push可以作为Maintainer提交代码的一种方式。但如果是Contributor该如何提交代码呢?

Git使用实例<team-repo-nickname>开发 说明:团队仓库名为<team-repo-nickname>,个人仓库名为<my-repo-nickname>

1 将远程仓库下载、链接到本地

〇 1.1 克隆原仓库到本地

git clone <team-repo-url> --origin <team-repo-nickname> <project-folder>

--origin <远程仓库名>用来给默认的名字origin重命名

--origin:-o

<project-folder>指本地新出现的文件夹的名称

〇 1.2 fork仓库使得自己有push的权限 在GitHub上操作,得到自己fork后的仓库链接<my-repo-url>

〇 1.3 进入clone后的文件夹

cd <project-folder>

〇 1.4 添加自己的远程仓库

git remote add <my-repo-nickname> <my-repo-url>

add后紧跟的是远程仓库的名字,可以自定义

〇 1.5 查看关联的远程仓库

git remote --verbose
git remote --verbose show <repo-nickname>

--verbose:-v

2 开始开发自己的功能

注:一般团队的远程仓库只有一个main分支

〇 2.1 先拉取最新内容

git fetch <team-repo-nickname> # 或 git fetch --all

〇 2.2 将最新的内容merge到自己的main分支

git switch main
git merge <team-repo-nickname>/main

这里应该不会遇到冲突,因为本地不会在main分支进行开发,除非你修改文件的时候忘记切换到新的分支

〇 2.3 switch到新的分支

git branch --create newFeature

〇 2.4 开始自己修改代码

〇 2.4.1 拉取最新的代码 如果你在切换分支之后一直把添加新功能这件事情扔在一边儿,那 么你在开始修改代码之前最好再拉取一下最新的代码进行合并:

git fetch <team-repo-nickname>
git merge <team-repo-nickname>/main

〇 2.4.2 修改 比如增加文件、删除文件,增加代码、删除代码等

〇 2.4.3 提交

(newFeature) git add .
(newFeature) git commit -m"add newFeature"

〇 2.4.4 拉取最新代码查看是否有冲突

在上一次commit之后,再次修改代码之前,你需要拉取最新的代码;这时可能会遇到冲突,因为你在本地修改的时候别人可能已经在远端仓库提交了PR并且被管理者merge到新的代码中。遇到冲突需要自己解决

git fetch <team-repo-nickname> # 或 git fetch --all
(newFeature) git merge <team-repo-nickname>/main

在解决冲突后再次commit;如果无冲突则没有必要commit

(newFeature) git add .
(newFeature) git commit -m"solve conflicts"

〇 再次修改

重复上述修改提交拉取最新代码的过程

3 push到远端和提PR

〇 3.1 push到自己的远端仓库

git push <my-repo-nickname> newFeature:newFeature

这里的语法是:git push <远程主机> <本地分支>:<远程分支>

含义:将某一本地分支推到远程主机的远程分支;如果远程仓库没有这一分支,则创建

也可以写作:

(newFeature) git push <my-repo-nickname> newFeature

这里的语法是:git push <远程主机> <远程分支>

含义:将本地的当前分支推到远程主机的远程分支;如果远程仓库没有这一分支,则创建

〇 3.2 提PR(Pull Request) 登陆GitHub,在你的仓库可以拿着刚刚推到远程的newFeature分支向团队仓库提一个PR

〇 3.3 等待开发者merge 如果PR被拒绝,你可以继续更改代码、commit、push、提PR直到开发者觉得你的代码可以merge;也可以直接弃坑lol

4 开发者merge之后删除newFeature分支

〇 4.1 删除远程分支

〇 方法一 在团队仓库的PR界面,如果成功merge,那么你可以看到一个delete branch的按钮,可以直接点击按钮删除自己fork的仓库中的newFeature分支

〇 方法二

git push <my-repo-nickname> --delete newFeature

〇 方法三

git push <my-repo-nickname> :newFeature

这里的语法是:git push <远程主机> :<远程分支>(注意和push到远端仓库做区分)

〇 4.2 删除本地分支

(main) git branch -d newFeature

确认删除可以使用:

(main) git branch -D newFeature

〇 4.3 可能遇到的问题 使用git fetch --all将所有远端仓库(<team-repo-nickname><my-repo-nickname>)的最新“状况”拉取到本地

这时使用命令git branch -a查看所有分支,发现远端<my-repo-nickname>的newFeature分支并没有删除

这说明不能通过git fetch命令获取到分支删除的更新

使用git remote -v show yxj查看分支状况,发现newFeature分支是陈旧的(stale)

使用git remote prune yxj删掉本地陈旧的远端分支(就是 远端已经删除掉了但是本地还没删除掉的分支)

或者拉取信息的时候使用:

git fetch -p --all

详情可以查看:CSDN | Git远程分支的删除与同步

使用private仓库进行合作开发

与参与开源项目是类似的。

如果直接在private项目中添加他人的access权限添加collaborator),相当于添加maintainer,maintainer可以直接push仓库;不过也可以fork之后提PR。注意,没有仓库的access权限应该是不能fork的!

Git进阶 - 标签管理

为什么要添加标签

发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也是版本库的一个快照。

Git有commit,为什么还要引入tag?

“请把上周一的那个版本打包发布,commit号是6a5819e...”

如果换一个办法:

“请把上周一的那个版本打包发布,版本号是v1.2”

“好的,按照tag v1.2查找commit就行!”

所以,tag就是一个让人容易记住的有意义的名字,它跟某个commit绑在一起。

——廖雪峰Git教程-标签管理

在本地的commit上添加标签

 git tag -a "1.0" -m"first versioin"

将标签上传至代码托管平台

git push --tags

Git进阶 - 命令简写

gitconfig

cat ~/.gitconfig

用vim编辑器打开vim ~/.gitconfigopen ~/.gitignore使用默认应用打开

添加

[alias]
	lg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit

之后使用命令git lg就可以代替上面这一长串的命令啦!你也可以设置c=commit,这样你以后用git c

zshrc

如果你还是觉得上面的命令不够简化,你还可以这样,直接让Shell解析你简化的命令:

cat ~/.zshrc(macOS版本需在10.15 Catalina及以上;什么,还没升级?快去升啊 笑)这是zsh的配置文件。

vim ~/.zshrcopen ~/.zshrc

添加

# Git
alias ga="git add "
alias gc="git commit -m "
alias gs="git status"
alias glg="git log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
# Git END

然后我们使用命令source ~/.zshrc让这个配置文件生效。

之后我们就可以用ga .来添加所有文件,用gc "update xxx"来提交,用glg查看简要的提交历史……你也可以添加更多的简写

如何使用GitHub上的代码

说说开源

什么是开源

开源就是把自己写的软件的源码公开,比如上传到GitHub并且设置仓库为public (instead of private)

开源者

从开源者的角度,你的代码被别的优秀开发者看到,他们能帮你提出很多代码上的建议,会帮你一起开发软件,加速软件开发速度; 从其他开发者的角度,你可以看到很多优秀的开源代码仓库,从而有了学习的内容; 整体来说,开源加快了某种社区的发展,加快了软件的开发进程,加强了程序员之间的交流…

当然也会有一些弊端,比如开源者可能很难从开源中获得资金的支持…不过也许能获得好的名声,嗯,只是也许

开源协议

有很多种开源协议。在GitHub上使用最多的是MIT协议。简单来说,开源协议告诉看开源代码的人该如何使用这份开源的代码。一般来说,开源者应该在代码中提供开源协议。你可以用现成的协议,也可以自己写。想了解更多可以查看:从MIT协议谈契约精神(如果之后打算开源自己的代码的话一定要仔细查看哦)

GitHub简介

GitHub是一个代码托管平台和世界最大的程序员交流社区。你可以将自己团队的项目放上去,然后大家一起push一起提pr(Pull Request)合作开发,这时你使用的是GitHub的代码托管功能,如果你不希望开源项目,你应该把repo(Repository)的属性设置为private;你也可以自己一个人开发,然后把代码放上去让更多的人看到(设置repo属性为public),如果有人跟你互动(比如在Issues模块反馈了几个bug,在Discussion模块和你简单交流),那么你就使用了GitHub的代码托管功能和交流社区的功能。

有的时候,我们也会去GitHub搜索一些开源项目来借鉴学习。

如何查看GitHub代码仓库/使用别人写的代码

你可以在GitHub上搜索你感兴趣的项目

点开某个repo查看README.md,一般来说README.md都会将项目描述的很清楚

想借鉴代码:

  • 如果只有几个文件的话可以直接复制粘贴
  • 直接git clone查看所有的代码
  • 下载代码文件(可能不带.git文件夹)

想使用程序:

  • 下载可执行文件/安装包
  • git clone后自己进行编译/build

想为这个开源项目贡献自己的力量:

  • 使用程序,在Issues模块提出自己发现的bug
  • git clone后自己对程序作出一些修改,对项目提出pr

记得查看Licence,在许可证允许的范围内使用源代码。

References

Git Official Site

廖雪峰|Git教程

  • ✡ RECOMMENDED ✡
  • 非常通俗易懂的文字版Git教程,推荐多次翻阅

Bilibili|电子系科协2020年暑培 p1 Git

可视化学习Git

  • 对Git中的高端操作不理解可以去试试可视动画
  • 初学阶段也可以通过闯关来加深对git的理解

Git docs|快速查阅git操作

GitHub|git-flight-rules

  • ✡ RECOMMENDED ✡
  • 如果你在使用git的过程中遇到了棘手的问题应该都可以在这里找到解决方法

一个挺详细的 Git 中文手册

Git Pro|进阶git的一本厚书

  • 一般没有必要看

补充内容

关于我讲的内容

我讲的内容是做了删减的,并没有讲到Git的全部命令。我把核心放到开发常用的一些命令上,希望大家知道Git怎么真正辅助我们的开发。

什么是梯子

什么是服务器——就是一台电脑

网络如何访问(骨干网,路由器)

概括的来说,能让你上网速度加快,能让你访问原本访问不了的网站的工具,就叫梯子/VPN/加速器…

使用THU GitLab

注意无法和校外的同学一起使用

图形化Git

有很多图形化的Git软件,我用过都觉得很难用…所以也没什么发言权,大家觉得命令行不好用也可以自己去探索一下

Xcode中使用Git,可以使用自带的一些功能(在菜单栏Source Control里面),但是我个人推荐使用终端命令行进行版本控制。注意打开终端之后需要先cd到项目的文件夹再使用Git

终端进阶

echo hello world >> haha

history | grep git

终端脚本的编写:需要掌握,在Xcode里面可能就要写的。但不是刚需。也很简单,用到的时候再简单学一下就好了

GitHub CI

查看THU-iOS tutorial repo.github/workflows中的yaml文件,可以看到我们将上传的讲稿自动部署到GitHub pages

Homework

〇(Git基础)

Git是目前世界上最先进的分布式()控制系统

〇(路径的理解)

如果使用pwd命令得到的路径为~/Developer/git_learning/,请写出文件~/Developer/Swift_learning/help.md的相对路径:

〇(Finder操作)

在Finder中显示/隐藏以点开头文件的快捷键为:

提示:快捷键符号可以在macOS按⌃⌘space调出表情/符号面板(菜单栏的Emoji & Symbols),在技术符号(Technical Symbols)中找到这些快捷键符号。也可以用英文简写表示。

〇 (终端常用命令与注意事项)

使用终端在当前路径下创建一个名为learning git.md的文本文件的命令为:

〇(查看帮助文档的方式、命令的参数)

在命令行查看Git帮助的命令为:

在命令行查看Git的restore操作的帮助文档的命令为:

使用终端查看一个文本文件的命令为cat <file>,请问这个cat是哪个英文单词的缩写呢?

如何退出帮助页面?

提示:查看cat的帮助文档

〇 (Git版本回退)

选择题:命令git reset --hard <commit>的作用是(),命令git revert <commit>的作用是()

A. 将工作区的所有文件恢复到某个commit之后的样子

B. 撤销/回退某个commit所做的更改

〇 (Git使用注意)

一般来说,使用git init初始化之后,需要给文件夹添加怎样的一个文件,这样之后才能才能直接使用git add .呢?

〇 (Git进阶操作)

请问git rebase命令的作用是什么呢?

提示:使用搜索引擎了解,一句话简述即可

〇 (开源协议)

MIT协议中有一句话THE SOFTWARE IS PROVIDED "XX XX",浏览文章从MIT协议谈契约精神,回答"XX XX"代表的英文单词是?它的中文意思是?

Answer

〇(Git基础)

版本

〇(路径的理解)

../Swift_learning/help.md

〇(Finder操作)

⇧⌘.

〇 (终端常用命令与注意事项)使用终端在当前路径下创建一个名为“learning git.md”的文本文件的命令为:

touch "learning git.md" 或 touch learning\ git.md 或 touch 'learning git.md'

〇(查看帮助文档的方式、命令的参数)

man git 或 git --help

git --help restore 或 git restore --help

concatenate

在英文输入方式下按q

〇 (Git版本回退)

A B

〇 (Git使用注意)

.gitignore

〇 (Git进阶操作)

合并多次commit为一次commit

〇 (开源协议)

AS IS 中文意思:按照原样的

Git实例

Author: 杨希杰 Yang Xijie

Collaborator:

git init/add/status/diff/commit/status/add/commit/log/diff

初始化仓库

$ cd ~/Desktop
$ mkdir notes
$ cd notes
$ git init
Initialized empty Git repository in /Users/yangxijie/Desktop/notes/.git/

添加一个文件

$ touch markdown_learn.md
$ vim markdown_learn.md
$ cat markdown_learn.md
# Markdown Note
# Grammar
`#`: title

[](): add link

![](): add image

查看状态

$ git status
On branch main

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	markdown_learn.md

nothing added to commit but untracked files present (use "git add" to track)

添加文件到暂存区

$ git add markdown_learn.md
$ git status
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   markdown_learn.md

再添加一篇文件

$ touch git_learn.md
$ vim git_learn.md
$ cat git_learn.md
# Git Note
# About Git
Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.

# Git Commands
`git add <file>`
`git commit -m "<msg>"`

查看状态

$ git status
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   markdown_learn.md

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	git_learn.md

修改文件

$ vim markdown_learn.md
# Markdown Note
# Grammar
`#`: title

[](): add link

![](): add image

**bold_text**

*italic_text*

再次查看状态

$ git status
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   markdown_learn.md

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   markdown_learn.md

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	git_learn.md

添加gitignore

我们希望一次添加这两个文件,在这之前先创建.gitignore

$ touch .gitignore
$ echo ".DS_Store" >> .gitignore

$ git add .
$ git status
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   .gitignore
	new file:   git_learn.md
	new file:   markdown_learn.md

提交

$ git commit -m "add two notes"
[main (root-commit) bdf79c8] add two notes
 3 files changed, 19 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 git_learn.md
 create mode 100644 markdown_learn.md

查看状态

$ git status
On branch main
nothing to commit, working tree clean

添加和提交

$ vim git_learn.md
$ cat git_learn.md
# Git Note
# About Git
Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.

# Git Commands
`git add <file>`
`git commit -m "<msg>"`
`git status`

$ git add .
$ git commit -m "add new git commands"
[main c874462] add new git commands
 1 file changed, 1 insertion(+)

查看log

$ git log
commit c874462dba874eb098a4445f8ff6a15b2e381fc7 (HEAD -> main)
Author: yxj <564197835@qq.com>
Date:   Thu May 20 20:42:43 2021 +0800

    add new git commands

commit bdf79c8b45ebdd1e7644d0a19b47b8c4cba84f9a
Author: yxj <564197835@qq.com>
Date:   Thu May 20 20:40:03 2021 +0800

    add two notes

比较差异

比较两次commit的差异(其中前面有加号的是第二次提交添加的,剩下的信息并不那么重要):

$ git diff bdf79c c87446
diff --git a/git_learn.md b/git_learn.md
index 575b88e..1b60d22 100644
--- a/git_learn.md
+++ b/git_learn.md
@@ -5,3 +5,4 @@ Git is a free and open source distributed version control system designed to han
 # Git Commands
 `git add <file>`
 `git commit -m "<msg>"`
+`git status`

git restore

TODO

git brancn/switch

新建仓库

$ cd ~/Desktop
$ mkdir teamwork
$ cd teamwork
$ git init

添加文件在main分支至少有一次commit

$ touch README.md
$ echo  'This is our teamwork!'  >> README.md
$ cat README.md
This is our teamwork!
$ git add README.md
$ git commit -m "add README"
[main (root-commit) 605051f] add README
 1 file changed, 1 insertion(+)
 create mode 100644 README.md

在sayHello分支上写代码

$ git branch -c sayHello
$ git switch sayHello
Switched to branch 'sayHello'
$ touch sayHello.swift
$ vim sayHello.swift
$ cat sayHello.swift
func sayHello(name: String) {
    print("Hello, \(name).")
}
$ git add sayHello.swift
$ git commit -m "add func sayHello"
[sayHello e30ff7a] add func sayHello
 1 file changed, 3 insertions(+)
 create mode 100644 sayHello.swift

回到main分支进行合并

$ git switch main
Switched to branch 'main'
$ git merge sayHello
Updating 605051f..e30ff7a
Fast-forward
 sayHello.swift | 3 +++
 1 file changed, 3 insertions(+)
 create mode 100644 sayHello.swift
 $ git log
 Untracked files:
commit e30ff7a9797f9577b056c91cdbc46b37bff71168 (HEAD -> main, sayHello)
Author: yxj <564197835@qq.com>
Date:   Fri May 21 18:37:09 2021 +0800

    add func sayHello

commit 605051f10e676b477d5f101dca5828cf34165ab6
Author: yxj <564197835@qq.com>
Date:   Fri May 21 18:30:57 2021 +0800

    add README

在goodMorning分支上写代码

同上述在sayHello分支上写代码的内容,之后也是回到main分支进行合并

向GitHub上的开源项目提交代码

TODO

Swift教学

2021.10.17

王可

简单介绍

  • 苹果公司于2014年在苹果开发者年会(WWDC)发布了Swift编程语言。经过7年的发展,Swift日趋成熟,同时作为苹果目前主推的编程语言(之前是Object-C),官方和社区提供的库也十分丰富。

  • Swift兼具了许多现代编程语言的优点,容易编写且易于阅读和维护,学习起来很容易上手。

基础

常量与变量、强类型、类型推断

varlet

let a = 1
a = 5
var b = 4 //编译器会自动推断为Int类型
b = 4.5 //如果强行赋值Double\Float类型会报错

可选类型

var x = "321"
let y = Int(x)
print(y)
print(y!)

元组(tuples)

let tuple = ("hello", "world",2021)
print(tuple.1)
print(tuple.2)
print(tuple.3)
let (string1, string2, int1) = tuple
print("\(string1) \(string2) \(int1)")

区间运算符

闭区间

for i in 1...5 {
  print(i)
}

半开区间

for i in 1..<5 {
  print(i)
}

集合类型

  • 数组

    • pythonlist类似
    a = [1,2,3,4,5,"str"]
    for i in a {
      print(i)
    }
    
    print(a.contains(6))
    a.append(6)
    print(a.contains(6))
    
    for i in a[1...] {
      print(i)
    }
    
  • 集合

    • 储存同种元素
    • 每个哈希值相同的元素只出现一次
    • 无顺序
    var set: Set<Int> = [1,2,3,4]
    for i in set {
        print(i)
    }
    set.insert(4.5)
    
  • 字典:

    • 使用键来访问数据
    var int2str: [Int: String] = [1: "Joker", 2: "Queen", 3: "King"]
    print(int2str[1])
    
    for (int,str) in int2str {
      print("\(int):\(str)")
    }
    

函数与闭包

函数

func greet(person: String) -> String {
  return "Hello " + person + "!"
}

print(greet(person: "Swift"))

Swift函数又一个很符合自然语言的特性,变量在函数内和函数外可以有两个名字,这比较符合英语介词的使用习惯。

func greet(with person: String) -> String {
  return "Hello " + person + "!"
}

print(greet(with: "Swift"))

使用元组承载多重返回值

func firstLast(of array: [Int]) -> (first: Int, last: Int) {
    return (array[0], array.last!)
}

var a = [1,2,3,4,5]

let (f, l) = firstLast(of: a)
print("\(f),\(l)")

闭包

闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数(Lambdas)比较相似。

var numbers = [2,1,4,3]

var sortedNumber = numbers.sorted(by: { (s1: Int, s2: Int) -> Bool in return s1 < s2})

for i in sortedNumber {
    print(i)
}

属于非初级的内容,有兴趣可以深入研究。

类和结构体

类和结构体有许多相同之处,比如都定义属性、方法和协议等,它们的主要区别在于类有继承和引用计数等。

struct Student {
    public var name: String
    public var sex: String
    private let ID: UUID
    
    init() {
        self.name = "Joker"
        self.sex = "Man"
        self.ID = UUID.init()
    }
    
    init(name: String, sex: String) {
        self.name = name
        self.sex = sex
        self.ID = UUID.init()
    }
    
    func getName() -> String {
      return self.name
    }
    
    func getID() -> UUID {
      return self.ID
    }
}

var joker = Student()
var queen = Student(name: "Queen", sex: "Woman")

print("\(joker.name) + \(joker.sex) + \(joker.getID())")
print("\(queen.name) + \(queen.sex) + \(queen.getID())")
class School {
    var name: String
    var address: String
    init(name: String, address: String) {
        self.name = name
        self.address = address
    }
}

class University: School {
    var QSRanking: Int
    init(name: String, address: String, QSRanking:Int) {
        self.QSRanking = QSRanking
        super.init(name: name, address: address)
    }
}

var THU = University(name: "Tsinghua", address: "Beijing Haidian", QSRanking: 17)

print("\(THU.name) + \(THU.address) + \(THU.QSRanking)")

授人以渔

Swift教学课后作业

2021.10.17 添加作业内容

2021.10.25 更新作业答案

作业:杨希杰 讲稿

Introduction

  • 基础部分为基本要求,进阶部分可自选;题目前有Optional也是选做。
  • 复制下面对应部分的作业,在括号或空白处中填空

基础

查文档

〇 Swift官网的网址是()。我们一般会在官网上查找Swift的文档,即Language Guide,网址为()。

〇 在Xcode中打开开发者文档的方法是:在菜单栏()中点击(),或使用快捷键()。

强类型

观察下面的代码:(提示:观察不出来可以实际运行)

let a = 1
a = 5
var b = 4
b = 4.5

〇 代码第二行报错的原因是()。

〇 代码第四行报错的原因是()。

可选类型

〇 如果变量a是一个可选类型,我们可以通过方式()实现:如果其不为空,则取出真实值赋给b;若其为空,则令b0

A. let b = a!

B. let b = (a != nil) ? a : 0

C. if let b = a { ... } else { let b = 0 ... }

D. if a != nil { b = a! ... } else { b = 0 ... }

函数

〇 一个函数可以通过()返回多个值。

〇 编写程序,计算10的阶乘并输出结果。要求:使用...运算符。【循环 区间生成运算符】 (

〇 回顾函数闭包的相关知识,补全下列代码:

let fruits: [String] = ["apple", "orange", "pear", "pineapple"]
// 希望打印出 ["pineapple", "orange", "apple", "pear"]
// 即Array中的所有字符串按照长度从大到小排序

// TODO

提示:

阅读函数sorted(by:)文档func sorted(by areInIncreasingOrder: (Self.Element, Self.Element) -> Bool) -> [Self.Element]

如果你想直接在大括号中写函数的话,$0$1可以分别指代第1个和第2个参数。

统计各个专业的人数

import Foundation

enum Major {
    case A
    case B
    case C
    case D
}

struct Student {
    var name: String
    var major: Major
}

let studentList: [Student] = [
    Student(name: "Alice", major: .A),
    Student(name: "Bob", major: .B),
    Student(name: "Carl", major: .C),
    Student(name: "David", major: .D),
    Student(name: "Eric", major: .A),
    Student(name: "Fred", major: .D),
    Student(name: "Gavin", major: .C),
    Student(name: "Henry", major: .D),
    Student(name: "Ivy", major: .B),
    Student(name: "Janet", major: .D),
    Student(name: "Kathy", major: .A)
]

// 题目:
// 统计studentList中各个专业的同学名字和总数目,并按照你倾向的方式打印。

// TODO

进阶

Error Handling

执行如下程序,结果为(

)(共两行),简要解释输出结果:()。

import Foundation

struct Printer {
    var paper: Int // 0 - 400
    var ink: Float // 0 - 100 percentage
    let inkPerPage: Float = 0.1

    enum error: Error {
        case outOfPaper
        case noInk
    }
}

struct File {
    var name: String
    var page: Int
}

func printFile(_ file: File, with printer: inout Printer) throws {
    if printer.paper <= file.page {
        throw Printer.error.outOfPaper
    } else if printer.ink <= Float(file.page) * printer.inkPerPage {
        throw Printer.error.noInk
    } else {
        printer.paper -= file.page
        printer.ink -= Float(file.page) * printer.inkPerPage
        print("File \"\(file.name)\" was successfully printed! (paper: \(printer.paper), ink: \(String(format: "%2.1f", printer.ink))%)")
    }
}

var myHomework = File(name: "my homework", page: 4)
var myReport = File(name: "my report", page: 5)
var myPrinter = Printer(paper: 7, ink: 80)
do {
    try printFile(myHomework, with: &myPrinter)
    try printFile(myReport, with: &myPrinter)
} catch {
    print(error)
}

Log()

新建Project - macOS - Command Line Tool,复制运行如下代码:

import Foundation // 1

struct Log: CustomStringConvertible { // 3
    public var time: String // 4
    public var log: String // 5

    init(filePath: String = #file, line: Int = #line, column: Int = #column, funcName: String = #function) { // 7
        let fileName = (filePath as NSString).lastPathComponent // 8

        let formatter = DateFormatter() // 10
        formatter.dateStyle = .none // 11
        formatter.timeStyle = .medium // 12
        time = formatter.string(from: Date()) // 13

        log = "\(fileName)(\(line),\(column)) \(funcName)" // 15
    }

    public var description: String { // 18
        return "[\(time) \(log)]\n\t" // 19
    }

    public var string: String { // 22
        description // 23
    } // 24

    public var error: String { // 26
        "[ERROR] " + description // 27
    }
}

func MyPrint() { // 31
    print(Log().string + "Hello, world!") // 32
    print(Log().error + "Bye, world.") // 33
}

MyPrint() // 36

// Literal        Type     Value
//
// #file          String   The name of the file in which it appears.
// #line          Int      The line number on which it appears.
// #column        Int      The column number in which it begins.
// #function      String   The name of the declaration in which it appears.

〇 该程序的运行结果是(

)。

〇 第3行新建了一个结构体名为Log,后面冒号跟着的CustomStringConvertible是(),在结构体内部的变量()完成了CustomStringConvertible

〇 (Optional)可以去掉第4行的public关键字吗?()

〇 第7行中,filePath: String = #filefilePath是参数名称,冒号后面的String是这个参数的(),=后面跟的是这个参数的()。

〇 (Optional)第8行中,(filePath as NSString)是在做()。

〇 第12行中出现了.medium,它们是()的一种。

〇(Optional)第12行如果要补全.前面的内容可以添加()。提示:查看.medium的文档。

〇 第13行中的formatter.string(from: ),这里string是一个(),看到from这一个介词可以推测from是参数的()。

〇 第19行的\n\t是()字符。

〇 第22行定义了变量string,我们将其称作(计算属性)。将22 23 24这三行代码补充完整:(

)。

〇 第32行的Log()()了一个结构体实例。

〇 第32行的+表示()。

〇 第36行调用了函数MyPrint,该函数有()个参数,()个返回值。

〇 (Optional)尝试如下步骤在Xcode中添加code snippet

To create a code snippet:

  1. Select: print(Log().string + "<\hashinfo\hash>")
  2. Go to Xcode -> Editor -> Create Code Snippet.
  3. Change \hash to #.
  4. Set title as Log() and completion as print.

Try typing print and select the code snippet.

其他

〇 (Optional)如果我们想导入并使用一个开源的第三方Swift Package,需要点击Xcode菜单栏()(写出层级关系),然后添加该Swift Package的地址。之后在需要用到该包的代码文件最开始写上import语句就可以了。

Swift有一个非常好用的格式化工具GitHub | SwiftFormat,阅读其README说明,在自己的Xcode中添加该格式化工具并尝试使用。你在你的Mac上安装了吗?()(安装了/还没/下次一定)。

〇 (Optional)Swift目前的最新版本是5.5,这一个版本引入的一个重要更新是Concurrency WWDC链接WWDC21 | Meet async/await in Swift,感兴趣的同学可以自行查看。

〇 (Optional)如果你还没有学习过Git,可以花时间掌握一下这个合作开发的必备工具。社团往期培训课程传送门 Git基础与进阶

答案

基础

查文档

〇 Swift官网的网址是(https://swift.org)。我们一般会在官网上查找Swift的文档,即Language Guide,网址为(https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html)。

〇 在Xcode中打开开发者文档的方法是:在菜单栏(Window)中点击(Developer Documentation),或使用快捷键(shift cmd 0)。

强类型

观察下面的代码:(提示:观察不出来可以实际运行)

let a = 1
a = 5
var b = 4
b = 4.5

〇 代码第二行报错的原因是(a是一个常量无法更改)。

〇 代码第四行报错的原因是(由类型推断机制,b的类型为Int,无法将小数4.5赋值给b)。

可选类型

〇 如果变量a是一个可选类型,我们可以通过方式(BCD)实现:如果其不为空,则取出真实值赋给b;若其为空,则令b0

A. let b = a!

B. let b = (a != nil) ? a : 0

C. if let b = a { ... } else { let b = 0 ... }

D. if a != nil { b = a! ... } else { b = 0 ... }

函数

〇 一个函数可以通过(元组)返回多个值。

〇 编写程序,计算10的阶乘并输出结果。要求:使用...运算符。提示:循环 区间生成运算符 (

var result: Int = 1
for i in 2 ... 10 {
    result *= i
}

print(result)

〇 回顾函数闭包的相关知识,补全下列代码:

let fruits: [String] = ["apple", "orange", "pear", "pineapple"]
// 希望打印出 ["pineapple", "orange", "apple", "pear"]
// 即Array中的所有字符串按照长度从大到小排序

// TODO

提示:

阅读函数sorted(by:)文档func sorted(by areInIncreasingOrder: (Self.Element, Self.Element) -> Bool) -> [Self.Element]

如果你想直接在大括号中写函数的话,$0$1可以分别指代第1个和第2个参数。

答案:

print(fruits.sorted(by: { $0.count > $1.count }))

func longer(a: String, b: String) -> Bool {
    return a.count > b.count
}

print(fruits.sorted(by: longer))

统计各个专业的人数

import Foundation

enum Major {
    case A
    case B
    case C
    case D
}

struct Student {
    var name: String
    var major: Major
}

let studentList: [Student] = [
    Student(name: "Alice", major: .A),
    Student(name: "Bob", major: .B),
    Student(name: "Carl", major: .C),
    Student(name: "David", major: .D),
    Student(name: "Eric", major: .A),
    Student(name: "Fred", major: .D),
    Student(name: "Gavin", major: .C),
    Student(name: "Henry", major: .D),
    Student(name: "Ivy", major: .B),
    Student(name: "Janet", major: .D),
    Student(name: "Kathy", major: .A)
]

// 题目:
// 统计studentList中各个专业的同学名字和总数目,并按照你倾向的方式打印。

// TODO

一种解答:

var studentDict: [Major: [Student]] = [Major.A: [], Major.B: [], Major.C: [], Major.D: []]

for student in studentList {
    switch student.major {
    case .A:
        studentDict[.A]!.append(student)
    case .B:
        studentDict[.B]!.append(student)
    case .C:
        studentDict[.C]!.append(student)
    case .D:
        studentDict[.D]!.append(student)
    }
}

for major in [Major.A, Major.B, Major.C, Major.D] {
    let students = studentDict[major]!
    let studentNames = students.map { $0.name }
    print("\(major)(\(students.count)): \(studentNames.joined(separator: ", "))")
}

进阶

Error Handling

执行如下程序,结果为(

File "my homework" was successfully printed! (paper: 3, ink: 79.6%)
outOfPaper

)(共两行),简要解释输出结果:(作业打印之后打印机的纸张不足以打印报告,因此抛出错误 outOfPaper)。

import Foundation

struct Printer {
    var paper: Int // 0 - 400
    var ink: Float // 0 - 100 percentage
    let inkPerPage: Float = 0.1

    enum error: Error {
        case outOfPaper
        case noInk
    }
}

struct File {
    var name: String
    var page: Int
}

func printFile(_ file: File, with printer: inout Printer) throws {
    if printer.paper <= file.page {
        throw Printer.error.outOfPaper
    } else if printer.ink <= Float(file.page) * printer.inkPerPage {
        throw Printer.error.noInk
    } else {
        printer.paper -= file.page
        printer.ink -= Float(file.page) * printer.inkPerPage
        print("File \"\(file.name)\" was successfully printed! (paper: \(printer.paper), ink: \(String(format: "%2.1f", printer.ink))%)")
    }
}

var myHomework = File(name: "my homework", page: 4)
var myReport = File(name: "my report", page: 5)
var myPrinter = Printer(paper: 7, ink: 80)
do {
    try printFile(myHomework, with: &myPrinter)
    try printFile(myReport, with: &myPrinter)
} catch {
    print(error)
}

Log()

新建Project - macOS - Command Line Tool,复制运行如下代码:

import Foundation // 1

struct Log: CustomStringConvertible { // 3
    public var time: String // 4
    public var log: String // 5

    init(filePath: String = #file, line: Int = #line, column: Int = #column, funcName: String = #function) { // 7
        let fileName = (filePath as NSString).lastPathComponent // 8

        let formatter = DateFormatter() // 10
        formatter.dateStyle = .none // 11
        formatter.timeStyle = .medium // 12
        time = formatter.string(from: Date()) // 13

        log = "\(fileName)(\(line),\(column)) \(funcName)" // 15
    }

    public var description: String { // 18
        return "[\(time) \(log)]\n\t" // 19
    }

    public var string: String { // 22
        description // 23
    } // 24

    public var error: String { // 26
        "[ERROR] " + description // 27
    }
}

func MyPrint() { // 31
    print(Log().string + "Hello, world!") // 32
    print(Log().error + "Bye, world.") // 33
}

MyPrint() // 36

// Literal        Type     Value
//
// #file          String   The name of the file in which it appears.
// #line          Int      The line number on which it appears.
// #column        Int      The column number in which it begins.
// #function      String   The name of the declaration in which it appears.

〇 该程序的运行结果是(

[23:51:18 main.swift(32,14) MyPrint()]
	Hello, world!
[ERROR] [23:51:18 main.swift(33,14) MyPrint()]
	Bye, world.
Program ended with exit code: 0

)。

〇 第3行新建了一个结构体名为Log,后面冒号跟着的CustomStringConvertible是(协议),在结构体内部的变量(description)完成了CustomStringConvertible

〇 (Optional)可以去掉第4行的public关键字吗?(可以。这里加上public是为了使得变量属性更加明确。)

〇 第7行中,filePath: String = #filefilePath是参数名称,冒号后面的String是这个参数的(类型),=后面跟的是这个参数的(默认值)。

〇 (Optional)第8行中,(filePath as NSString)是在做(类型转换)。

〇 第12行中出现了.medium,它们是(枚举值)的一种。

〇(Optional)第12行如果要补全.前面的内容可以添加(DateFormatter.Style)。提示:查看.medium的文档。

〇 第13行中的formatter.string(from: ),这里string是一个(函数),看到from这一个介词可以推测from是参数的(外部名称)。

〇 第19行的\n\t是(转义)字符。

〇 第22行定义了变量string,我们将其称作(计算属性)。将22 23 24这三行代码补充完整:(

public var string: String {
    get {
        return description
    }
}

)。

〇 第32行的Log()新建)了一个结构体实例。

〇 第32行的+表示(字符串拼接)。

〇 第36行调用了函数MyPrint,该函数有(0)个参数,(0)个返回值。

〇 (Optional)尝试如下步骤在Xcode中添加code snippet

To create a code snippet:

  1. Select: print(Log().string + "<\hashinfo\hash>")
  2. Go to Xcode -> Editor -> Create Code Snippet.
  3. Change \hash to #.
  4. Set title as Log() and completion as print.

Try typing print and select the code snippet.

其他

〇 (Optional)如果我们想导入并使用一个开源的第三方Swift Package,需要点击Xcode菜单栏(File -> Swift Packages -> Add Package Dependency...)(写出层级关系),然后添加该Swift Package的地址。之后在需要用到该包的代码文件最开始写上import语句就可以了。

Swift有一个非常好用的格式化工具GitHub | SwiftFormat,阅读其README说明,在自己的Xcode中添加该格式化工具并尝试使用。你在你的Mac上安装了吗?()(安装了/还没/下次一定)。

〇 (Optional)Swift目前的最新版本是5.5,这一个版本引入的一个重要更新是Concurrency WWDC链接WWDC21 | Meet async/await in Swift,感兴趣的同学可以自行查看。

〇 (Optional)如果你还没有学习过Git,可以花时间掌握一下这个合作开发的必备工具。社团往期培训课程传送门 Git基础与进阶

Training July, 2021

Introduction

在当今信息化时代,生活中的很多事情都已经可以用一款App来搞定。开发App,有很多需要涉及的内容:比如代码如何编写、用户界面如何设计、应用如何迭代更新、如何找到现在市场需要的应用……其中最重要的可能是各种设计,但最基础的还是编写代码

这个暑假,我们主要学习的是SwiftSwiftUI。苹果几年前推出了自己的编程语言Swift,使用这门全新的、现代的编程语言让我们的开发过程变得高效容易。苹果也推出了全新的声明式UI框架SwiftUI,让短短几行代码即可变成屏幕上的用户界面。

这次暑培,主要是让大家熟练掌握Swift这门编程语言,使用SwiftSwiftUI来构建用户界面,为大家之后的实际参与开发打基础。在学习过程中,也希望大家能对App构建的过程、App设计的思路有一些简单的认识。

Arrangement

组织社团开发组的同学、社团内对iOS开发有兴趣的同学从【7月13日(周二 小学期第三周周二)】开始,通过查看WWDCSwiftUI相关的教程、学习Stanford CS193p - Developing Apps for iOS的前六课,掌握使用SwiftUI开发iOS App的核心知识。在这期间开发组的核心成员将安排答疑坊,有任何问题我们都可以一起讨论。

在这之前,我们推荐没有编程经验的同学先学习浙江大学 Swift创新导论前三周课程中与Swift相关的部分,掌握编写程序的基本方法。如果有同学有其他语言编程经验,但希望提前学习Swift(Stanford的课程也会教你如何写Swift),可以参考Aimls Swift编程基础Swift有一个提前的了解。

具体的时间安排见下,具体的课程内容见Training Details

Schedule

〇 7月13日(周二,小学期第三周)

学习Swift语法

WWDC20 Introduction to SwiftUI

〇 7月14日(周三,小学期第三周)

熟悉Swift语法

Stanford cs193p Lecture 1 Getting Started with SwiftUI

〇 7月15日(周四,小学期第三周)

Stanford cs193p Lecture 2 Learning More about SwiftUI, Reading Assignment 1, Programming Assignment 1

〇 7月16日(周五,小学期第三周)

Stanford cs193p Lecture 3 MVVM

WWDC20 App essentials in SwiftUI

〇 7月17日(周六,小学期第三周)

Stanford cs193p Lecture 4 More MVVM enum Optionals, Reading Assignment 2, Programming Assignment 2

〇 7月19日(周一,小学期第四周)

Stanford cs193p Lecture 5 Properties Layout @ViewBuilder

WWDC20 Data Essentials in SwiftUI

〇 7月20日(周二,小学期第四周)

Stanford cs193p Lecture 6 Protocols Shapes, Reading Assignment 3, Programming Assignment 3


Notice Stanford cs193p主页

Notice Stanford课程链接点不开的同学,开发组的同学帮大家把视频和作业都下载搬运到了清华云盘,大家可以查看在清华云盘查看该课程(包含 YouTube视频英文字幕B站中英硬字幕搬运):https://cloud.tsinghua.edu.cn/d/bd289554fd0b4f9eb920/

答疑安排

7月13日(周二,小学期第三周)~ 7月20日(周二,小学期第四周)

地点:微信交流群Swift SwiftUI 答疑

点击查看入群二维码

Training Details 2021, July

WWDC

WWDC可以说是每年Apple发布新技术的盛会。作为开发者,我们不仅可以在其中找到Apple关于新技术的介绍,也可以看到很多Apple为初学者准备的教程。大家只需要在Mac或其他设备上下载Developer.app,就可以看到每年WWDC的全部内容。我们这次主要关注与SwiftUI相关的内容

〇 推荐大家先查看WWDC20Introduction to SwiftUI,通过官方的三明治App的例子对SwiftUI有一个简单的了解。

✡ WWDC20 Introduction to SwiftUI https://developer.apple.com/wwdc20/10119

这个talk介绍了swiftUI的基本特性。打开Xcode,编辑器的左侧显示了代码,右侧显示了一个带有该代码可视化表示的画布。在 SwiftUI 中,视图定义只是 Swift 代码,这意味着画布和代码编辑器只是查看和编辑相同代码的不同方式。如果在画布中选择某些内容,该选择也会反映在代码中。如果更改代码中的某些内容这种变化也反映在画布上。

操作演示:

  • 文本旁边添加一个图像
  • 更改为字体
  • 添加圆角矩形
  • NavigationView 支持在应用程序的不同部分之间导航
  • NavigationLink 接收要推送的目的地
  • ……

SwiftUI 中的视图非常轻巧。提取子视图几乎没有运行时开销。 SwiftUI 中的视图和传统 UI 框架中的视图完成相同的主要角色:它们定义了一个 UI。UI 编程中同步多线程代码很难。但是swiftUI淡化了它的难度。最重要的是能够在所有 Apple 平台之间使用相同的视图代码、模型代码和应用程序代码。也可以对特定平台进行改进。

〇 在学习Stanford课程的过程中,可以参考下面两个WWDC20的session,对SwiftUI里面的App Scene View@State @ObservedObject有更深入的了解,从而更好的管理自己的App层次App中的数据流动

✡ WWDC20 App essentials in SwiftUI https://developer.apple.com/wwdc20/10037

今年SwiftUI拓展了使用用于声明场景和应用程序的新 API 。可以仅使用 SwiftUI 构建整个应用程序。这个talk介绍了 SwiftUI 的场景架构,并展示如何自定义应用程序中的场景,简要概述了可用于自定义应用程序的不同 API,以及可以从哪里了解更多信息。

今年新增了 DocumentGroup 场景类型,它自动管理打开、编辑和保存基于文档的场景。更多信息在“在 SwiftUI 中构建基于文档的应用程序” session。

✡ WWDC20 Data Essentials in SwiftUI https://developer.apple.com/wwdc20/10040

【Instantiate a Model Object in a View】

Connect data to show in views with data defined in Model.

Model class:

class Book: ObservableObject {
	/// @Published: Once title changed, Views related with Book refreshed.
	/// @Published works when this class is an ObservableObject
    @Published var title = "Great Expectations" 
}

SwiftUI codes:

struct LibraryView: View {
    @StateObject var book = Book() // Book is a class conforming to ObservableObject. Only instantiating uses `@StateObject`
    
    var body: some View {
        BookView(book: book)
    }
}

struct BookView: View {
    @ObservedObject var book: Book // Here book is not an instance of Model. So yse @ObservedObject

    // ...
}

【Share an Object Throughout the App】

Save your time to pass arguments again and again if there are plenty of layers.

@main
struct BookReader: App {
    @StateObject var library = Library() // initiate ObservableObject with `@StateObject`
    
    var body: some Scene {
        WindowGroup {
            LibraryView()
                .environmentObject(library) // Here pass `library` to all sub-views of LibraryView().
        }
    }
}
struct LibraryView: View {
    @EnvironmentObject var library: Library // Get that EnvironmentObject
    
    // ...
}

【Manage Mutable Values】

〇 当层View中需要更改的值定义为@State var(因为要保存这个值,而不是每次refresh UI的时候值都回到初始值) 〇 当层View使用的不可更改的值,定义该值为let

〇 当层View需要给下层View传值、且下层View只是使用而不改变该值,下层View中定义该值为var 〇 当层View需要给下层View传值、且下层View会改变该值,下层View中定义该值为@Binding var,确保下层View改的是当层View值的引用(改的是同一个);当层View传递时写$foo

〇 某值只有当层和subView用,加private,上层View无法访问到该量

struct PlayerView: View {
    let episode: Episode
    @State private var isPlaying: Bool = false
    
    var body: some View {
        VStack {
            Text(episode.title)
            Text(episode.showTitle)
            PlayButton(isPlaying: $isPlaying) // Pass a binding.
        }
    }
}
struct PlayButton: View {
    @Binding var isPlaying: Bool
    
    var body: some View {
        Button(action: {
            self.isPlaying.toggle() // change isPlaying in sub-view. So use @Binding
        }) {
            Image(systemName: isPlaying ? "pause.circle" : "play.circle")
        }
    }
}

〇 通过下面这几个session,你可以看到每年苹果对SwiftSwiftUI的更新。2019年是Swift大版本Swift5.0推出的第一年,也是SwiftUI元年,再往前的WWDC和Swift与SwiftUI关系不大。

注:WWDC21的内容,在今年秋季、各平台新系统发布才能正式运用。感兴趣的同学可以 提前了解,但请注意没有办法在这个暑假使用。

WWDC21 What's new in Swift https://developer.apple.com/wwdc21/10192

WWDC20 What's new in Swift https://developer.apple.com/wwdc20/10170

WWDC19 What's new in Swift https://developer.apple.com/wwdc19/402

WWDC21 What's new in SwiftUI https://developer.apple.com/wwdc21/10018

WWDC20 What's new in SwiftUI https://developer.apple.com/wwdc20/10041

点进去之后,看完这些视频,可能会发现这些视频下面都会有一些相关的推荐,大家感兴趣也可以对WWDC推出的新内容做进一步的了解。

〇 还有一些WWDC19的session,但这些视频已经过时,而且苹果在WWDC20上对这些视频进行了更新,也就是在上面列出来的那些;所以虽然WWDC19对于SwiftSwiftUI来说很重要,但我们现在只需要看新的session就好。

大家如果想感受一下SwiftUI刚刚发布的时候大家的热情,可以看:

WWDC19 Keynote https://developer.apple.com/wwdc19/101 的2h6min34s - 2h14min22s

还有下面的视频,前二十分钟对新推出的SwiftUI也有详细的介绍,感兴趣的同学也可以看一下:

WWDC19 Platforms State of the Union https://developer.apple.com/wwdc19/103

〇 另外,如果想跟进苹果最新发布的技术,每年WWDCKeynotePlatforms State of the Union都可以关注一下,对设计和App开发感兴趣,每年的Apple Design Awards也可以进行关注;感兴趣的相关session也可以自己抽时间查看。

Stanford CS193p - Developing Apps for iOS

英文课程 Swift SwiftUI App设计思维

课程网址:https://cs193p.sites.stanford.edu

课程视频(YouTube English):https://youtube.com/playlist?list=PLpGHT1n4-mAsxuRxVPv7kj4-dQYoC3VVu

课程视频 中英字幕:https://www.bilibili.com/video/BV1fy4y1u7hX

课程清华云盘链接(包含 YouTube视频英文字幕 和 B站中英硬字幕搬运):https://cloud.tsinghua.edu.cn/d/bd289554fd0b4f9eb920/

非常系统性的Swift+SwiftUI教程!老师已经开了很多年的iOS开发课程了。暑期我们希望大家能完成前六课的学习(六节课的内容还是蛮多的哦,希望大家认真对待),更希望大家看完前六课之后也继续学习后面的课程。希望这门课程能让你入门iOS开发!

Basic

Lecture 1 Getting Started with SwiftUI

The first lecture jumps right into building the first application of the quarter: a card-matching game called Memorize. It will be the foundation for the first few weeks of course material.

Lecture 2 Learning More about SwiftUI

Development on Memorize continues. Creating reusable components (a Card in the game) and combining them to make more complex user-interfaces.

Lecture 3 MVVM

Conceptual overview of the architectural paradigm underlying the development of applications for iOS using SwiftUI (known as MVVM) and an explanation of a fundamental component of understanding the Swift programming language: its type system. Then both of these are applied to the Memorize application started in the first two lectures.

Lecture 4 More MVVM enum Optionals

The MVVM architecture is fully applied to Memorize. The important Swift concepts of enums and Optionals are covered and used to finish off the game logic of the Memorize game.

Lecture 5 Properties Layout @ViewBuilder

Explore property observers, computed properties, @State and @ViewBuilder. The mechanisms behind how Views are layed out on screen are examined followed by a demo which chooses a better font for each card in Memorize depending on the space available. Along the way, apply better access control to Memorize's internal API.

Lecture 6 Protocols Shapes

Discussion of what is perhaps the most important type in Swift: a protocol. The demo combines the concepts of generics and protocols to make the cards better use the space available on screen. Finally the Shape protocol is explained and the pie-shaped countdown timer is added to Memorize (but not yet animated).

Advanced

Lecture 7 ViewModifier Animation

Lecture 8 Animation Demo

Lecture 9 EmojiArt Drag/Drop

Lecture 10 Gestures

Lecture 11 Persistence Error Handling

Lecture 12 Binding Sheet Navigation EditMode

Lecture 13 Publisher More Persistence

Lecture 14 Document Architecture

浙江大学 - Swift创新导论

中文课程 Swift语法 UIKit 项目实战

链接:https://www.icourse163.org/course/ZJU-1450024180

前三周课程清华云盘链接:https://cloud.tsinghua.edu.cn/d/2ddcf31a51cf4a48b690/

前三周的教程可以让没有编程经验的同学对Swift这门编程语言、编程思维iOS应用开发有一个简单的了解。

后面的课程并不推荐大家学习,因为并没有使用Apple最新的SwiftUI框架进行教学;但是有一些很好的例子能够培养大家的创新思维,感兴趣的同学也可以看看后面几周中一些实际的案例。

第一周

第1讲:课程概述

第2讲:Swift浅析

第3讲:Swift编程基础知识

第4讲:移动应用设计流程

第5讲:iOS 人机交互基础

第6讲:iOS交互设计实践(一)

第7讲:iOS交互设计实践(二)

第8讲:常量、变量、函数

第9讲:数组与字典

第10讲:控制流(Control Flow)(一)

第11讲:控制流(Control Flow)(二)

第二周

第12讲:函数(Functions)和闭包(Closures)(一)

第13讲:函数(Functions)和闭包(Closures)(二)

第14讲:Playground 实践应用(一)

第15讲:Playground 实践应用(二)

第16讲:对象(Objects)和类(Classes)(一)

第三周

第17讲:对象(Objects)和类(Classes)(二)

第18讲:枚举(Enumeration)和结构(Structures)(一)

第19讲:枚举(Enumeration)和结构(Structures)(二)

第20讲:协议(Protocols)和拓展(Extensions)(一)

第21讲:协议(Protocols)和拓展(Extensions)(二)

第22讲:错误处理(Error handling)(一)

第23讲:错误处理(Error handling)(二)

Aimls - Swift编程基础

中文教程 Swift语法

Swift5.0语法讲解 链接:https://www.bilibili.com/video/BV144411C7Gg

Swift5.1新特性 SwiftUI初步了解:https://www.bilibili.com/video/BV1gJ411M72p

全部课程知识点整理总结:https://www.bilibili.com/video/BV1jE411y7cw

教程云盘链接:https://cloud.tsinghua.edu.cn/d/bba8530022af47de9e0b/

学习Swift编程语法的教程。Stanford的教程中也会教你如何使用Swift,但其中主要是靠你自学,可能会有些累。有编程基础的同学可以二倍速观看上面的Swift教程,对Swift有一个简单的理解。为什么要用二倍速呢?只要学过一门编程语言,学习其他编程语言就会特别快。推荐大家在学习过程中做好笔记

designcode - SwiftUI iOS

英文教程 较高质量的付费教程 不做要求 想提升的同学可以看(教程看得越多学到的东西越多)

教程清华云盘链接:https://cloud.tsinghua.edu.cn/d/65cd3bfc68844bd68356/

designcode.io

iOS Club - Markdown & Git

大家在学习的过程中可以使用Markdown来整理笔记。社团之前有Markdown的培训,已经上传至B站,不了解Markdown的同学可以进行查看。【教程】面向开发的Markdown学习

在之后的开发过程中,一件重要的事情是团队成员间的代码协作,社团之前也有过版本控制工具Git的相关培训,以后打算进行团队开发、还不了解版本控制的同学可以进行查看。【教程】Git基础与进阶

全部课程的清华云盘链接:https://cloud.tsinghua.edu.cn/d/01f70b980e8946c3a8b7/

Stanford CS193p L1-L6 观前引导

Author: 王可 王拓为

Lecture1 Getting started with SwiftUI

介绍Xcode、SwiftUI的最基础界面编写。

重点:

  • 弄清楚下列代码中每个关键字的含义与作用

    import SwiftUI
    
    struct ContentView: View {
        var body: some View {
            Text("Hello, world!")
                .padding()
        }
    }
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
    
  • 弄清楚文件界面中每个文件的作用

作业:

1.为什么要body的类型为some View?

答案:因为body得到的可能不是严格的View类型。

2.使用SwiftUI编写App时,整个程序的入口是哪个文件(类似于C中main函数)?

答案:文件名为: AppName+App.swift

代码为

import SwiftUI

@main
struct lecture1App: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

Lecture2 Learning more about SwiftUI

本节课将更进一步地介绍一些SwiftUI的设计模式,包括View、ZStack等组件的互相嵌套,还讲了数组、Foreach、按钮的用法。

重点:

函数相关
  • 函数的最后一个参量可写在括号外:

    Button(action: {
      
    },label:{Text("s")})
    
    Button(action: {}) {
      Text("s")
    }
    

    两者等价,这在实际使用时会更加简洁。

界面相关
  • LazyGrid的用法
  • ScrollView的用法
Spcaer()
  • 一种设计样式,能尽可能的扩充空间
@State
  • 变量修饰符,能在该值改变时同步对页面进行更新

作业:

照着课上的代码自己写一遍,做一些修改,最好能写到手机上体验一下

Lecture3 MVVM and the swift type system

本节课介绍在SwiftUI设计理念中极为重要的MVVM(Model-View-ViewModel)模式,同时介绍除VIew界面外,其他两个界面:Model,ViewModel的写法。

重点:

MVVM设计范例
  • Model->ViewModel->View,有点类似于C++设计中,底层+界面控制+界面的逻辑。
structclass的区别与联系
  • 前者是面向过程的结构体,后者是面向对象的结构体
  • 其他区别见课程

作业:

  1. 跟着老师把代码敲一遍,理清MVVM之间的关系。

  2. View界面是面向过程的还是面向对象的?

    答:面向过程。

Lecture 4 More MVVM enum Optionals

在这一讲中, 前面提到的 MVVM 模型将被完整地应用到游戏 Memorize 的编写中。enums 和 Optionals 这两个 Swift 编程中的关键概念将被介绍并用于完善游戏 Memorize 的逻辑部分。

enum
  • Another variety of data structure in addition to struct and class

  • Associated Data

  • Setting the value of an enum

  • Checking an enum's state

  • break statement & default statement

  • Methods yes, (stored) Properties no

  • Getting all the cases of an enumeration

Optionals
  • An Optimal is just an enum

  • We use Optional at any time we have a value that can sometimes be 'not set' or 'unspecified' or 'undetermined'

Lecture 5 Properties Layout @ViewBuilder

在这一讲中,将介绍 property observers,computed properties,@State 和 @ViewBuilder。我们也将继续完善游戏 Memorize,并通过一个在给定空间中为卡片选取合适字体大小的例子揭示 Views 在屏幕上的布局方式的背后机制。在这一过程中,我们对游戏 Memorize 内部 API 实现了更好的访问控制。

Swift Demo Warmup
  • Access Control
@ViewBuilder
  • What exactly is that argument to ZStack, Foreach, GeometryReader, etc?
Shape
  • What if I want to draw my own View rather than constract it from other Views?
Animation
  • Mobile app UIs look pretty bad without animation.

  • Luckily, in SwiftUI, animation (almost) comes for free!

ViewModifier
  • What exactly are functions like foregroundColor, font, padding, etc. doing?

Lecture 6 Protocols Shapes

在这一讲中,我们将讨论 Swift 编程中最重要的类型 —— protocol。为了使游戏 Memorize 中的卡片可以更好地使用屏幕上的给定空间,我们将结合使用 generic 和 protocol 两种概念。最后,我们使用名为 Shape 的 protocol 将一个饼状计时器加到游戏 Memorize 中。

Property Observers
  • "Watching" a var and reacting to changes
@State
  • State that is entirely localized inside a View

  • Only used for temporary state like presentation of alerts or editing things or animation

Animation
  • Implicit vs. Explicit Animation

  • Animation Views (via their ViewModifiers which can implement the Animatable protocol)

  • Transitions (animating the appearance/dissppearance of Views by specifying ViewModifiers)

  • Animating Shapes (via the Animatable protocol)

Tools

Written by 杨希杰 on 210324

Swift Formatter

Xcode Extension|Swift Format Tool

  • 格式化Swift代码的Xcode插件。结合Xcode自带的快捷键设定可以实现快捷键格式代码的功能

Google

当你开始开发应用之后,百度一定会无法满足你的需求。这时就要去请教你最好的朋友Google了。

Git

项目代码版本管理工具:Git Official Site

iOS Club 教程:Git基础与进阶

廖雪峰|Git教程

  • ✡ RECOMMENDED ✡
  • 非常通俗易懂的文字版Git教程,推荐多次翻阅

Bilibili|电子系科协2020年暑培 p1 Git

可视化学习Git

  • 对Git中的高端操作不理解可以去试试可视动画
  • 初学阶段也可以通过闯关来加深对git的理解

Git docs|快速查阅git操作

GitHub|git-flight-rules

  • ✡ RECOMMENDED ✡
  • 如果你在使用git的过程中遇到了棘手的问题应该都可以在这里找到解决方法

一个挺详细的 Git 中文手册

Git Pro|进阶git的一本厚书

  • 一般没有必要看

Linux

开源类Unix操作系统,目前服务器安装的系统主要是Linux。如果要使用网络传输数据,那么跑在Linux上的服务器是必须的。

菜鸟教程|Linux

GitHub

世界范围的代码托管平台。

Markdown

一种轻量的格式文本编辑风格。与word等富文本编辑不同,markdown采用一些标记来表示文本格式。一般使用markdown编辑的文本文件后缀为.md,进行渲染可以得到格式文本。

iOS Club 教程:面向开发的Markdown学习

markdown编辑器推荐:

  • Typora
    • 全平台免费markdown编辑器,所见即所得型markdown编辑器。
  • VS Code安装Markdown插件

VS Code

非常受世界开发者欢迎的轻量文本编辑器。

虚拟机

通过软件模拟硬件的方式实现在不同的硬件上运行不兼容的系统。

iOS开发主要使用Mac进行开发,需要使用Xcode,如果在学习阶段没有Mac可以参考清华云盘上的资源(Groups/iOS-Club-THU/iOS-Club/培训Training)安装虚拟机。在项目开发中如果没有Mac可以从社团借用。

Lookin

iOSUI调试工具

Lookin官网

CocoaPods

项目引入库管理

CocoaPods简介

Postman

测试HTTP请求

Other

Apple Developer

GitHub|EESAST training2020

  • 清华大学电子系学生科协 2020 年暑期培训相关 PPT 与文档
  • 包括但不限于:Git、Linux、Docker、HTML/CSS/JS/TS/React/Node.js/MongoDB/GraphQL/Hasura/Apollo、CI/CD
  • GitHub pages|training2020
  • 视频链接:Bilibili training2020

EESΛST Docs

  • 电子系科协技术文档仓库:包括Languages、Tools、Game、Web等部分。

MIT course|The Missing Semester of Your CS Education

  • ✡ RECOMMENDED ✡
  • 大学里的计算机课程通常专注于讲授从操作系统到机器学习这些学院派的课程或主题,而对于如何精通工具这一主题则往往会留给学生自行探索……在这个系列课程中,我们讲授命令行、强大的文本编辑器的使用、使用版本控制系统提供的多种特性等等……精通这些工具不仅可以帮助您更快的使用工具完成任务,并且可以帮助您解决在之前看来似乎无比复杂的问题。
  • 包括但不限于:shell、shell script、Vim、Git
  • MIT course|The Missing Semester of Your CS Education 中文讲稿

12factor for SAAS

  • In the modern era, software is commonly delivered as a service: called web apps, or software-as-a-service. The twelve-factor app is a methodology for building software-as-a-service apps.
  • 文章阐述了网络服务应用的十二个要素。只要从事与网络相关的App开发,这篇文章能够给你提供一个较宏观的认识与标准。
  • 12factor|中文翻译

GitHub|Student-resources

  • 本文介绍的是利用学生、教职工身份可以享受到的相关学生优惠、教育优惠或教师优惠的权益(比如如何白嫖高性能服务器

How To Ask Questions The Smart Way

  • GitHub|提问的智慧 简体中文版
  • 提问的时候总是惹别人生气、或者别人根本不理你?可能是你的提问方式出了点问题。提问的智慧教你如何在信息时代优雅的向别人提问!

VPN in China

iOS网络服务应用尝试

Written by 杨希杰 on 210324

GraphQL

Introduction

一种前后端数据请求/交互的风格/标准/规范。是有着大公司做靠山、较新的查询语言规范,是一种基于图的查询语言。对比原有的REST请求风格,GraphQL能够让你每次都获取到不多不少的数据,结合社区的工具,几乎可以省掉后端的开发团队,让你的项目开发效率大大提升。

GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.

GraphQL clients是嵌入在前端/需要在前端导入的一个工具,这个工具能帮助你在UI中引入数据查询,让你用GraphQL的语句来向后端的GraphQL server发送请求。

GraphQL clients can help you handle queries, mutations, and subscriptions to a GraphQL server. They use the underlying structure of a GraphQL API to automate certain processes. This includes batching, UI updates, build-time schema validation, and more.

Resources

GraphQL

  • GraphQL官网

GraphQL|中文官网

  • 推荐看英文官网

GraphQL | Learn

  • IMPORTANT
  • 入门GraphQL

GraphQL Client|Apollo Client (iOS)

Apollo is the industry-standard GraphQL implementation, providing the data graph layer that connects modern apps to the cloud.

Apollo

  • Apollo官网

Apollo docs|Apollo Basics

  • IMPORTANT

Apollo Client | iOS

GraphQL Server|Hasura + Databse

GraphQL Server处理前端从GraphQL Client发送的GraphQL请求,在数据库中获取相应的数据并返回到前端/客户端。

这里Hasura相当于一个控制台,帮你在中间操作数据库;同时Hasura也提供可视化的控制台,让数据库的管理变得方便。

The Hasura GraphQL engine makes your data instantly accessible over a real-time GraphQL API, so you can build and ship modern apps and APIs faster. Hasura connects to your databases, REST servers, GraphQL servers, and third party APIs to provide a unified realtime GraphQL API across all your data sources.

Hasura

  • Hasura官网

Hasura | Tutorials

  • IMPORTANT
  • 教程资源整合,包括了从GraphQL到Hasura的所有内容
  • 具体见下方

Hasura | GraphQL Engine Documentation

  • IMPORTANT
  • 关于Hasura的部署使用的文档

GitHub | GraphQL engine

Hasura Cloud

  • Hasura Cloud相当于一个搭建好的Hasura平台,可以直接通过在其中创建的project开启console。
  • 注意这个是不带数据库的,你需要将一个已有的postgres数据库链接到Hasura上(这里数据库可以用Heroku免费的,但我并不清楚有这个的使用是不是有限制)
  • 免费版的可以自己尝试玩,但是有流量限制(1G/month),一分钟也只能请求60次
  • 标准版流量可叠加(20G/month,2刀 per additional GB),但没有请求限制。每月100刀
  • Hasura Cloud Documentation

其他:

  • Hasura-cli
    • 记录每次在Hasura控制台对数据库的操作,方便对项目进行迁移
  • 跑在服务器Docker上的postgres
    • 也可以在服务器上安装postgres
    • 需要将数据库的地址或服务端口交给Hasura

对象存储服务

图片和视频等资源存储在哪里?数据库里?服务器的文件路径下?云服务厂商提供的对象存储服务上?

登录验证

token、jwt需要去了解一下,密码加盐如何前后端中分别实现?

Hasura Tutorial

Hasura | Tutorial

  • 一条龙的教程资源整合。非常详细的文档。总之就是非常推荐
  • GitHub|Hasura | Tutorials
    • 所有教程代码的存放仓库

Introduction to GraphQL

Hasura Tutorial|GraphQL intro

  • IMPORTANT
  • GraphQL简介,包括:GraphQL是什么,GraphQLREST的区别,GraphQL中的核心概念和术语,GraphQL三种请求数据的方式,GraphQL ClientsGraphQL Servers

Frontend GraphQL Tutorial

Hasura Tutorial|GraphQL Client with iOS

  • IMPORTANT

Hasura Tutorials

Hasura Tutorial|Hasura Basics

  • IMPORTANT

Hasura Tutorial|Auth Patterns with Hasura

  • IMPORTANT

Hasura Tutorial|Hasura Advanced

  • IMPORTANT

Sample Apps

Hasura Tutorial|WhatsApp Clone

Hasura Tutorial|React to-do app

Build This Site

Intro

This site is generated by mdbook and is issued by GitHub pages using GitHub workflow.

mdbook render markdown files to html. Use brew install mdbook to install it on your Mac. GitHub pages develops this site to GitHub server.

Repository

You can go to GitHub repo by clicking the GitHub icon in the top right corner.

Docs

mdbook docs

mdbook docs | SUMMARY.md

mdbook docs | book.toml

How to Contribute

First, clone the repo on your Mac.

Then, add your markdown files in mdbook/src/article. Also add your file path and title in mdbook/src/SUMMARY.

You should pay attention to images which are suggested to put in mdbook/src/media and use relative paths, or use urls instead, to avoid image disappearing.

Next, fork that repo and push your edition to your GitHub. Then make a pull request and wait until admin merge your edition.

Other

Change image size

<img src="" style="zoom:40%"/>