豪翔天下

Change My World by Program

0%

之所以要叫超简短书评,是因为这本书我根本没看完,只看了前面几篇文章,我就毫无继续看下去的动力了。说来也怪,我看过那么多的书,还从来没有这种感觉。可能是最近身边的杂事太多,也可能是这本书确实不如期望中的那样好。给人的感觉就是作者在写身边的小故事,但是故事平平静静,并且平静中并没有什么惊喜,甚至我连他到底在讲什么都不知道。最后,和大多数散文集一样,此书书名“一生里的某一刻”同样取自其中一篇。然后我就直接跳到那一篇。果然,依然是云里雾里的。超简短书评,就这样吧,相信大家也能看出我对此书的态度了。

基本概念及程序框架

主要目录文件

AppDelegate.swift: 所有应用开始都有一个AppDelegate,是整个应用程序的一个代理。在应用启动的时候,最先被调用的就是这个AppDelegate中的applicationDidFinishLaunching方法,可以在这里做全局初始化,但一般为了保持代码的整洁,具体逻辑并不放在这里。

Assets.xcasserts:

Main.storyboard: 项目主要的UI文件

Info.plist: 项目的基础配置

*.h: 头文件。包含类,类型,函数和常量的声明

*.m: 源代码文件。可以有Objective-CCSwift代码

*.mm: 源代码文件。可以有Objective-C/C/C++/Swift

*.cpp: C++代码

组件IB(Interface Builder)

Labels and Text Field

Combo Boxes

Text Views

Sliders

Date Pickers

Buttons

Radio Buttons

Check Buttons

Image Views

定位

居中定位

1
# 直接选择该Object右下角的Align的Horizontally in Container和Vertically in Container全部设置为0,然后选择Update Frame修改为Items of New Constraints,再点击Add 2 Constraints button.

权限

info.plist权限列表,如果要用到相应的功能却没有添加相应的权限,很可能APP会直接闪退。以下列表来自于[槑头脑]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!-- 相册 --> 
<key>NSPhotoLibraryUsageDescription</key>
<string>App需要您的同意,才能访问相册</string>
<!-- 相机 -->
<key>NSCameraUsageDescription</key>
<string>App需要您的同意,才能访问相机</string>
<!-- 麦克风 -->
<key>NSMicrophoneUsageDescription</key>
<string>App需要您的同意,才能访问麦克风</string>
<!-- 位置 -->
<key>NSLocationUsageDescription</key>
<string>App需要您的同意,才能访问位置</string>
<!-- 在使用期间访问位置 -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>App需要您的同意,才能在使用期间访问位置</string>
<!-- 始终访问位置 -->
<key>NSLocationAlwaysUsageDescription</key>
<string>App需要您的同意,才能始终访问位置</string>
<!-- 日历 -->
<key>NSCalendarsUsageDescription</key>
<string>App需要您的同意,才能访问日历</string>
<!-- 提醒事项 -->
<key>NSRemindersUsageDescription</key>
<string>App需要您的同意,才能访问提醒事项</string>
<!-- 运动与健身 -->
<key>NSMotionUsageDescription</key> <string>App需要您的同意,才能访问运动与健身</string>
<!-- 健康更新 -->
<key>NSHealthUpdateUsageDescription</key>
<string>App需要您的同意,才能访问健康更新 </string>
<!-- 健康分享 -->
<key>NSHealthShareUsageDescription</key>
<string>App需要您的同意,才能访问健康分享</string>
<!-- 蓝牙 -->
<key>NSBluetoothPeripheralUsageDescription</key>
<string>App需要您的同意,才能访问蓝牙</string>
<!-- 媒体资料库 -->
<key>NSAppleMusicUsageDescription</key>
<string>App需要您的同意,才能访问媒体资料库</string>

widget也都可以通过代码来设置他们的属性,例如

在applicationDidFinishLaunching函数内部写上

label1.stringValue = “wanghao”
button1.title = “Change Text”
Toolbar 菜单栏
view controller,也就是最开始的那种storyboard
container view,这里应该是可以放其他的view,相当于网页里面的iframe
custom view
vertical Split view/horizontal split view :水平或者垂直分割的view
Collection view:就是将一些列的数据以表格的形式展(包含了Collecotion Item),datasource可以直接outlets到view controller
object:就是一个对象,它可以与类帮顶起来(右上角custom class进行定义),定义了过后,又可以将它与下面的view相联系(control+拖曳,选择outlets),
table view:表格

Array Controller:好像是管理一组controller

menu bar:

要连接两个view,也是control拖曳

这个地方可以添加绝对定位或者想对定位 绝对定位想对定位

右边那个按钮则是像html里面的margin

上面是Day 2:auto layout

创建新的类的时候也可以同时创建xib文件,上面的collection controller,如果要有一个复杂的item那么久需要自己创建一个类

menu bar

直接在appdelegate里面:要设置application is agent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
let statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(NSVariableStatusItemLength)
//let menu = NSMenu()
let popover = NSPopover()

func applicationDidFinishLaunching(...){
if let button = statusItem.button {
button.image = NSImage(named: "图片名称")// 在asets里面添加了的图片
//button.action = Selector("showWeatheraaa")

button.actoin = Selector("toggleWeather:")
}

//menu.addItem(NSMenuItem(title: "直接加", action: Selector("showWeather:"), keyEquivalent:"S"))
//menu.addItem(NSMEnuItem.separatorItem())
//menu.addItem(NSMenuItem(title:"quit", action: Selector("terminate:"), keyEquivalent:"q"))

// statusItem.menu = menu
popover.contentViewController =
}

func showWeather(sender: NSStatusBarButton){
print("我靠")
}

func toogleWeather(sender: NSStatusBarButton){
if popover.shown{
popover.performClose(sender)
}else{
if let button = statusItem.button {
popover.shownRelativeToRect(button.bounds, ofView: button, perferredEdge: .MinY)
}
}
}

新建了NSOBbject过后,要让它MainMenu .xib加载的时候久被加载,需要把它以object的形式添加到XIB中去

而这个object的Outlets就是相对应的view,比如Status Menu

同理如果创建的是一个NSview,那么一个view泽可以与之关联

安装与配置

  • angular不同的版本对typescript的版本要求是不同的,可以参考这里
  • angular升级是非常简单的,只要参考官方升级文档一步一步升即可
1
2
3
4
5
6
7
8
9
10
11
ng serve --host 0.0.0.0 --port 3000	# 启动,指定host,指定port

ng build --aot --optimization --build-optimizer # 编译项目
--aot # 默认为false,是否用提前编译进行构建
--optimization # 默认为false,使用构建输出优化
--build-optimizer # 默认为false,使用aot进行优化,推荐加上这个参数
--extract-css # 默认为false,从全局样式中提取css到css文件而不是放在js文件
--source-map # 默认为true,输出source-map文件
--vendor-chunk # 默认为true,将第三方包单独放到一个vendor文件中

ng build --deploy-url /app/ --deploy-url /app/ # 如果想要app运行在一个子路由路径下可以这样做

Module

1
2
3
4
5
6
7
8
9
10
11
@NgModule({
declarations: [
UserComponent
],
imports: [

],
entryComponents: [
DialogComponent, // 对于动态调用的组件,没有在html中调用,而是用js来调用的组件需要在这里声明,例如一些弹框,不声明的话,在动态编译的时候可能发现模板没有引用就不去加载了,但是我发现在lazy loading的时候,如果在child module中声明entryComponents不起作用,只能在app.module中声明才行
]
})

