Author Archives: wuxiao7813

小内存 VPS Mysql error code 12

WordPress 跑一段时间后都会出现数据库连接错误,上去重启一下 mysql 就好了
今天终于忍不住了,上去看了一下 error log,最重要的一行

InnoDB: mmap(137363456 bytes) failed; errno 12

找到原因了,开始看怎么解决,VPS 只有 1G 内存,原来想着跑个 WordPress 还是绰绰有余的,没想到这么容易就挂了
加内存不太可能,本来就没什么人访问的地方,只是自己用来做个记录的,那就看 swap 了

开始操作,下面命令需要 root 权限
dd if=/dev/zero of=/swapfile bs=1M count=1024
这个命令就是生成一个 1G 的 swapfile 文件,用来做 swap
mkswap /swapfile
swapon /swapfile
运行完这个命令,虚拟内存就开启了
然后设置开机加载,不然下次重启就没有了
在 /etc/fstab 下写入配置:
/swapfile none swap defaults 0 0
解释一下这几个东西的意思,他对应的标签在下面

<file system> <mountpoint> <type> <options> <dump/pass>

<file system> 挂载设备,比如我们的 /swapfile

<mountpoint> 挂载点,告诉我们设备挂载到哪,虚拟内存不需要挂在另外的目录,就是 none

<type> 文件系统类型,我们的类型就是 swap

<options> 设置选项,默认 defaults,代表 rw,suid,dev,exec,auto,nouser和 async

<dump> 1 表示需要将 的内容备份,我们这个明显不需要,设置 0

<pass> 1 指定如何使用 fsck 来检查硬盘,0 则不检查,这个一般在 / 下写 1,如果不是 0,检查会按照数字从小到大检查,数字相同会同时检查

搞完后,可以用 free -m 看一眼是不是多出来了 swap 内存

搜索中参考的文章如下,感谢

ssh-and-bash-fork-cannot-allocate-memory-vps-ubuntu

给Linux新加或者修改Swap交换内存(Amazon EC2 )

搭建 SS

安装shadowsocks依赖

  1. sudo -s // 获取超级管理员权限
  2. apt-get update // 更新apt-get
  3. apt-get install python-pip // 安装python包管理工具pip
  4. pip install shadowsocks // 安装shadowsocks
  5. ssserver -c /etc/shadowsocks.json -d start // 启动shadowsocks

配置shadowsocks

vi /etc/shadowsocks.json

(ps. 可能有人不会用vi,这里简单说下,vi打开文件后,按i即可进入编辑状态,编辑完后,按esc退出编辑状态,按:进入命令状态,输入wq即可保存并退出,还不懂的话自行百度vi吧)

  1. 单一端口配置:
{
    "server":"0.0.0.0",
    "server_port":端口,
    "local_address":"127.0.0.1",
    "local_port":1080,
    "password":"连接密码",
    "timeout":300,
    "method":"aes-256-cfb",
    "fast_open":false
}
  1. 多端口配置:
{
    "server":"0.0.0.0",
    "port_password": {
        "端口1": "连接密码1",
        "端口2" : "连接密码2"
    },
    "timeout":300,
    "method":"aes-256-cfb",
    "fast_open": false
}

换了个 Blog 服务器

AWS 3 年合约到期了,想着简单一点继续用 AWS,然后开始了换服务器的过程,想着 AWS 应该比较简单可以备份恢复来解决,但折腾了好久发现现在的服务器和我当年的差别太大,无论 EBS 还是 IP 都搞不定,只好回到了手工备份数据下载,上传

折腾了 4,5 个小时,终于 run 起来了,太久没弄过了,好多东西都很生疏,还好数据没丢

下次再换又是 3 年后了,希望到时候能更熟练一点,或者能有个更好的解决方案

最近做的一件傻事

替换一个图片库,调研了一圈,看了别人很多代码,选了一个

然后开始陷入改 bug 的循环,自己写一个已经完成不知道多久了

有点傻,为什么花这么多时间去研究别人的,把精华提炼出来就好了

Swift 2.0 遭遇的坑

let a = OCObject.Value

但是这个 Value 可能是 nil

在 Swift 1.2 的时候,大部分的时候我们用的是 var ,可能还会判断一下,但 2.0 自动全部转成了 let