Lazy loading延迟加载

  • 延迟加载是基于页面路由的,每个路由都可以单独作为一个延迟加载,在进入页面的时候加载该页面所需要的组件
  • 如果实现了延迟加载我们在进入对应的页面后会发现新请求一个1.xxxx.js的文件,开头是一个数字。这就是当前页面的一些组件,同时我们会发现当前页面的组件在main.js中没有了
  • 如果我们的页面都是单纯的component而不是module的话需要做这些改造
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 在页面组件新建路由文件,例如dashboard.route.ts
export const routes: Routes = [
{path: '', component: DashboardComponent}
]

// 在页面组件新建module文件,例如dashboard.module.ts
import {routes} from './dashboard.route';
@NgModule({
imports: [
CommonModule,
RouterModule.forChild(routes) // 注意这里是forChild不是forRoot
],
declarations: [
DashboardComponent,
]
})
export class DashboardComponent { }

// app-routing.module.ts修改路由方式
const routes: :Routes = [
{
path: 'dashboard',
loadChildren: () => import('./dashhboard/dashboard.module').then(m => m.DashboardModule) // 注意这里是Module不是Component
}
]

// 最后在app.module.ts中,移除DashboardCompoent依赖即可

模板语法

数据绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 变量字符串连接
<img src="https://haofly.net/{{ image.url }}" />

// 动态绑定类
[ngClass]="{'myClass': selected}"
[ngClass]="type='xxx' ? 'mt-1' : 'mt-2'"

// 动态绑定样式
[ngStyle]="{'pointer-events': ok ? 'none' : 'auto'}"

<div [innerHTML]="string"></div> // 直接渲染html变量,默认会去掉元素内部的inline styling等属性
<div [innerHTML]="var"></div>
constructor(protected _sanitizer: DomSanitizer) {
this.var = this._sanitizer.bypassSecurityTrustHTML('string') // 直接渲染html变量,和上面的innerHTML不同的是,这样做,元素内部的inline styling等不会被去掉
}

控制语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// for 循环
<ul>
<li *ngFor="let item of items; let i = index">
{{i}}:{{item}}
</li>
<li *ngFor="let item of map | mapToIterable"> <!--对于key value的map进行for循环遍历-->
{{item.key}}:{{item.value}}
</li>
</ul>

// switch语句
<div [ngSwitch]="myvalue">
<div *ngSwitchCase="'aaa'">
...
</div>
<div *ngSwitchCase="'bbb'">
...
</div>
<div *ngSwitchDefault>
...
</div>
</div>

// ngShow和ngHide在angular 2+已经不支持了,可以直接这样做
[hidden]="myVar"

get方法/computed方法

  • 类似于vuejs中的computed
1
2
3
get 字段名() {
return this.firstname + ' ' + this.lastname;
}

表单Form

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// js/ts文件
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';

export class MyComponent implements OnInit {
myForm: FormGroup;

constructor(private formBuilder: FormBuilder);

ngOnInit(): void {
this.myForm = this.formBuilder.group({
formFieldName: ['初始值', [Validators.required, this.checkName()]], // 第一个参数设置初始值,第二个参数是验证方法列表,注意如果有多个validator,后面的一定要用中括号包起来,否则会报错Expected validator to return Promise or Observable
字段2: ['', []],
字段3: new FormControl('', {
validators: [
this.aaaaaa.bind(this) // 自定义验证方法
],
updateOn: 'blur' // 失去焦点的时候进行验证
}),
字段4: [{value: '初始值', disabled: true}] // 如果要让某个字段disabled需要在这里做,直接在html上面disable可能不生效
}, {
validator: this.checkAll // 如果不是针对某个字段,而是针对整个表单,比如同时验证多个字段,那么可以在这里做
})
}

this.checkName(): any {
return (control: AbstractControl): { [key: string]: boolean } | null => {
return control.value >= 0 && control.value <= 2 ? null : {nameValueError: true}; // 如果出错可以返回一个key-value
};
}

this.checkAll(formGroup: FormGroup): any {
return (formGroup.value.formName !== 'new') ? null : {typeEmpty: true};
}

onSubmit(): void {
this.submitting = true;
this.myForm.get('field1').setValue(value); // 手动设置form表单字段的额值
if (this.myForm.valid) {
console.log('its ok');
}
}
}

// html中这样使用
<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
<div class="form-group">
<label>Name</label>
<input type="text" class="form-control" (input)="inputChange" formControlName="formFieldName" [(ngModel)]="user.name">
<p class="form-warning" *ngIf="submitting && createForm.get('formName').errors">
<span *ngIf="createForm.get('formName').errors.nameValueError"> // 这是上面自定义的错误
Name Should be 1 or 2.
</span>
</p>
</div>
<div class="mat-form-field"> // 注意表单级别的校验error,不能写在field下面,后者不会显示出来,mmp
<mat-error class="form-errors" *ngIf="formGroup.hasError('wrongDate')" i18n
>The end date should be after the start date.
</mat-error>
</div>
<button type=submit">Submit</button>
</form>

filter过滤器

1
{{ timestamp * 1000 | date: 'yyyy-MM-dd'}} // 时间格式化

样式

1
2
3
4
// 如果要覆盖第三方组件的样式,可以用::ng-deep,并且为了防止把其他组件也覆盖了,可以加:host前缀将样式覆盖限制在当前的宿主元素上面去
:host ::ng-deep .xxx {

}

组件通信

父组件至子组件通信

  • 直接用@Input
1
2
3
4
5
<app-child [field]="value"></app-child>

export class ChildComponent {
@Input() field: any; // 根据我的测试,子组件可能无法在contructor或者onInit中获取到这个值,因为这个值可能是动态的,所以最好在子组件创建一个get XXX()方法来获取变化后的值
}

子组件至父组件通信

  • @Output EventEmitter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<app-child (field)="onChildClick($event)"></app-child>

export class ParentComponent {
onChildClick(field) {
console.log(field);
}
}

export class ChildComponent {
@Output() field = new EventEmitter<String>();

onClick() {
this.field.emit('click');
}
}
  • @ViewChild不仅能获取子组件的字段,还能直接使用子组件的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<app-child></app-child>

export class ParentComponent {
@ViewChild(ChildComponent)
private childComponent: ChildComponent

onTest () {
this.childComponent.onTest1();
}
}

export class ChildComponent {
onTest1 () {}
}

不相关的组件通信

  • 创建service来通信,复杂的应用场景这个还是用得比较多
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 需要先找个地方新建一个service
@Injectable()
export class MyFieldService {
private myField: Subject<string> = new Subject<string>();

setMessage(value: string) {
this.myField.next(value)
}

getMessage() {
return this.myField.asObservable()
}
}

export class Component1 {
constructor(private myFieldService: MyFieldService)

onFieldChange() {
this.myFieldService.setMessage('new value');
}
}

export class Component2 {
constructor(private myFieldService: MyFieldService) {
// 需要特别注意的是,如果回调函数报错了,之后就不会监听了,造成了只能监听一次的假象
this.myFieldService.getMessage().subscribe((value) => {
...
}
}
}

生命周期

依次是

  • ngOnChanges(需implements OnChanges): 当设置或重新设置数据绑定的输入属性时响应,但是当组件没有输入,或者使用它时没有提供任何输入,那么框架就不会调用ngOnChanges()
  • ngOnInit(需implements OnInit)
  • ngDoCheck
  • ngAfterContentInit()
  • ngAfterContentChecked()
  • ngAfterViewInit(需implements AfterViewInit): 当初始化完组件视图以及子视图或包含该指令的视图之后调用,只会调用一次
  • ngAfterViewChecked
  • ngOnDestroy

扩展

  • @angular/flex-layout: angular的flex布局组件,能够很方便地实现flex响应式布局

    1
    <div fxLayout="row" fxLayoutAlign="space-between"></div>

事件

Angular1里元素绑定点击事件用ng-click,但是Angular2里元素绑定点击事件用(click),例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// click事件
<button (click)="toggleImage()">

// input事件是指输入的时候
// change事件是指内容改变以后(离开焦点)
<input (input)="onInput()" (change)="onChange()"
(keyup)="onKeyUp(event)" // 键盘输入事件event.target.value可以获取input的value
>

<!-- select元素点击获取选择的值 -->
<select (change)="onChange($event.target.value)">
<option *ngFor="let item of devices | keyvalue" value="{{ item.key }}">{{ item.value }}</option> <!--keyvalue过滤器将字典转换为key value对象的形式-->
</select>

// keydown事件指定键,例如按下回车
<input (keydown.enter)="" />

网络请求

  • angularjs的网络操作由HttpClient服务提供,在4.3.x开始使用HttpClient代替Http
  • angular的http请求返回的是一个Observable(可观察对象),在被消费者subscribe(订阅)之前,不会被执行。subscribe函数返回一个subscription对象,里面有一个unsubscribe函数,可以随时拒绝消息的接收
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
constructor(private http: HttpClient) {}
ngOnInit(): void {
// 必须使用subscribe才会真的去发送请求。每次调用subscribe可以发送一次请求,也就算是说要发送多个请求,直接在最后那subscribe就可以了。

this.http.get('/').subscribe(
data => {},
error => {
error.json // 获取json格式的错误相应
} // catch error
);
this.http.post('', body, {}, {params: new HttpParams().set('id', 3')}); // 添加url参数
this.http.post('', body).subscribe(...); // post请求
this.http.post('', body, {headers: new HttpHeaders().set('Authorization', 'my-auth-token')}); // 设置请求头
this.http.get('').subscribe(
data => {}
err => {'错误处理'}
);
this.http.get('').retry(3).subscribe(...); // 设置重试次数
this.http.get(''). {responseType: 'text'}.subscribe(...); // 请求非json数据

// 设置自定义的超时时间
import { timeout, catchError } from 'rxjs/operators';
import { of } from 'rxjs/observable/of';

this.http.get('').pipe(timeout(2000), catchError(e => {
return of(null); // 需要注意的是,这里的of的参数会传递给subscribe的res作为返回值
})).subscribe((res) => {});

await this.http.get('').toPromise(); // 将网络请求转换为promise就可以用promise的await语法了

// 如果一个函数需要返回一个Observable对象,但是又根据条件来进行http请求,条件满足直接返回结果可以用of来封装一下
if ([condition]) {
return of('result');
} else {
return this.http.get('');
}
}

httpclient全局error handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 新建一个http-interceptor.ts文件,或者其他名字都可
import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse, HTTP_INTERCEPTORS } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { _throw } from 'rxjs/observable/throw';
import 'rxjs/add/operator/catch';

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req)
.catch(errorResponse => {
if (errorResponse.error && errorResponse.error.msg) {
...
}

throw errorResponse;
});
}
}

export const ErrorInterceptorProvider = {
provide: HTTP_INTERCEPTORS,
useClass: ErrorInterceptor,
multi: true,
};

// 然后在app.module.ts中声明这个provider即可
@NgModule({
providers: [
ErrorInterceptor
]
})

文件上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<input #photoUpload type="file" accept="image/*" (change)="onInput($event)">
<button class="primaryButton" (click)="uploadImage()">Upload Image</button>

export class MyComponent {
@ViewChild('photoUpload') adminPhotoUpload: ElementRef;

uploadImage(): {
const files = this.photoUpload.nativeElement.files;
const formData: FormData = new FormData();
formData.append('file', file, file.name);

const headers = new HttpHeaders();
headers.append('Content-Type', 'multipart/form-data');
headers.append('Accept', 'application/json');

return this.http.post(`${apiURL}/api/storage`, formData, {
headers
});
}

// 或者这样做
onInput(event): {
this.file = event.target.files[0];
}
}

单元测试

所有的单元测试文件均以.spec.ts结尾,该文件具体语法规则如下:

1
2
3
4
describe('test haofly"s function', () =>{
it('true is true', () => expect(true).toEqual(true));
it('null is true', () => exect(null).not.toEqual(true));
});

推荐扩展包

ngx-dropzone

  • 拖拽上传文件组件

ngx-socket-io

  • Socket-io扩展
  • 有一个问题是该第三方包现在是支持extraHeaders的(支持自定义header传入后端),但是却没有发布到npm仓库,参考这个issue,下面有人提出解决办法,参考这里,但是登录的时候还没有token,所以最好是在组件的init里面自己new一个Socket对象吧