开眼(Eyepetizer)在升级 1.9.0 版本的时候升级到了 2.0,做了 iPad 适配,还做了其他很多事情,但我们并没有针对原来存在的很多 nil 情况做测试,导致升级后用户一直在反馈闪退,Crash

花了一点时间查看问题,找到了根源,还是我们做的不够好,写个文章,作为备忘

【转】Git-rebase 小筆記

原文写的太好了,转过来收藏

原文地址:http://blog.yorkxin.org/posts/2011/07/29/git-rebase

最近剛好有個機會整理很亂的 Git commit tree,終於搞懂了 rebase 的用法,筆記一下。

大家都知道 Git 有個特色就是 branch 開很大開不用錢,但很多 branches 各自開發,總要在適當時機 merge 進去 master 。看過很多 git 操作指南都告訴我們,可以妥善利用 rebase 來整理看似很亂或是中途可能不小心手滑 commit 錯的 commits ,甚至可以讓 merge 產生的線看起來比較簡單,不會有跨好幾十個 commits 的線。

rebase 的意義:重新定義參考基準

首先要提一下 rebase 的意思,我擅自的直譯是「重新 (re-)定義某個 branch 的參考基準(base)」。把這個意思先記起來,比較容易理解 rebase 的運作原理。就好比移花接木那樣(稼接),把某個樹枝接到別的樹枝。

在 git 中,每一個 commit 都可以長出 branch ,而 branch 的 base 就是它生長出來的 commit ,rebase 也就是把該 branch 所長出來的 commit 給改去另一個 commit 。不過,因為 rebase 會調整 commit 的先後關係,弄不好的話可能會把你正在操作的 branch 給搞爛,所以在做 rebase 之前,最好開一個 backup branch ,什麼時候出差錯的話,reset 回 backup 就行了。

以下用實際的例子來操作比較容易解釋。看 log 的程式是 GitX (L) 。

Update 2012/06/28:也可以看 ihower 的錄影示範 ,實際操作會比讀文字來得容易懂。

例如我要寫個網頁,列出課堂上的學生。我把樣式的設計 (style) 跟主幹 (master) 分開,檔案有 index.html 和 style.css 。

到目前為止有以下的 commit history:

style 完成了一小部份,而接下來要修飾的頁面是 master 裡面有改過的,如何讓 style可以繼承 master 呢?就是用 rebase 把 style branch 給接到 master 後面了,因為 rebase 是「重新定義基準點」。就像是在稼接時,把新枝的根給「接」在末梢上。

rebase 的基本指令是 git rebase <new base-commit> ,意思是說,把目前 checkout 出來的 branch 分支處改到新的 commit。而 commit 可以使用 branch 去指(被指中的 commit 就是該 branch 的 HEAD),所以現在要把 style 這個 branch 接到 master 的 HEAD (dc39a81e),就是在 style 這個 branch 執行

git rebase master

完成之後,圖變這樣:

果然順利接起來了。

而在執行的過程中會看到:

First, rewinding head to replay your work on top of it...
Applying: set body's font to helvetica
Applying: adjust page width and alignment

這是它的操作方式,照字面上的意思,就是它會嘗試把當前 branch 的 HEAD 給指到你指定的 commit (在這裡是原本 master 的 HEAD,也就是 dc39a81e),然後把每個原本在 style 上面的 commits (d242d00c..0b373e34) 給**重新 commit** 進去 style 這個 branch (re-apply commits)。也由於是「**重新 commit**」,所以 rebase 以後的 commit ID (SHA) 都不一樣。

那如果過程中有 conflict 呢?後文會提到。

fast-forwarding: 可以的話,直接改指標,不重新 commit

接著再開個新的 branch 叫 list ,專門改學生清單,同時另一個人也在改 style 這個 branch ,修飾網頁的整體裝飾。改啊改,變成這樣分叉的兩條線:

image

list 改到一個段落,沒有問題了,就想 merge 進 master 。在 master branch 做

git merge list

這時 git 發現,剛好 master 直接指到 list 的 HEAD commit 也行 ,所以 git 直接就改了 master 的 commit ID ,也就是所謂的 fast-forward,熟悉 C 語言的同學應該對這種指標移動不陌生。完成之後就是這樣:

image

rebase --onto: 指定要從哪裡開始接枝

list 繼續改,style 還是繼續改,變這樣:

image

現在 style 要開始裝飾學生清單了,而學生清單是 list 這個 branch 在改的。於是 style應該要 rebase 到 list ,可是這時管 list 的說,我後面幾個 commits 還沒敲定,你先拿 64a00b7e (add their ages) 這個 commit 當基準,這我改好了。所以這時候,應該要把 style 這個 branch 接到 64a00b7e 的後面。

該怎麼辦呢?這時就要用 git rebase --onto 了。指令是

git rebase --onto <new base-commit> <current base-commit>

意思是說,把當前 checkout 出來的 branch 從 <current base-commit> 移到 <new base-commit> 上面 ,就像是在稼接時,把新枝的根給「種」在某個點上,而不是接在末梢。(這似乎也是稼接最常用的方式?有請懂園藝同學的指教一下)

再看一下 commit history:

image

現在 style 是 based on dc39a81e (add some students),要改成 based on 64a00b7e (add their ages),也就是

  • <current base-commit> = dc39a81e
  • <new base-commit> = 64a00b7e

那就來試試看

git rebase --onto 64a00b7e dc39a81e

image

果然達到了目的,style 現在是 based on 64a00b7e 了(當然 commit IDs 也都不同了)。

conflict 的處理

接著改 style 的人修改了學生清單的樣式,可是他很機車,他要改 index.html 裡面的東西(實際情況是,list 裡寫了一個 table,但寫 css 總要有些 class 或 id 的 attributes 才能設定)。剛好改 list 的人也在他自己的 branch 裡面改,這時候,在 rebase 試著 re-apply commits 的過程中,必定會產生 conflict。

image

現在 list 要利用到 style 裡面修飾好的樣式,在這個情況下,就是把 list 給 rebase 到 style 上面,也就是在 list branch 做 git rebase style 。不過你會看到這個:

First, rewinding head to replay your work on top of it...
Applying: add gender column
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Failed to merge in the changes.
Patch failed at 0001 add gender column

When you have resolved this problem run "git rebase --continue".
If you would prefer to skip this patch, instead run "git rebase --skip".
To restore the original branch and stop rebasing run "git rebase --abort".

跟預期的一樣出現了 conflict。當然,它會先試著自動 merge ,但如果改到的行有衝突,那就得要手動 merge 了,打開他說有衝突的檔案,改成正確的內容,接著使用 git add <file> (要把該檔案加進去 staging area,處理 rebase 的程式才能 commit),再 git rebase --continue 。

完成以後就會像這樣:

image

Interactive Mode: 偷天換日,自定重新 commit 的詳細步驟

接著 style 和 list 又陸續改了一些東西,主要是 list 裡面加了表單元件,而 style 則繼續修飾網頁整體設計。到了一個段落,該輪到 style 修飾 list 的表單了。目前的 commit history 長這樣:

image