TroubleShooting

  • Cannot read property ‘stringify’ of undefined: 在模板中无法直接使用JSON等原生对象,可以在constructor()中传入:

    1
    2
    3
    public constructor() {
    this.JSON = JSON;
    }
  • can’t bind to ‘ngSwitchWhen’ since it isn’t a known property of ‘template’: ngSwitchWhen已经被ngSwitchCase替代了

  • **can’t bind to ‘ngModel’ since it isn’t a known property of ‘input’: ** 尝试将FormsModule添加到@NgModuleimports

  • ng: command not found: npm install -g @angular/cli@latest

  • URLSearchParams is not a constructor: 通常是因为引用URLSearchParams是通过import { URLSearchParams } from "url"引入的,但其实它早就内置于nodejs中了,可以不用写import语句直接用就可以了

  • 相同路由改变query params页面不跳转: 这是和很多单页框架一样的特性,这个时候可以用window.location.search进行页面刷新或者通过监听请求参数的变化来重新获取数据,例如:

    1
    2
    3
    4
    5
    ngOnInit() {
    this.route.params.subscribe(params => {
    this.service.get(params).then(...);
    }
    }
  • **ExpressionChangedAfterItHasBeenCheckedErrord: Expression has changed after it was checked.**:这是因为在子组件里面直接改变了父组件的值,通常是在ngAfterViewInit或者ngOnChanges中,因为这种改变可能会导致无限循环,所以是禁止的,但是如果确保不会发生无限循环,可以将改变的语句写到setTimeout中去

  • 给用代码生成的元素绑定事件/addEventListener需要使用.bind方法才能在回调函数内部使用this:

    1
    2
    3
    4
    5
    6
    7
    ngAfterViewInit() {
    document.querySelector('my-element').addEventListener('click', this.onClick.bind(data, this));
    }

    onClick(data, event) {

    }

扩展

现在是2016年11月22日,毕业大约5个月,外面飘着小雨,突然想起以前好像每次都喜欢在这样的场景下看着书思考人生呢。细想毕业的这几个月,感觉我的人生是不是走得太快了。
原以为,毕业以后,至少能每周都找个地方玩儿吧,周末约上三五个好友,踏踏青,赏赏水,坐坐长江的邮轮,顺便回南山看看母校的樱花。原以为,毕业以后,至少能挥金如土吧,刚签约的时候感觉自己的工资和同学比起来虽然不高,但是相比于大学每个月的生活费,那可不止三四倍呀,是不是很快就可以过上想买就买的日子了呢。原以为,毕业以后,至少能经常给家人朋友买点礼物吧,每个人生日的时候我都能当面送上一份特别的礼物,给他们来个惊喜。
但是,生活在这短短的时间里给我泼了好几大盆的冷水。
虽然每天朝九晚六,可以睡到八点半再起床,但是每天还是不够睡。即使是运气好不用加班,六点就能下班,也实在不想拖着疲惫的身体去找好朋友逛吃逛吃。看似每天工作只有7.5小时,实际上,剩下的时间都只能用于恢复精力。只想在家躺着,哪儿也不去,能够躺在舒适的床上才是我最大的希冀。周末?哪里来的周末。周末得坐长途车去看女朋友,女朋友比我更累吧,周末都得上班,所以周末必须好好陪陪她,陪伴的形式就是陪她上班。没有踏青,没有赏水,只有偶尔互相之间温柔的眼神。那是我每周长途跋涉唯一的安慰。
是谁发明的挥金如土这个词,难道不应该是挥金然后吃土吗。白领的意思就是每个月发了工资还了信用卡然后惊讶的发现,我靠,这个月工资又白领了。总有那么多花钱的地方,房租、水、电、气、长途车费、吃,基本上每个月会有1/3的钱会花在能让我活下去的这些需求上。接下来的1/3,偶尔吃个大餐,偶尔买个礼物,偶尔送个红包,基本上也花出去了,再剩下的1/3可以存下来,然后隔一两个月,心血来潮想买一个大件,然后,就没有然后了。买不起礼物,记住了每个朋友的生日,但是朋友生日的时候顶多说一声生日快乐,朋友结婚或者生孩子,有时候连“赞”都不敢点。
有位室友遇到公司裁员,丢了工作。对,就是那个我认为他是我大学见过最聪明的一位室友,居然被公司开除了,我完全不敢相信。听到这个消息的那天,我情绪很激动,想立马给他找到一份工作,但是问了好几家,基本上对工龄的要求都至少是两年。这可是一位刚毕业的本科大学生啊,哪儿来两年的工作经验呢。还好,写这篇文章的时候,他应该已经过了某个公司的的面试了,不过,那个地方,真的好偏远。
室友被离职的同一天,我另一个朋友也打电话来了,说他准备报一个培训班,从零开始学开发,问我要一些建议。这位朋友是我从小玩儿到大的,碰巧连所学的专业也相近,都跟计算机有关,不过我偏软件,他便硬件。毕业后他去了格力,结果发现里面有好多不可描述的内部危机,所以干脆直接回家乡发展,但是,同样的,也是一个工作经验连半年都没有的人,甚至他连基本的技能也差好大一截,所以不得不去报培训班。我一直觉得他比我努力好多倍,但总感觉他的运气总是不好,总是会在人生面对重大决策的时候或者人生将要迎来更加光明的时候生活给他泼一盆冷水。
更甚的,在前面的文章中有提到,我的妹妹去世了,一个二十出头的女孩子,刚当上妈妈就离开了这个原本美丽的世界。
相比于他们,或许,我算是很幸运的了。毕业以后找了一份“相对”稳定的工作。只能说相对,这两年是IT业的寒冬,谁也说不准明天会怎样,唯一知道的是如果要裁员,多半是拿新人开刀。另外,我还在重庆买了房子,走在了百分之九十几的同学前面了。贷款50万,分30年还清,利息大概30万。为什么那么急着买房子?说简单点,一是怕房价继续上涨(从去年的趋势看,基本上我要是提前一年买房,我可以少奋斗一年,这一年还不吃不喝才能补上那个差价),二是害怕突如其来的婚姻,不是不想结婚,只是还没准备好,不过,由于某些原因,我还是选择提前准备一下,我不想我的孩子一生下来,就呆在别人的房子里。最近,公司开始从PHP转Java了,动作挺大的,而我,确是一个PHP程序员,看样子我不得不去学曾经最讨厌,黑得最多的一门垃圾语言了。
我知道,当我们进入社会后,会面临各种各样的压力,各种各样的困难,各种各样的挫折。这些我真的都知道,只是,真的没想到,这一切会来得那么快,以迅雷不及掩耳之势一棒子打在刚脱离“青春”的我们头上,把我们打得晕头转向,不知所措。长夜漫漫,夜晚越来越睡不着了,想好多事,憧憬一下未来的样子,反正,就是不敢想现在,害怕发现现在的自己正在远离曾经的梦想。

先有Mac还是先有钱?我不知道有多少人是因为池老师的一句话而买的苹果电脑,反正,我买苹果电脑的很大一部分原因是因为池老师的影响,说池老师是苹果亚太区总布道师不足为过。

之前看过池老师的《人生元编程》(读后感),没想到半年不到我就拜读了池老师的第二本书了。看上一本书的时候我的电脑还没满一年,看这本书时,已经换了一个电源插座了(500多,心里十分痛)。不过,让我有点小小失望的是,池老师自诩为一个作家,但是读完他的第二部作品,我发现,他的作品并不能称之为一部经典的著作。可能是我看的书越来越多了,像这集合各种网文的书籍有了一点反感,不过还好,池老师的作品并没有那么“鸡汤”性质,干货还是满满的,只是这个价钱,稍贵。

池老师看起来已经老了,这本书读起来有点像他的归山之作,看样子,他可能要把他最后的青春献给锤子了。虽然我一直不看好锤子,但是他们认真做事的态度着实让我佩服,真的找不到第三家像他们那样在自己的产品上投入那么多感情的手机厂商了。

这本书讲述了池老师在五个方面的见解:写给走在编程路上的人、文艺中年、自省、跨越、人物,写出了他这么多年经历中的各种感悟,读来让人回味无穷。原来我们确实只是普通人,原来成功的人确实做了很多我们常人做不到的事情。我也有运营者一个公众号,也有自己业余的小项目,也有很多很多的idea,但总是因为下班后的各种琐事而推迟,渐渐地被遗忘,最后变成了岁月中的一抹尘埃,随着年华被侵蚀。此书更多的是纠正了我在编程领域的一些错误的思想,甚至是最近经常在自己周围发生的。

池老师,加油,希望有一天我也能站在你那样的高度。

语录

全世界都放弃,至少还有我在聆听。——《他居然是这种人》

以最小的代价解决问题!知行合一,方可无敌于天下。——《程序员真正的价值》

我不是懂得多,我只是学得快而已。——《程序员如何选择技术方向》

年轻的时候谁能不犯错?重要的是犯错之后你做了什么。强大了,还是沉沦了。
无论犯什么错,永远不要执行:sudo rm -rf /
无论如何,最好不要犯全天下男人都会犯的错。
——《程序员犯过的错误》

只有疯狂到自以为能够改变世界的人,才能真正地改变世界。——《把时间“浪费”在美好的事物上》

老树终归会老去,朝花有一天会盛开,每个人都有自己的时代,自己的路自己走,走不动了,就是终点。——《朝花与老树》

你本布衣,只要躬耕于南阳,终能指点江山争天下。——《旅途中的思考》

无论世界多么浮躁,总会有人潜于浮华之下,在深水河中静静地打磨那些精美的鹅卵石和珍珠,追逐自己的梦想。无论在哪个时代,这样的人才是推动世界向前的力量。——《年龄的故事》

王小波用他短暂的生命给时间留下了丰厚的遗产。有人欣赏他杂文的讥诮反讽,有人享受他小说的天马行空,有人赞扬他激情浪漫,有人仰慕他特立独行。在这些表象的背后,他一生最珍贵的东西,是对自由的追求。——《我的阅读之路》

Write the code, change the world.——《专访:谈技术、成长及锤子》

不管做什么,从年轻的时候,你就要对你做的事情有深入的了解,不肤浅,不浮躁,坚持去做一件事情,同时有意识地去提升自己的能力。——《专访:谈技术、成长及锤子》

世界上优秀的人本来就是少数,认识到这一点,你会更容易理解这个世界。——《专访:谈技术、成长及锤子》

有的鸟儿注定是无法被困住的,因为它们的予以是如此的流光溢彩。——《希望可能意味着一切》

人的一辈子活满了,最多也就3万多天,我们不可能从开始就知道结局,也不可能像朝阳升起和夕阳西下那样精准地规划自己的每一天,未知才是最美好的,所以,最初的选择可能没有想象的重要,在路上,才是最重要的。——《你是牛儿我是渣》

人类所能想象到的所有美好和黑暗,都能从书里找到。——《我在大学里学到的几件事》

A reader lives a thousand lives before he dies. The man who never reads lives only one. ——《我在大学里学到的几件事》

我对生命的意义有种理论。我们可以在第一章里对读者解释生命的意义何在。这样可以吸引住他们。一旦他们被吸引住,并且付钱买了书,剩下的章节里我们就可以胡扯了。——《你需要多久才能变成一个“傻瓜”》

如果你工作时,经常在“能不能行呢”的不安感和“啊,成功了”的成就感之间往复行走,那么恭喜你,离成功已经没有几公里了。——《你需要多久才能变成一个“傻瓜”》

既然已经知道了无论怎么努力都很难提前完成,还不如踏踏实实等待截止日期的降临,具体的做法大致如此。
1.确定截止日期和最晚的开始时间。如11月30日要提交演讲稿,那最好11月25日开始动手准备。
2.25日之前不要开始真正的准备工作,踏下心来忙其他事情,但是要留出点时间来做相关的思考和阅读,有好的创意或想法,记下来。
3.不要焦虑,但是告诉自己的大脑,月底还有这么一件事情在等你,别忘了。
4.一旦进入预定的启动时间,立刻开足马力工作,截止日期带来的恐惧和焦虑会帮助你迅速进入“深度优先处理”的状态,之前的阅读、思考和想法会让你专注、果断、创意十足,也许你真的能在这个时间段提前完成既定目标。
——《如何克服焦虑——深度优先处理》

年轻的时候常常会产生一种错觉,无论看长河落日,望滚滚常见,还是在史书中阅尽人世沧桑,人事渺小,你还是感到,日子会这样一天天长长久久地过下去。——《40岁了,还有没有路走》

如果你忙的不可开交,除了提醒自己要时不时地抬头看路之外,真的没什么可抱怨的,这是好事,生活的本质就是让自己忙碌起来,然后把一件事情做成,再把另一件事情做成。真正该抱怨的是那些处于舒适区的、闲散的人们。我清楚地记得自己每一次处于恐慌和焦虑的时候,都是因为在某个领域处于舒适区太久,缺乏挑战,生活闲适造成的,每一次改变也是由此开始。——《闲适有毒》

1.基于工具和平台提供服务。企业愿意要什么软件,就用这些工具和平台做什么软件,费用按人月结算,算是实现了真正的人月神话。遇到外包项目或工具做不了的项目,直接拉黑。
2.做垂直小软件,解决某行业80%中小企业客户的80%的需求,价格低,走量。
3.为企业软件公司提供互联网和数据服务,如云存储、项目管理、信息流协作、数据服务等,在免费和收费之间兜兜转转,也有生存之道。
——《淤出来的聪明之企业软件》

垃圾框架,用了Laravel过后感觉其他的PHP框架都是垃圾。但是由于生活所迫,不得不研究一下ThinkPHP框架了,哎。

配置

数据库配置放在database.php

1
THINK_VERSION = '3.2.2'	# thinkphp版本

帮助函数

1
2
3
4
$this->redirect('')	# 重定向
Log::record(''); # 记录日志信息到内存
Log::save(''); # 手动将保存在内存中的日志信息写入日志
Log::write(''); # 实时写入一条日志信息,不受配置的允许日志级别影响,可以实时写入任意级别的日志信息

数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 定义model对象
$model = M('Product')

# 直接执行sql语句
$Model = new Model()
$Model->query(sql)

# 获取上一次执行的sql语句
M()->getLastSql();

# 查询数据
$model->select() # findall功能
$model->where('name="haofly"')->find() # 查找满足条件的第一条数据
$model->where('name="haofly"')->select()# 查找满足条件的所有数据

# 创建数据
$model->create(['name' => 'test']);

# 更新数据
$model->where('id=5')->save(['name' => 'test']); // 根据条件更新记录
$model->name = 'test' && $model->where('id=5')->save(); // 使用对象的方式来操作

# 删除数据
$model->where('id=5')->delete()

模板引擎

1
2
3
4
5
6
7
# volist: 用于在模版中循环输出数据集
# 首先有$this->assign('list, $list)
# 然后在模版中这样写循环
<volist name="list" id="vo">
{$vo['name']}<br>
{$vo['pass']}<br>
</volist>

此时此刻,我正在市二院里写着文字,我不知道这个医院的太平间在哪儿,但是想起了前不久我那去世了的妹妹。

妹妹与June的中文音同,不是我的亲妹妹,只是在上学的时候认识的一个好女生。她爸爸是学医的,所以她很会照顾人,人也很温柔,很善良。那时候,每天中午睡午觉,她都会主动给我一床被子给我盖好,每次我有任何健康方面的问题都会问她。我也会经常给她买糖葫芦,给她买好吃的,我知道这不是爱情,就像哥哥与妹妹的感情一样,所以,自然而然我们就成了兄妹了。高中的时候,我们很幸运能在同一所学校,虽然没有在同一个班级,但是,那三年,我们依然在一起照顾对方。那时候,我谈恋爱了,不知道她有没有,但是她总是会说班上有人在追求她,我好担心,担心我这么善良的妹妹会被其他男人骗,担心她不会处理感情中遇到的事。虽然那时候我也不太懂怎么谈恋爱,但身为哥哥的我自然有强烈的保护欲望。我们还认识了一些对方的朋友,那时候真的好开心,那时候周末放半天出去和女朋友买奶茶也会单独给她带一杯。

可是后来,毕业了。毕业以后,我就很少和他们联系了,可能,更多的,是我变了。我不知道她在哪里上学,不知道她有没有找到男朋友,连她在哪儿都不知道。只是在后来的几次电话中得知她已经找到男朋友了,并且,男朋友的老家在东北,几千公里呀。我不知道她那些日子都经历了什么,我不知道为什么她那固执的爸爸会让她一个人嫁到那么遥远的地方,只知道后来的后来,她突然结婚了,她说是“奉子成婚”。我由衷地祝福她,只是在心里却很难受。一个人在那么远的地方,如果娘家人对她不好,可怎么办呀。我没有说出来,只是叫她多看看书,因为我知道,无论一个人在什么困境,只要还会看书,至少心里面会知道什么叫做甜。再后来,只是听她说她生了一个女孩子,然后过了不到一年居然又怀孕了。她说通常是她一个人在家里,老公在外面打工,娘家人在不多远,只是没有过来照顾她。再后来,就是听到她的死讯了。那之前她还发了说说,心情不错,应该是刚生了第二个孩子。据说是在散步的时候失足掉下去的,也有人说是娘家人看她生了两个女孩而不是男孩而产后抑郁的,甚至有人说是谋杀。至于到底什么是真想,我已经不想去深究了。我的妹妹已经走了,再多的真想也换不来她的重生,只希望,那么善良的她,现在已经到了天堂,静静地看着我们,祝福着我们。

我觉得我十分对不起我的妹妹,在那边她一个人的时候,我没有经常去陪她,她只能靠和远在故乡的父亲通通电话,而我,作为一个同龄人,却没有给她带来任何的安慰。这是我一个哥哥的失职。大概有两次吧,她回到家乡,我都说要去看她,但是因为这样或那样的事情,最后都拖到她的再次离开。那几晚,我挨着给我的姐姐妹妹以及那些曾经说过“下次见面一起吃个饭”的朋友都问候了一遍,还好,他们在电话或者QQ里,心情仿佛都还不错。我不希望还会有人从我身边离开,但,哪怕是离开,我也希望能看到它们现在是快快乐乐的。

妹妹,哥哥不知道几十年后还会不会记起你,希望你不要把我怪罪。再见了,妹妹。我已经好几年没吃糖葫芦了,以后可能也不会吃了,我好想下一串糖葫芦,依然是买给你的。

昨天,终于完成了人生中的一件大事——买房。以前觉得买房,看着好就买,应该不是一件麻烦的事情,但通过最近几个月的看房经历来看,其麻烦程度完全超乎了我的想象,甚至给我带来了极大的心理压力。我不知道现在买房是不是好时机,但是我愿意以后花更多的努力来将损失降到最低,来让房子带给我的是更多的幸福感,而不是一辈子的压力。这两天想了很多很多,或许也是我的一次比较大的成长。

最累不过买房

从我毕业开始,父母就开始问我什么时候买房,我知道他们一直都尊重我的意见,我也觉得不用那么早买房,毕竟我和女朋友结婚,可能要等到下一个猴年马月了。但是,今年的重庆房价仿佛一下子变成了网红,国庆前后,当20个城市开启“限购模式”后,仿佛所有人都把焦点投向了重庆,质疑这座新一线城市为何房价迟迟配不上自己的地位。作为一个土生土长的重庆人,我由衷地感谢市政府的能力,也深刻的意识到重庆的房价,可能不会再那么平静了。我和我的父母,以及周围的好多亲戚,都认为,重庆的房价肯定会继续涨,虽然没外地涨得快,但涨价是肯定的。当然,按照往常的规律来说,涨价一般发生在四月份和九十月份。于是我们就决定慢慢看。但是由于我对房子要求并不高,每看一套房子就觉得喜欢,父母就得周末专程从老家跑到城里来看。上周末一连看了两天,每天看个三四套,耳边一直是中介的各种吹嘘,简直筋疲力竭,我也不想再看了,于是匆忙之中选了一套。额,最终我买了最偏远的一套,远离喧嚣,旁边有公园,交通还算方便,我觉得,那才是我理想中的房子。

最愧不过父母

确定买房的那个夜晚,我侧夜难眠,自小时候得的一场重大疾病过后,我就从来没有一下子向父母要过这么多的钱。这让我感到很大的心里压力,我也知道这套房几乎会花光家里所有的积蓄,但是,面对突如其来的房价泡沫,我也不得不硬着头皮下手,哪怕我心里也觉得这个泡沫可能会破灭。本来还说这周末带父母去做一次全身体检,结果没想到,反而让他们这般劳累。没想到,即使是毕业了,也没能让他们过上舒服的生活,现在的我,只希望从装修完了开始不再让他们花哪怕一分钱。不花父母钱,这个日子我已经期待了那么久,也延迟了那么久,希望这次不会再延迟。趁着父母还健健康康的,多让他们享福,最不想听到子欲养而亲不在。

他们没有给我最好的,但给了我他们所有的。

最傻不过曾经

果然,到目前为止,还是有些事情我无法控制。从大学开始,我就努力地提高自己,以使得自己能够在面临两难选择的时候能够轻松应对,比如大二分专业,我已经锻炼到无论哪一个专业我都能应对自如,比如毕业后选工作,我也自认为锻炼到无论哪一个公司哪一个地方,我都能继续成长、持续学习。但是,当面临买房的时候,我软弱了。没有足够的知识去确定哪个房子最好,也没有足够的面对未来的信心,这一次,我没有了当年的豪言壮语,多了几分面对现实的无奈。

还记得曾经的曾经,我们傲气冲天,敢说出“上大学以后不再要父母一分钱”,敢说出“买房完全靠自己”,敢说出“毕业的时候开自己买的车出去”这样的话,但是后来,多少次凝望西边的天空,心里无不充满遗憾,如今的我,依然能说出这样的话,只是已没有了当年的底气,潜意识里否定了这样的话,否定了现在的自己。

是啊,没想到,成长为了曾经讨厌的人。成长怎么会是这样的,难道成长不是一次又一次的实现曾经许下的梦想的过程吗?

最美不过将来

幸运的是,买房确实给我带来了极大的安全感。我曾经以为,女生眼里的安全感就是男生要保护好女生不受伤害,但后来又觉得是感受到自己的男朋友不会离开自己,再后来我又觉得是看到男人的努力觉得以后会过上好日子。女人我永远不懂,但是,买房让我体验到了什么是我的安全感。不用再担心房价是上涨还是下跌,不用再每天去看哪个楼盘更好。买房的第二天,我心里就放轻松了许多。既然买了,就不要后悔。接下来,我要和爸爸妈妈还有我的女朋友一起规划新家的样子。这套房子离我上班的地方很近,但是离女朋友上班的地方很远,所以,希望明年能买辆车,当然,依然只能贷款😂。以后,我们的宝宝会降临在这套房子里,可能要一直等到她上小学才会换房子。我是买了房,但是这样一个我每天居住的地方怎么能反过来把我奴役了,我会把它装扮得漂漂亮亮的,一起走过接下来的几年时光。不做房奴,美好的未来,才刚刚开始。

最好不过现在

我很幸福,从小到大,爸爸妈妈哥哥一直没有离开我,并且现在的女朋友居然是自己的小学同学,并且我身边所有最亲近的人都健康,都很幸福。我们挣的钱不多,但不至于每天为了钱焦头烂额;我们的学历不怎么高,但不会每天找无趣的事情;我们没有住在一起,但都在一个美丽的城市里。想回家看爸爸妈妈,长途车2小时,想去看女朋友,高铁2小时,基本上,随时都能找到理由去看看他们。我和爸爸性格一样,为了多陪陪亲人,宁愿不去外面挣大钱,就过过温馨的小日子,一辈子这样平静,多好呀。现在这么美好,为什么不珍惜呢,我还那么年轻,为什么不趁现在就努力呢?

此书采购于Amazon的0元限时购活动,同时,我表示很激动,终于能拜读期待已久的“初代程序员”王小波的作品了。第一次那么想了解王小波是在我读了《把时间当作朋友》过后,果然,不出半年,我就读到了王小波的作品。和《把时间当作朋友》一样,《人为什么活着》也是一本杂文集,署名只是取了其中某一篇的标题,当然是最富有吸引力的标题。

在我的关于王小波的种种映象里,他应该是一个,很正统,并且很有写鸡汤文潜质的那么一个伟大的作家,不过在阅读了这本书过后,我只想说,这TM是怎样一个有趣的人呀?他反对儒家思想,反对道德至上,以他独特的眼光反抗者外部的不解,哪怕他用脏话骂一个人我都觉得毫不做作,反而要拍手称绝,骂得有理有据,骂得头头是道,骂得真爽。和近现代的一些作家比起来,他的人生经历了这个国家太多的路程,但是作品却没有流露出太多的历史厚重感,没有那么多的包袱,他的文章读起来便有趣多了。看到不到十分之一的时候我最大的感受就是,这个作家,太接地气了。

也是通过这本书,我第一次,对孔孟时期的思想有了深一步的思考。求学的时候,我们只知道这些名家的思想中最出名的几句名言(这就像我每次看完一本书,下面都会摘了很多名言一样),却不了解他们的知识体系,体系必然有一个中心,然而,孔孟思想的中心,似乎都不太正确,或许只能说是政治正确。他们为我们当今的世界定了太多的规矩,其中,很大一部分是我们一直沿用的,只是,很少有人去问对不对,反正周围的人只是认为不按照那样做,便是不对的。

最最让我佩服的是,他一个经历过文革的作家,居然也是一名程序员,他对知识的涉猎程度完全超乎我的想象。首先,他是我知道的唯一一位经历过文革的还会写代码的人,其次他还是上个世纪的程序员,也就是说,电脑刚在国内发展起来的时候,他就已经开始使用电脑,并能自己编写软件解决自己的需求。放在今天,这完全就是一位极客了。肯定会有无数的粉丝。今天的程序员作家也不少,但是经历像他这么丰富的人,肯定是没有的。所以关上书的时候,我感慨了一句:经历丰富的人,写日记都像是在写诗。

最后,这是我女朋友最近和她母亲聊天的截图:

宝宝真的好庆幸,我有这么“不管我”的父母,让我从小能拥有一般农村孩子无法理解的自由,他们带给我的不仅仅是生活上的轻松,更是我一辈子的财富。也很高兴女朋友和我一样能有自由的思想,而不是唯父母命是从。

看了全书,我还是不知道人为什么活着,或许,好好玩儿,活在当下,这才是人生的答案。自由,哪怕仅仅是思维能够自由,那就足够了。

名言
  • 只希望你和我好,互不猜忌,也互不称誉,安如平日,你和我说话像对自己说话一样,我和你说话也像对自己说话一样。说吧,和我好吗?——《孤独的灵魂多么寂寞啊》
  • 我不喜欢稀里糊涂地过日子。我妈妈有时说:真奇怪啊,我们稀里糊涂地就过来了。他们真的是这样。我们的生活就是我们本身。我们本身不傻,也不斤斤计较大衣柜一头沉。干嘛要求我们有什么外在的样子,比方说,规规矩矩,和某些人一样等等。有时候我真想叉着腰骂:滚你的,什么样子!真的,我们的生活是一些给人看的仪式吗?或者叫人安分守己。不知什么叫“分”,假如人活到世上之前“分”都叫人安排好了,不如再死回去的好。——《人为什么活着》
  • 理性就像贞操,失去了就不会再有;只要碰上了开心的事,乐观还会回来的。——《积极的结论》
  • 我现在所教的数学,你们也许一生都用不到,但我还要教,因为这些知识是好的,应该让你们知道。——《跳出手掌心》
  • 在文学艺术及其他人文的领域之内,国人的确是在使用一种双重标准,那就是对外国人的作品,用艺术或科学的标准来审评;而对中国人的作品,则用道德的标准来审评。——《跳出手掌心》
  • 我最赞成罗素先生的一句话:”须知参差多台,乃是幸福的本源。”——《思维的乐趣》
  • 能够带来思想快乐的东西,只能是人类智慧之高的产物。——《思维的乐趣》
  • 假设有某君思想高尚,我是十分敬佩的;可是如果你因此想把我的脑子扔出来挖掉,换上他的,我决不肯,除非你能够证明我罪大恶极,死有余辜。人既然活着,就有权保证他思想的连续性,到死方休。更何况那些高尚和地下完全是以他们自己的立场来度量的,假如我全盘接受,无异于请那些善良的思想母鸡到我脑子里下蛋,而我总不肯相信,自己的脖子上方,原来是长了一座鸡窝。——《思维的乐趣》
  • 于是人人盛赞节烈妇女。翻开历史一看,女人味了节烈,割鼻子拉耳朵的都有。鼻子耳朵不比头发指甲,割了张不出来,而且人身上有此零件,必有用处,拿掉了肯定有不便处。若是为“节烈”之名而自杀,肯定是更加不妥的了。此类行为,就像那条抽疯的海豚。——《西方快乐观区别之我见》
  • 罗素在讨论伦理问题时曾经指出,人人都希求幸福。假如说,人得到自己希求的东西就是幸福,那就言之成理;倘若说因为某件事是幸福的,所以我们就希求它,那就是错误的。——《西方快乐观之我见》
  • 大多数人在受到重视之后,行为就会好。——《居住环境与尊严》
  • 从孔孟到如今,中国的哲学家从来不挑担、不推车,所以他们的智慧从不考虑降低肉体的痛苦,专门营造站着说话不腰疼的理论。——《人生的逆转》
  • 假如从整个人类来考虑问题,早就回发现,趋利避害,直截了当地解决实际问题最重要——说实话,中国人在这方面已经很不像样了——这不是什么哲学的思辨,而是我们的生活经验。我们的社会里,必须有改变物质生活的原动力,这样才能把未来的命脉握在自己的手里。——《人生的逆转》
  • 花钱不说,还下了很多功夫,现在用的软件都是我自己写的。——《从Internet说起》
  • 不知为什么,傻人道德上的敏感度总是很高,也许这纯属巧合。——《奸近杀》
  • 我既不是同性恋,也不想自杀,所以我对计算机没兴趣。得的全是三分。但我现在时常用得着它,所以还要买书看看,关心一下最新的进展,以免用时抓瞎。这是因为我写文章的软件时自己编的,别人编的软件我即使不惯,也信不过,就这么点原因。但就因为这点小原因,握在编程这件事上,还真正有点修为。由此可见,为研究某种学问这件事感兴趣和对这门学问本身感兴趣可以完全是两回事。——《苏东坡与东坡肉》
  • 有人告诉我说,没你这么写杂文的!杂文里应该有点典故,有点考证,有点文化气味。典故我知道一些,考证也会,但就是不肯这么些。——《有关天圆地方》
  • 罗素举这个例子是要说,人们的见识总要受处境的限制,这种限制既不知不觉,又牢不可破——这是一个极好的说明。——《有关天圆地方》
  • 很不幸的是,任何一种负面的生活都能产生很多乱七八糟的细节,使它变得蛮有趣的。人就在这种趣味中沉沦下去,从根本上忘记了这种生活需要改进。——《有关贫穷》
  • 古人说,食色性也。想爱和想吃都是人性的一部分,如果得不到,就称为人性的障碍。——《从<黄金时代>谈小说艺术》
  • 我们的生活有这么多的障碍,真他妈的有意思。——《从<黄金时代>谈小说艺术》
  • 我相信如果不能把已写出的每一根线索都记在心里,就不能写出好的结构;如果不能把写出的每一句话记在心里,就不能写出好的风格。——《<怀疑三部曲>序》
  • 当然,人有贤愚之分。但一个人认为思维是快乐的,那他就可说是热爱智慧的。——《<怀疑三部曲>序》
  • 我认为没有智慧、性爱而且没意思的生活不足取,但有些人却以为这样的生后就是一切。他们还说,假如有什么需要热爱,那就是这种生活里面的规矩——在我看来,这种生活态度简直是种怪癖。很不幸的事,有这种怪癖的人是很多的,有人甚至把这种怪癖叫做文化,甚至当做了生活本身。——《<怀疑三部曲>序》
  • 细读过《孟子》之后,我发现里面全是这样的一些想法。这世界上有很多书都是这样的:内容不可挑剔,只是很没有意思。除了显而易见的坏处,这种书还有一种害人之处就在于:有人从这些书中受到了鼓舞,把整个生活朝更没意思的方向推动。——《<怀疑三部曲>序》
  • 一切人间的荒唐事,整个社会的环境虽是一个原因,但不主要。主要的是:那个闹事的人是在借酒撒疯。——《肚子里的故事》

数据类型

  • print(e)打印变量
  • 在类型后面加上?表示可选项,由于可选项可能为nil,所以在使用的时候要先确保其有值,这时候需要使用!强行解包,例如number! * 10或者使用??避免空值(number ?? 0) * 10
基本变量使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let a = 1	// 声明常量
var b = 2 // 声明变量
var c: String // 声明变量类型

// 可选类型表示允许常量或者变量没有值,即nil,可选类型用?表明
var d: String? // 可选变量,会自动将其值设置为nil
var e: Int? = 404 // 可选变量,这样可以把nil赋值给该变量
println(e!) // 需要!来获取值
// 隐式解析可选类型,用!声明,表明强制要求该变量一定有值。一个隐式解析可选类型其实就是一个普通的可选类型,只是可以被当作非可选类型来使用,如果该变量没有值,那么去获取的时候就会报错
let f: String? = "test"
println(f) // 不需要!

// 三木运算符
?: // 和php一样吧

// 空合运算符
a ?? b // 如果a包含一个值则是a,否则就是b

// 闭区间运算符
for index in 1...5 {} // 返回一个a到b区间的所有的值
// 半开区间运算符
for index in 0..<5 {}
字符串
1
2
3
4
5
6
7
string.isEmpty    	// 判断字符串是否为空
count(string) // 求长度
let g = "\(var1) 呵呵" // 直接将变量的值插入字符串,用反斜线为前缀的括号中
string.hasPrefix("abc") // 判断前缀
string.hasSuffix("def") // 判断后缀
string.uppercaseString // 转换为大写
string.lowercaseString // 转换为小写
数组
1
2
3
4
5
6
7
8
9
10
11
12
13
var strList = ["A", "B"]	// 直接定义
var strList: [String] = ["A", "B"] // 定义
var strList = [Int](count: 10, repeatedValue: 0)// 定一个一个包含10个零的数组
var StrList = Array(count: 10, repeatedValue: 1)
strList.count // 数组长度
strList.isEmpty // 是否为零
strList += ["C"] // 可以直接加
strList[5...7] = ["E", "F"] // 能这样插入
strList.insert("G", atIndex: 0) // 也能这样插
strList.removeAtIndex(0) // 删除一个元素
strList.removeLast() // 删除最后一个元素
for item in strList {} // 数组遍历
for (index, value) in enumerate(strList) {} // 遍历的同时获取到当前索引
集合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var h = Set<Character>()	// 定义
h.insert("B") // 插入
h.count // 数量
h.isEmpty // 是否为空
h.remove("B") // 删除一个元素,返回值为该函数本身
h.contains("B") // 集合是否包含一个特定的值
for item in h {} // 遍历
for item in sorted(h) {} // 有序遍历
h.intersection(i) // 求两个集合的交集
h.symmetricDifference(b) // 求两个集合不同的
h.union(b) // 求两个集合的并集
h.subtracting(b) // 求在h集合但不在b集合的
h.isSubsetOf(b) // h是否为b的子集
h.isSupersetOf(b) // h是否为b的父集
h.isDisjointWith(b) // h和b是否完全不一样
字典
1
2
3
4
5
6
7
8
9
10
11
12
var z: [String:String] = ["A": "a", "B": "b"]
z.count // 字典元素数量
z.isEmpty // 是否为空
z["C"] = "c"// 添加值
z.updateValue("C", forKey:"C") // 更新值,返回老值
z["C"] = nil // 移除值
z.removeValueForKey("C") // 移除值
for (key, value) in z {} // 字典遍历
for key in z.keys {} // 遍历key
for value in z.values {} // 遍历value
let a = Array(z.keys)
let b = Array(z.values)
函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 简单定义,函数的参数默认是常量,如果在函数题中更改参数值居然会编译错误,如果要传入一个变量参数,可以在参数名前加var,加了var,就可以在函数内部进行修改,但修改后的值并不能影响函数外部该值的原来的值,如果真的要修改外部的,那么要将参数定义为inout,表示是输入输出参数
func sayHello(userName: String, second: String = "默认值") -> String {
let a = "B" + userName
return a
}

// 多返回值定义
func count(string: String) -> (a: Int, b: Int) {
return (a, b)
}

// 提供外部参数名的,在调用时必须提供外部的参数名
func function(externalParamterName localParameterName: int) {}
// 调用时
func(外部变量名: "abc")

// 好吧,这样就不用单独定义外部参数名了,外部参数名和内部参数名一样
func function(#string: String) {}

// 可变参数
func function(numbers: Double...) {}
结构体/类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// getter/setter
struct Point {
var x = 0.0, y = 0.0
}
struct Size {
var width = 0.0, height = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set(newCenter) {
origin.x = newCenter.x - (size.width / 2)
origin.y = newCenter.y - (size.height / 2)
}
}
}
var square = Rect(origin: Point(x: 0.0, y: 0.0),
size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
square.center = Point(x: 15.0, y: 15.0)

// 属性观察器,每次属性被设置的时候都会调用属性观察器,有两种
willSet: 在设置新的值之前调用
didSet: 在新的值呗设置之后调用
// 直接在定义结构体或类内部变量时使用
class Example {
var test: Int = 0 {
willSet(newValue) {
println("hehe")
}
didSet {
println("haha")
}
}
}

// 下标脚本
// 构造器
struct teset {
var a: Double
init() {
a = 12.0
}
}

参考实例

Prodigal-iOS: 音乐播放器

AMM: Aria2 Menubar Monitor