不過在 style 要 rebase 到 list 上面之前,管 list 的人想把 list 上面的一些 commits 給整理過,因為他發現有這些問題:

  • "wrap the form with div" 太後面了,想移到前面
  • "fix typo of age field name" 跟 "add student id and age..." 可以合併
  • "add student id and age ..." 裡面東西太多,該拆成兩個
  • "form to add more *studetns*" 這 message 有錯字 “studetns”
  • "add gender select box" 裡面的程式碼有打錯字(囧

上面提到了 rebase 運作的方式是重新 commit 過一遍,那這個「重新 commit」的過程,能不能讓程式設計師來干預,達到偷天換日修改 commit 的目的呢?當然可以,只要利用 rebase 的 Interactive Mode。Git 的靈活就在這裡,連 commit 的內容都可以改。

如何啟動 interactive mode 呢?只要加入 -i 的參數就行了。以這個例子來說,list branch 是 based on 0580eab8 (fill in gender column) ,要從這個 commit 後面重新 apply 一次 commits ,也就是:

git rebase -i 0580eab8

接著會以你的預設編輯器打開一個檔案叫做 .git/rebase-merge/git-rebase-todo ,裡面已經有一些 git 幫你預設好的內容了,其實就是原本 commits 的清單,你可以修改它,告訴 git 你想怎麼改:

git rebase -i
pick 2c97b26 form to add more studetns
pick fd19f8e add student id and age field into the form
pick 02849bf fix typo of age field name
pick bd73d4d wrap the form with div
pick 74d8a3d add gender select box

# Rebase 0580eab..74d8a3d onto 0580eab
# ...[chunked]

第一個欄位就是操作指令,指令的解釋在該檔案下方有:

  • pick = 要這條 commit ,什麼都不改
  • reword = 要這條 commit ,但要改 commit message
  • edit = 要這條 commit,但要改 commit 的內容
  • squash = 要這條 commit,但要跟前面那條合併,並保留這條的 messages
  • fixup = squash + 只使用前面那條 commit 的 message ,捨棄這條 message
  • exec = 執行一條指令(但我沒用過)

此外還可以調整 commits 的順序,直接剪剪貼貼,改行的順序就行了。

調整 commit 順序、修改 commit message

首先我想要把 "wrap the form with div" 移到 "form to add more studetns" 後面,然後 "form to add more studetns" 要改 commit message (有 typo),那就改成這樣:

git rebase -i
reword 2c97b26 form to add more studetns
pick bd73d4d wrap the form with div
pick fd19f8e add student id and age field into the form
pick 02849bf fix typo of age field name
pick 74d8a3d add gender select box

接著儲存檔案後把檔案關掉(如 vim 的 :wq),就開始執行 rebase 啦,遇到 reword  時會再跳出編輯器,讓你重新輸入 commit message 。這時我把 studetns 改正為 students ,然後就跟平常 commit 一樣,存檔並關掉檔案。

git commit
form to add more students

# Please enter the commit message for your changes. Lines starting
# ...[chunked]

完成後會看到:

Successfully rebased and updated refs/heads/list.

再看 commit history ,的確達到了目的,而且 list 這個 branch 一樣還是 based on 0580eab8 ,後面那些剛剛 rebase 過的 commits 統統換了 commit ID :

image

合併 commits

剩下這些要做:

  • "fix typo of age field name" 跟 "add student id and age..." 可以合併
  • "add student id and age ..." 裡面東西太多,該拆成兩個
  • "add gender select box" 裡面的程式碼有打錯字

現在來試試看合併,一樣是 git rebase -i 0580eab8 ,並使用 fixup 來把 commit 給合併到上一個(如果用 squash 的話,會讓你修改 commit message ,修改時會把多個要連續合併的 commit messages 放在同一個編輯器裡):

git rebase -i
pick c3cff8a form to add more students
pick 7e128b4 wrap the form with div
pick 0d450ea add student id and age field into the form
fixup 8f5899e fix typo of age field name
pick e323dbc add gender select bo

完成後再看 commit history ,的確合併了:

image

修改、拆散 commit 內

剩下了拆 commit 和訂正 commit 內容。現在先來做訂正 commit ,這個學會了就知道怎麼拆 commit 了。

在這裡下 edit 指令來編輯 commit 內容:

git rebase -i
pick c3cff8a form to add more students
pick 7e128b4 wrap the form with div
pick 53616de add student id and age field into the form
edit c5b9ad8 add gender select box

存檔並關閉之後,現在的狀態是停在剛 commit 完 "add gender select box" 的時候,所以現在可以偷改你要改的東西,存檔以後把改的檔案用 git add 加進 staging area ,再打

git rebase --continue

來繼續,這時候因為 staging area 裡面有東西,git 會將它們與 "add gender select box" 透過 commit --amend 一起重新 commit 。

最後是拆 commit 。怎麼拆呢?剛剛做了 edit ,不是停在該 commit 之後嗎?這時候就可以偷偷 reset 到 HEAD^ (即目前 HEAD 的**前一個**),等於是退回到 HEAD 指到的 commit 的前一個,於是該 commit 的 changes 就被倒出來了,變成 changed but not staged for commit ,再根據你的需求,把 changes 給一個一個 commit 就行了。

實際的操作如下。首先是用 edit 指令來編輯 commit 內容:

git rebase -i
pick c3cff8a form to add more students
pick 7e128b4 wrap the form with div
edit 53616de add student id and age field into the form
pick 4dbcf49 add gender select box

接著使用

git reset HEAD^

來把目前的 HEAD 指標給指到 HEAD 的前一個,指完之後,原本 HEAD commit 的內容就被倒出來,並且也不存在 stage area 裡面, git 會提示有哪些檔案現在處於 changed but not staged for commit:

Unstaged changes after reset:
M   index.html

現在我可以一個一個 commit 了,原本是 add student id and age field ,我想拆成一次加 student id field ,一次加 age field 。commit 完成以後,再打

git rebase --continue

這次因為 staging area 裡面沒東西,所以就繼續 re-apply 剩下的 commits 。

現在打開 log 看,拆成兩個啦!

image

掌管 list branch 的人折騰完了,便告訴管 style 的說,可以 rebase 了,git 再度拯救了苦難程序員的一天

image


更多 rebase :

 

使用xctool自动打包,测试xcode项目

xctool是facebook开源的一个命令行工具,用来替代苹果的xcodebuild工具。

功能如下:

  • 像xcode一样跑测试用例
  • 结构化输出编译测试结果
  • 彩色且方便阅读的编译内容输出

示例截图:

xctool

如何安装xctool

最简单的办法是通过homebrew安装xctool

brew update
brew install xctool

搞定

如何使用xctool

打包

path/to/xctool.sh 
  -workspace YourWorkspace.xcworkspace 
  -scheme YourScheme 
  archive

build

path/to/xctool.sh 
  -workspace YourWorkspace.xcworkspace 
  -scheme YourScheme 
  build

测试

path/to/xctool.sh 
  -workspace YourWorkspace.xcworkspace 
  -scheme YourScheme 
  test

作者:  发表于May 14, 2013 at 5:17 pm

版权信息可以任意转载, 转载时请务必以超链接形式标明文章原始出处作者信息及此声明

【转】iOS抓包利器Charles

转自:http://wonderffee.github.io/blog/2013/07/13/best-packet-capture-tool-charles-in-ios/

看唐巧的分析支付宝客户端的插件机制一文发现他使用了抓包工具Charles,想起去年有人给我推荐过这个工具,但是当时我觉得WireShark就够用了就没尝试。这次看到又有人使用Charles我就重视起来了,Charles到底有什么好?

搜了一下,发现大多数使用者都是将Charles作为移动端抓包工具使用的,这样就意味着我们可以用Charles来截取iPhone/iPad上app所发出的网络请求来进行分析,分析支付宝客户端的插件机制一文就是这么用的。WireShark显然做不到这一点,优势一下子就体现出来了。

在Mac上安装Charles后,启动Charles,首先弹出一个框提示是否允许Charles有自动修改网络设置的权限,选择允许后出现Charles主界面。Charles主界面左侧有Structure和Sequence,你会发现会发现Structure这一栏里会逐步出现当前我的mac正在请求的链接,也就是说Charles一启动就自动进行抓包了。不过遗憾的是Structure栏里没有过滤选项,意味着你不能过滤特定网站。切换到Sequence栏,这个就容易懂了,按时间顺序来排列的,与WireShark一致。下方的Filter可以过滤,而是还是实时过滤的,这一点就比WireShark强多了。

如何在Mac上用Charles远程抓iPhone上app的网络请求呢?方法相当简单,下面就提供了HTTP和HTTPS抓包的操作步骤,简单几步就搞定了。

HTTP抓包

  • 打开Charles程序
  • 查看Mac电脑的IP地址,如192.168.1.7
  • 打开iOS设置,进入当前wifi连接,设置HTTP代理Group,将服务器填为上一步中获得的IP,即192.168.1.7,端口填8888
  • iOS设备打开你要抓包的app进行网络操作
  • Charles弹出确认框,点击Allow按钮即可

HTTPS抓包

  • 下载Charles证书http://www.charlesproxy.com/ssl.zip,解压后导入到iOS设备中(将crt文件作为邮件附件发给自己,再在iOS设备中点击附件即可安装;也可上传至dropbox之类的网盘,通过safari下载安装)
  • 在Charles的工具栏上点击设置按钮,选择Proxy Settings…
  • 切换到SSL选项卡,选中Enable SSL Proxying,别急,选完先别关掉,还有下一步
  • 这一步跟Fiddler不同,Fiddler安装证书后就可以抓HTTPS网址的包了,Charles则麻烦一些,需要在上一步的SSL选项卡的Locations表单填写要抓包的域名和端口,点击Add按钮,在弹出的表单中Host填写域名,比如填api.instagram.com,Port填443

我简单试用了一下Charles的远程抓包功能,发现Charles比WireShark还有一个优势是能对JSON数据(在JSON Text栏)进行解析,从而让我们可以更直观地查看JSON串信息(在JSON 栏)。此外Charles对中文支持比较好,JSON串中的中文信息一般会显示为一长串的\ug开头的字符,解析之后就能显示出中文了。平常总头痛Wireshark对中文支持不好,用Charles就完全没有这个问题了。

参考资料:
使用Charles远程调试iOS移动应用
mac下的抓包工具Charles

【转】NSURLSession使用说明及后台工作流程分析

转自:http://www.cnblogs.com/biosli/p/iOS_Network_URL_Session.html

非常感谢作者的详细解释,这几天一直在研究这个东西,拿来主义了,下面是正文

NSURLSession简介
NSURLSession是iOS7中新的网络接口,它与咱们熟悉的NSURLConnection是并列的。在程序在前台时,NSURLSession与NSURLConnection可以互为替代工作。注意,如果用户强制将程序关闭,NSURLSession会断掉。

NSURLSession提供的功能:

通过URL将数据下载到内存
通过URL将数据下载到文件系统
将数据上传到指定URL
在后台完成上述功能

工作流程
如果我们需要利用NSURLSession进行数据传输我们需要:

创建一个NSURLSessionConfiguration,用于第二步创建NSSession时设置工作模式和网络设置:
工作模式分为:

一般模式(default):工作模式类似于原来的NSURLConnection,可以使用缓存的Cache,Cookie,鉴权。
及时模式(ephemeral):不使用缓存的Cache,Cookie,鉴权。
后台模式(background):在后台完成上传下载,创建Configuration对象的时候需要给一个NSString的ID用于追踪完成工作的Session是哪一个(后面会讲到)。
网络设置:参考NSURLConnection中的设置项。

  1. 创建一个NSURLSession,系统提供了两个创建方法:

sessionWithConfiguration:
sessionWithConfiguration:delegate:delegateQueue:
    第一个粒度较低就是根据刚才创建的Configuration创建一个Session,系统默认创建一个新的OperationQueue处理Session的消息。

    第二个粒度比较高,可以设定回调的delegate(注意这个回调delegate会被强引用),并且可以设定delegate在哪个OperationQueue回调,如果我们将其设置为[NSOperationQueue mainQueue]就能在主线程进行回调非常的方便。

  2.创建一个NSURLRequest调用刚才的NSURLSession对象提供的Task函数,创建一个NSURLSessionTask。

  根据职能不同Task有三种子类:

NSURLSessionUploadTask:上传用的Task,传完以后不会再下载返回结果;
NSURLSessionDownloadTask:下载用的Task;
NSURLSessionDataTask:可以上传内容,上传完成后再进行下载。
  得到的Task,调用resume开始工作。

  3. 如果是细粒度的Session调用,Session与Delegate会在指定的OperationQueue中进行交互,以咱们下载例子,交互过程的顺序图如下(假如不需要鉴权,即非HTTPS请求):

  5. 当不再需要连接调用Session的invalidateAndCancel直接关闭,或者调用finishTasksAndInvalidate等待当前Task结束后关闭。这时Delegate会收到URLSession:didBecomeInvalidWithError:这个事件。Delegate收到这个事件之后会被解引用。

  6. 如果是一个BackgroundSession,在Task执行的时候,用户切到后台,Session会和ApplicationDelegate做交互。当程序切到后台后,在BackgroundSession中的Task还会继续下载,这部分文档叙述比较少,现在分三个场景分析下Session和Application的关系:

当加入了多个Task,程序没有切换到后台。
  这种情况Task会按照NSURLSessionConfiguration的设置正常下载,不会和ApplicationDelegate有交互。

当加入了多个Task,程序切到后台,所有Task都完成下载。
  在切到后台之后,Session的Delegate不会再收到,Task相关的消息,直到所有Task全都完成后,系统会调用ApplicationDelegate的application:handleEventsForBackgroundURLSession:completionHandler:回调,之后“汇报”下载工作,对于每一个后台下载的Task调用Session的Delegate中的URLSession:downloadTask:didFinishDownloadingToURL:(成功的话)和URLSession:task:didCompleteWithError:(成功或者失败都会调用)。

  之后调用Session的Delegate回调URLSessionDidFinishEventsForBackgroundURLSession:。

  注意:在ApplicationDelegate被唤醒后,会有个参数ComplietionHandler,这个参数是个Block,这个参数要在后面Session的Delegate中didFinish的时候调用一下,如下:

@implementation APLAppDelegate

– (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier

completionHandler:(void (^)())completionHandler

{

BLog();

/*

Store the completion handler. The completion handler is invoked by the view controller’s checkForAllDownloadsHavingCompleted method (if all the download tasks have been completed).

*/

self.backgroundSessionCompletionHandler = completionHandler;

}

//……

@end

//Session的Delegate

@implementation APLViewController

– (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session

{

APLAppDelegate *appDelegate = (APLAppDelegate *)[[UIApplication sharedApplication] delegate];

if (appDelegate.backgroundSessionCompletionHandler) {

void (^completionHandler)() = appDelegate.backgroundSessionCompletionHandler;

appDelegate.backgroundSessionCompletionHandler = nil;

completionHandler();

}

NSLog(@”All tasks are finished”);

}

@end

当加入了多个Task,程序切到后台,下载完成了几个Task,然后用户又切换到前台。(程序没有退出)
  切到后台之后,Session的Delegate仍然收不到消息。在下载完成几个Task之后再切换到前台,系统会先汇报已经下载完成的Task的情况,然后继续下载没有下载完成的Task,后面的过程同第一种情况。

当加入了多个Task,程序切到后台,几个Task已经完成,但还有Task还没有下载完的时候关掉强制退出程序,然后再进入程序的时候。(程序退出了)
  最后这个情况比较有意思,由于程序已经退出了,后面没有下完Session就不在了后面的Task肯定是失败了。但是已经下载成功的那些Task,新启动的程序也没有听“汇报”的机会了。经过实验发现,这个时候之前在NSURLSessionConfiguration设置的NSString类型的ID起作用了,当ID相同的时候,一旦生成Session对象并设置Delegate,马上可以收到上一次关闭程序之前没有汇报工作的Task的结束情况(成功或者失败)。但是当ID不相同,这些情况就收不到了,因此为了不让自己的消息被别的应用程序收到,或者收到别的应用程序的消息,起见ID还是和程序的Bundle名称绑定上比较好,至少保证唯一性。

总结
  就像前面说的,在普通的应用场景下NSURLSession与NSURLConnection相比没有什么优势,但是在程序切换到后台之后Background的Session就显得更加灵活了。

  另外,现在主流的网络开发框架AFNetworking已经更新到了2.0(只支持iOS 6 / iOS 7),其中最重要的一个更新就是添加了NSURLSession相关的支持。虽然就我现在(2013.10.13)看到他们的源码中,还没有完全的支持后台的Session(或者说没有考虑全我上述的后台情况),但是大家有兴趣可以关注一下他们后续的更新情况。

参考资料:
苹果官方文档:

https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/URLLoadingSystem/Articles/UsingNSURLSession.html#//apple_ref/doc/uid/TP40013509-SW1

注意:苹果又开始不更新Xcode 中的文档,然后悄悄在网上更新了,大家请关注相关文档最后的更新时间,以最新的为准

AFNetworking 2.0:

https://github.com/AFNetworking/AFNetworking/

xcode编译的一个诡异bug

今天开发的一个bug搞了好久也没看懂,最后觉得应该是编译器的问题
定义了一个变量

const static unsigned long long overSize = 1024 * 1024 * 1024 *4;
NSLog(@"%llu", overSize);

你猜输出是多少,本来打算是定一个一个4G的数字,结果出来是0,我想不通了,64位啊,无符号啊,2的64次方减1啊,怎么4G都表示不了

然后换一种写法

unsigned long long overSize = 1024 * 1024 * 1024;
unsigned long long overSize4 = overSize * 4;
NSLog(@"%llu", overSize4);

这样就能正常输出大小了,这个东西应该是编译器的bug吧