[转载]若干个游戏辅助的分析手记(三) - 漓江里的大虾 - 博客园

mikel阅读(903)

[转载]若干个游戏辅助的分析手记(三) – 漓江里的大虾 – 博客园.

若干个游戏辅助的分析手记(三)

—-飞五棋牌平台幸运骰子自动投注

目标进程进程名(幸运骰子):Sicbo.exe

  OK,开工。
进入游戏后,用OD附加,随后设断点:bp send,然后下注。没有断下,依次断WSASend等函数都断不下来。此路不通,祭出CE。
CE搜索投注额,最后定为到下面代码:

0040303A - 89 18  - mov [eax],ebx // 修改了自己在东风的押注数额


先走马观花逛 一圈,一路回溯会先经过GamePubl.dll模块,最后会回溯到ProcMsg模块。在ProcMsg模块中是一个大的消息循环。这几个模块的相关 CALL浪费了分析了整整一个通宵的时间,=_=!最后终于搞明白了它的大体框架,但是让我崩溃的是—-随后得出的结论这几个CALL仅仅是根据消息 更新界面(即相当于CE搜到的是界面的”Text”),所以得另寻方法。

  哎,一晚上的体力活没想最后成了杨白劳。睡一觉起来再说。

  zzZZZ.。o0O…

  一觉起来顿觉神清气爽。昨晚运气不佳,浪费了一个通宵。这活不但要看体力,还有一部分运气也是非常重要的。好了,不灰心。继续开工。

  整理下思路—-经过昨天一个晚上的分析,既然知道了上面是根据消息来更新界面,那么得找到消息的源头。在OD中对Sicbo.exe的主窗口下WM_LBUTTONUP消息断点,断下来后,返回程序领空,来到下面代码:

复制代码
10011AC0 >|$  53 PUSH EBX 10011AC1 |.  55 PUSH EBP 10011AC2 |.  56 PUSH ESI 10011AC3 |. 8BF1 MOV ESI,ECX 10011AC5 |.  8B86 08020000 MOV EAX,DWORD PTR DS:[ESI+208] 10011ACB |. 33DB XOR EBX,EBX 10011ACD |. 85C0 TEST EAX,EAX 10011ACF |.  57 PUSH EDI 10011AD0 |.  0F8E 95000000 JLE GamePubl.10011B6B 10011AD6 |.  8B6C24 1C     MOV EBP,DWORD PTR SS:[ESP+1C] 10011ADA |.  8D9B 00000000 LEA EBX,DWORD PTR DS:[EBX] 10011AE0 |>  85DB          /TEST EBX,EBX 10011AE2 |.  0F8C 9E000000 |JL GamePubl.10011B86 10011AE8 |.  3B9E 08020000 |CMP EBX,DWORD PTR DS:[ESI+208] 10011AEE |.  0F8D 92000000 |JGE GamePubl.10011B86 10011AF4 |.  8B86 FC010000 |MOV EAX,DWORD PTR DS:[ESI+1FC] 10011AFA |.  8B0C98        |MOV ECX,DWORD PTR DS:[EAX+EBX*4] 10011AFD |.  8A41 56       |MOV AL,BYTE PTR DS:[ECX+56] 10011B00 |.  84C0          |TEST AL,AL 10011B02 |.  8D3C9D 000000>|LEA EDI,DWORD PTR DS:[EBX*4] 10011B09 |.  74 51         |JE SHORT GamePubl.10011B5C 10011B0B |.  3B9E 08020000 |CMP EBX,DWORD PTR DS:[ESI+208] 10011B11 |.  7D 73         |JGE SHORT GamePubl.10011B86 10011B13 |.  8B96 FC010000 |MOV EDX,DWORD PTR DS:[ESI+1FC] 10011B19 |.  8B043A        |MOV EAX,DWORD PTR DS:[EDX+EDI] 10011B1C |.  8A48 55       |MOV CL,BYTE PTR DS:[EAX+55] 10011B1F |.  84C9          |TEST CL,CL 10011B21 |.  74 39         |JE SHORT GamePubl.10011B5C 10011B23 |.  3B9E 08020000 |CMP EBX,DWORD PTR DS:[ESI+208] 10011B29 |.  7D 5B         |JGE SHORT GamePubl.10011B86 10011B2B |.  8BC2          |MOV EAX,EDX 10011B2D |.  8B0C38        |MOV ECX,DWORD PTR DS:[EAX+EDI] 10011B30 |.  03C7          |ADD EAX,EDI 10011B32 |.  E8 39F7FEFF   |CALL GamePubl.?GetVisible@CCardCtrl@@QAE> 10011B37 |.  84C0          |TEST AL,AL 10011B39 |.  74 21         |JE SHORT GamePubl.10011B5C 10011B3B |.  3B9E 08020000 |CMP EBX,DWORD PTR DS:[ESI+208] 10011B41 |.  7D 43         |JGE SHORT GamePubl.10011B86 10011B43 |.  8B4C24 18     |MOV ECX,DWORD PTR SS:[ESP+18] 10011B47 |.  8B5424 14     |MOV EDX,DWORD PTR SS:[ESP+14] 10011B4B |.  8B86 FC010000 |MOV EAX,DWORD PTR DS:[ESI+1FC] 10011B51 |.  55            |PUSH EBP 10011B52 |.  51            |PUSH ECX 10011B53 |.  8B0C38        |MOV ECX,DWORD PTR DS:[EAX+EDI] 10011B56 |.  52            |PUSH EDX 10011B57 |.  E8 C426FFFF   |CALL GamePubl.?OnLButtonUp@CCardCtrl@@QA> 10011B5C |>  8B86 08020000 |MOV EAX,DWORD PTR DS:[ESI+208] 10011B62 |.  43            |INC EBX 10011B63 |.  3BD8          |CMP EBX,EAX 10011B65 |.^ 0F8C 75FFFFFF \JL GamePubl.10011AE0 10011B6B |> 8BCE MOV ECX,ESI 10011B6D |.  E8 CC430000   CALL <JMP.&MFC71.#1903_?Default@CWnd@@IAE> 10011B72 |.  C686 28020000>MOV BYTE PTR DS:[ESI+228],0;返回程序领空后回到这里       10011B79  |.  FF15 C4D80110 CALL DWORD PTR DS:[<&USER32.ReleaseCaptur> [ReleaseCapture 10011B7F |. 5F POP EDI 10011B80 |. 5E POP ESI 10011B81 |. 5D POP EBP 10011B82 |. 5B POP EBX 10011B83 |.  C2 0C00       RETN 0C
复制代码

回到程序领空的时候是停在了VA:10011B72,下面开始分析。
因为真正的投注(网络操作)跟用户界面更新是分开的,所以为了方便分析,要得找到网络操作跟用户界面更新的分界线,因为bp send行不通,所以换个方法。
打开虚拟机,在虚拟机中用另一个账号登陆游戏后申请坐庄,接着回到本机。
在上面的函数的开头F2下断,即下面这句代码下断:

10011AC0 >|$  53            PUSH EBX

在本机投注后,程序断了下来,接着观察虚拟机中发现已经有了投注信息,说明网络操作的代码不在这个代码段中。OK,回溯到上一层,继续上面的思路。
下面代码是上一层的代码:

复制代码
00409670   .  53 PUSH EBX 00409671   .  8B1D A4E04000 MOV EBX,DWORD PTR DS:[<&GDI32.PtInRegion>>; GDI32.PtInRegion 00409677   .  55 PUSH EBP 00409678   .  8B6C24 14     MOV EBP,DWORD PTR SS:[ESP+14] ; 第三个参数 Y 0040967C . 56 PUSH ESI 0040967D . 57 PUSH EDI 0040967E . 8B7C24 18     MOV EDI,DWORD PTR SS:[ESP+18] ; 第二个参数 X 00409682 . 8BF1 MOV ESI,ECX 00409684 . 8BCD MOV ECX,EBP 00409686   .  51            PUSH ECX                                  ; /Y 00409687   .  8BC7          MOV EAX,EDI                               ; |
00409689   .  50            PUSH EAX                                  ; |X 0040968A . 8B86 CC040000 MOV EAX,DWORD PTR DS:[ESI+4CC]            ; |
00409690   .  50            PUSH EAX                                  ; |hRegion 00409691 . FFD3 CALL EBX ; \PtInRegion 00409693 . 85C0 TEST EAX,EAX 00409695 . 0F84 9F000000 JE Sicbo.0040973A 0040969B . 80BE 02060000>CMP BYTE PTR DS:[ESI+602],1 004096A2 . 0F85 92000000 JNZ Sicbo.0040973A 004096A8 . 80BE 01060000>CMP BYTE PTR DS:[ESI+601],1 004096AF . 0F85 85000000 JNZ Sicbo.0040973A 004096B5 . 8BCD MOV ECX,EBP 004096B7 . 51            PUSH ECX                                  ; /Y 004096B8 . 8B8E AC040000 MOV ECX,DWORD PTR DS:[ESI+4AC]            ; | 004096BE . 8BC7 MOV EAX,EDI ; | 004096C0 . 50            PUSH EAX                                  ; |X 004096C1 . 51            PUSH ECX                                  ; |hRegion 004096C2 . FFD3 CALL EBX ; \PtInRegion 004096C4 . 85C0 TEST EAX,EAX 004096C6 . 74 04 JE SHORT Sicbo.004096CC 004096C8 . B3 01         MOV BL,1 004096CA . EB 3F JMP SHORT Sicbo.0040970B 004096CC >  8B96 B4040000 MOV EDX,DWORD PTR DS:[ESI+4B4] 004096D2 . 8BCD MOV ECX,EBP 004096D4 . 51 PUSH ECX 004096D5 . 8BC7 MOV EAX,EDI 004096D7 . 50 PUSH EAX 004096D8 . 52 PUSH EDX 004096D9 . FFD3 CALL EBX 004096DB . 85C0 TEST EAX,EAX 004096DD . 74 04 JE SHORT Sicbo.004096E3 004096DF . B3 02         MOV BL,2 004096E1 . EB 28 JMP SHORT Sicbo.0040970B 004096E3 >  55 PUSH EBP 004096E4 . 57 PUSH EDI 004096E5 . 8D8E B8040000 LEA ECX,DWORD PTR DS:[ESI+4B8] 004096EB . E8 C0D6FFFF CALL Sicbo.00406DB0 004096F0 . 85C0 TEST EAX,EAX 004096F2 . 74 04 JE SHORT Sicbo.004096F8 004096F4 . B3 03         MOV BL,3 004096F6 . EB 13 JMP SHORT Sicbo.0040970B 004096F8 >  55 PUSH EBP 004096F9 . 57 PUSH EDI 004096FA . 8D8E C0040000 LEA ECX,DWORD PTR DS:[ESI+4C0] 00409700 . E8 ABD6FFFF CALL Sicbo.00406DB0 00409705 . 85C0 TEST EAX,EAX 00409707   .  74 31 JE SHORT Sicbo.0040973A 00409709   .  B3 04         MOV BL,4 0040970B >  E8 12260000   CALL <JMP.&MFC71.#1091_?AfxGetThread@@YGP>
00409710 . 85C0 TEST EAX,EAX 00409712   .  74 09 JE SHORT Sicbo.0040971D 00409714 . 8B10 MOV EDX,DWORD PTR DS:[EAX] 00409716 . 8BC8 MOV ECX,EAX 00409718   .  FF52 7C       CALL DWORD PTR DS:[EDX+7C] 0040971B . EB 02 JMP SHORT Sicbo.0040971F 0040971D > 33C0 XOR EAX,EAX 0040971F >  0FB696 550800>MOVZX EDX,BYTE PTR DS:[ESI+855] 00409726   .  8B40 20       MOV EAX,DWORD PTR DS:[EAX+20] 00409729 . 0FB6CB MOVZX ECX,BL 0040972C . 51            PUSH ECX                                  ; /lParam 0040972D . 52            PUSH EDX                                  ; |wParam 0040972E . 68 C9040000   PUSH 4C9                                  ; |Message = MSG(4C9) 00409733   .  50            PUSH EAX                                  ; |hWnd 00409734   .  FF15 34E54000 CALL DWORD PTR DS:[<&USER32.SendMessageA>>; \这里是下注关键 0040973A >  8B4C24 14     MOV ECX,DWORD PTR SS:[ESP+14] 0040973E . 55 PUSH EBP 0040973F . 57 PUSH EDI 00409740   .  51 PUSH ECX 00409741 . 8BCE MOV ECX,ESI 00409743   .  C686 02060000>MOV BYTE PTR DS:[ESI+602],0 0040974A . FF15 CCE04000 CALL DWORD PTR DS:[<&GamePublic.?OnLButto>;  GamePubl.?OnLButtonUp@CGameFrameView@@QAEXIVCPoint@@@Z 00409750 . 5F POP EDI 00409751 . 5E POP ESI 00409752 . 5D POP EBP 00409753 . 5B POP EBX 00409754   .  C2 0C00       RETN 0C
复制代码

  按照上面的思路,依然在函数头下断,投注后程序断了下来,观察虚拟机中此次并没有投注信息,很好!这段代码就是关键了!
重点分析此段代码。
首先分析该函数的参数,这个函数一共有3个参数,第一个参数是什么没去分析了这个不是重点。重点是第二个参数代表X坐标,第三个参数是Y坐标。
函数开头的代码是把XY的坐标传进PtInRegion来判断投注区。随后对XY坐标进行转化成相应投注区的索引。
继续往下看代码发现了这里:

复制代码
0040972C   .  51            PUSH ECX                                  ; /lParam 0040972D . 52            PUSH EDX                                  ; |wParam 0040972E . 68 C9040000   PUSH 4C9                                  ; |Message = MSG(4C9) 00409733   .  50            PUSH EAX                                  ; |hWnd 00409734   .  FF15 34E54000 CALL DWORD PTR DS:[<&USER32.SendMessageA>>; \这里是下注关键
复制代码

  

  这里的代码非常非常可疑,因为这里是一个SendMessage的调用,还记得刚才说要找界面更新的消息源头么?为了验证这个 SendMessage,在 00409734   .  FF15 34E54000 CALL DWORD PTR DS:[<&USER32.SendMessageA>> 这句代码下断,游戏投注断了下来,观察虚拟机中没有投注信息,回到本机单步执行该条语句,再次回到虚拟中发现了投注信息,说明这里就是下注的关键了!已经 离成功很近了。

下面着重分析SendMessage的参数 首先是HWND,该句柄通过SPY++查找,发现该句柄就是游戏本身主窗口的句柄了, 接着是消息号是4C9,很明显这是一个自定义的消息。 WPARAM 是投注筹码的索引, LPARAM 是投注区域的索引。 OK了,参数分析完毕。如下:

复制代码
SendMessage( HWND hWnd, // 游戏本身主窗口的句柄 
 UINT Msg, // 4C9,自定义的消息 
 WPARAM wParam, // 投注筹码的索引,2是1000,3是10000以此类推
 LPARAM lParam // 投注区域的索引,1是东,2是南以此类推
 );
复制代码

那么游戏本身实现投注就是靠SendMessage来传递一个自定义的消息,而该自定义的消息处理函数内部进行了什么处理在这里不用去分析已经 可以得出结论:该自定义消息处理函数内部进行投注的网络操作,网络操作完毕后,随后再次发送一个自定义消息来更新界面,这里就解释了第一天通宵的时候跟的 那几个CALL是在一个消息循环里的原因了。 所以我们不必去分析该自定义的消息处理函数了,因为我们也可以直接向该窗口发送此消息就OK。因为SendMessage是跨进程的,这样做没有任何问 题。 有了上面的参数分析,非常容易就写出投注的测试程序。这里提一下一点比较有意思的事情是,投注额的索引是从2开始,2代表1000.我们可以传递“1”。 这样虽然在游戏中没有100元的筹码,但是我们可以投注100.呵呵,有意思。

至此,投注的功能已经搞定。

后记:投注功能写好后,就是根据客户提供的“赌徒算法”,进行挂机自动投注了。测试了几次,短时间(连续挂一两个小时)收益还不错。但是长时间比如每天睡觉的时候挂机,起床的时候,发现有赢有亏,看来“赌徒算法”有待改进,哈哈。

[转载]SQL SERVER 查询性能优化——分析事务与锁(一) - DotNet菜园 - 博客园

mikel阅读(1062)

[转载]SQL SERVER 查询性能优化——分析事务与锁(一) – DotNet菜园 – 博客园.

一、使用工具观察与分析数据库中锁信息

      对于并发系统、对于有大量读写数据库操作的业务系统等,当多人同时访问数据库时,最复杂的情况之一就是大量的事务与资源纠缠在一起,相互被锁而彼此等待, 也称为死锁。当数据库中出现很多很多锁时,系统瞬间就无法提供正常服务。此时观察系统资源的使用情况,会发现CPU使用率不高,内存占用量也不高,还有很多未使用的内存,网络带宽也充足,硬盘也不繁忙,通过数据库管理工具查询的话,SQL SERVER中的数据也正常无误,但是使用系统的用户访问此数据库时却要需要等很多久很久,更多的就出现连接超时,数据库无响应。

      此现象类似高速公路上有大量的车辆进入,而在收费口却只开了一个、二个。造成收费口的车辆拥堵,而后方却又有大量的车辆涌入,这时所有的车辆都会慢下来,驾驶员彼此再减速刹车互相等待,最后导致后方的车子会被迫停下来,形成高速公路停车场。(就如第一次节假日高速公路免费通行那天,即2012年国庆高速公路大堵车的情形,高速公路成停车场)

      如果数据库中在设计时,设计不当,设计有过热数据表时,也就是所有应用程序的访问行为都跟某张数据表有关,甚至集中访问某些字段,这就像前述例子中的所有 车辆都在同一地点(高速公路收费口)驶上高速公路。这时耗时与快速的两种访问行为混合在一起,就像大货车与小轿车一起争车道,很容易发生彼此锁定,形成死 锁而动弹不得的情况。

      此类性能延迟现象,尤其在数据使用一段较长时间之后容易发生,当新系统刚上线时,或SQL语句写的不好,如Select * from SaleInfo 且没有where条件,初期因数据量不大,可以瞬间取回结果而不影响别人。但当系统使用一段时间之后,数据库中的数量增加,使用人数增多,使用方式改变。不好的SQL语句就出现运行迟缓,这时数据库开始积聚等待处理的需求,也就是彼此干扰而造成锁定,交互作用后,可能瞬间瘫痪整个系统。当你观察当前数据库的锁定时,可能会发现存在几千个锁定,很难理出头绪。

       本文首先来讨论可以使用哪些工具来观察与分析SQL SERVER中的锁信息。当然,所有的性能调校都需要先了解情况,最好是可重复发生的情景,例如在每天什么时间,多少用户上线后,执行了什么样的功能后,等等。先通盘了解情况,然后评估SQL SERVER的问题,再进一步深入观察SQL SERVER

第一步,使用SQL SERVER提供图形界面工具观察哪些进程被加锁

 1 ) SQL SERVER 2005可以通过Microsoft SQL Server Management Studio的“管理–活动监视器”》“右键”》“进程信息”,这里呈现连接、锁定的各种细节。如下图1。

 

                                                                             图1

2) SQL SERVER 2005 的Microsoft SQL Server Management Studio提供图形界面工具中的信息不会自动更新。刷新“活动监视器”界面中的内容,需要手动使用鼠标点击上方工具栏上的“刷新”按钮。如下图2,是未点击“刷新”按钮之前的显示的内容,图3,是点击“刷新”按钮之后的,显示的内容。

        图2

        图3

2) SQL SERVER 2005中可以通过下图中的设置,设置成自动更新的时间周期。如下图4。

 

 

                               图4

4) SQL SERVER 2005Microsoft SQL Server Management Studio中的“活动监视器》进程信息”提供相当多的信息,其中“等待类型”与“阻塞者”可呈现进程彼此的关系及进程的状态信息。

如果要查询“锁”的细节信息,则可以点击右键窗口中的“活动监视器按进程分类的锁”及“活动监视器按对象分类的锁”两个标记进行观察。

活动监视器按进程分类的锁

如下图5。

 

                                               图5

活动监视器按对象分类的锁

如下图6。

 

                                                图6

 

接下来说一下SQL SERVER 2008中的活动监视器的使用:

1) SQL SERVER 2008活动监视器就不在管理中了。一、点击工具栏上的图标。二、使用右键点击数据库服务器,如下图7。然后都会出现如图8的图形界面工具。

 

 

                图7

 

                       图8

 

2) SQL SERVER 2008中默认就是自动更新的,自动更新时间间隔为10秒。可以通过下图中的设置,设置成自动更新的时间周期。如下图9。右键单击“概述,选择“刷新间隔,然后选择活动监视器获取新的实例信息所用的间隔。

 

 

          图9

 

3) SQL SERVER 2008Microsoft SQL Server Management Studio中的“活动监视器–》进程”只提供了一些基本的信息,其中“等待类型”与“阻塞者”可呈现进程彼此的关系及进程的状态信息。所提供的锁定进程的信息没有SQL SERVER 2005中所提供的详细。

                                      图10

[转载]编码的奥秘摩尔斯电码 - 许宏川 - 博客园

mikel阅读(920)

[转载]【编码的奥秘】摩尔斯电码 – 许宏川 – 博客园.

所谓编码就是用某种方法来替代文字,而交流的双方都熟知这种替代规则便可以在特殊情景下交流了。

摩尔斯电码就是其中一种。

 

摩尔斯电码(Morse alphabet)是美国人摩尔斯(Samuel Finley Breese Morse)于1837年发明的。

是一种用二进制(点和划)组合来表示文字的编码规则。

规则是:

用1~4位的二进制组合来表示26个英文字母,因为4位二进组组合一共可以表示2+4+8+16=30个符号,足够表示26个英文字母。

然后用5位的二进制组合来代表数字

然后再用6位的二进制组合表示标点符号

这样只要双方都熟记以上规则便可以用摩尔斯编码进行交流了。(当然还有其它语言的编码表,但是作为了解并不需要知道那么多)

例如可以用手电筒,手电筒闪烁一下代表点,亮一会代表划,点划之间要停顿一下,因为一个字母由一串电话组成,一个单词由一串字母组成,一个句子由一串单词组成,那么就规定停顿时间是单词>字母>点划就行了。

如果交流的双方之间有遮挡物,光线无法通过,那么也可以用某种能发出两种(例如嘀嗒)声音的工具,例如嘀代表点,嗒代表划。

但是光线和声音都只能是短距离交流,如果想长距离使用交流那就是使用电报机了。

原理是短脉冲信号电表点,长脉冲信号代表划。

另外为了提高传递速度,把短位用于常用字母,例如e、t等,还使用了一些缩写,例如u代表you,hv代表have,vy代表very等等。

注:摩尔斯电码只是提供了一种编码的思想,并不是全世界统一使用以上规定的编码表,特别是军事上,为了保守秘密,每个军方可以有自己的编码表,甚至会不定期更换。

[转载]HTTP 长连接 - lengyuhong - 博客园

mikel阅读(917)

[转载]HTTP 长连接 – lengyuhong – 博客园.

HTTP 长连接

上周再做一个easyproxy的小项目,实现代码反向代理的工作,具体就是在tcp层对各个请求(只要遵循建立在tcp层之上的协议即可)进行解析,然后分发各个具体服务上。

这中间遇到的一个问题就是HTTP中的长连接问题,重新去看了下具体的http协议,发现之前对这块知识还是存在盲点。

这篇文章可以算是自己的学习笔记, 很多内容更是直接使用我看到和觉得讲得不错的资料,希望对大家也有所帮助。

基础知识

 1. 名称

维基百科中的介绍:

“HTTP persistent connection, also called HTTP keep-alive, or HTTP connection reuse, is the idea of using a single TCP connection to send and receive multiple HTTP requests/responses, as opposed to opening a new connection for every single request/response pair.”

讲这个,是因为我觉得“长连接”这个称呼并不是非常准确,用“持久连接”,而“Long Connection”这种直接翻译词就更不用再用了。但毕竟这个名字已经沿用这么久了,我们也就继续用这个名

2. 原理

在HTTP协议中长连接中同一个TCP连接来发送和接收多个HTTP请求/应答,而不是为每一个新的请求/应答打开新的连接的方法。

下图是网上一张很形象讲解普通连接和长连接区别的图

从图中,我们就能清晰地看到,两者区别就是长连接会“复用”原先建立TCP连接

3. 实现

在 HTTP 1.0 中

没有官方的 keepalive 的操作。通常是在现有协议上添加一个指数。如果浏览器支持 keep-alive,它会在请求的包头中添加:

Connection: Keep-Alive

然后当服务器收到请求,作出回应的时候,它也添加一个头在响应中:

Connection: Keep-Alive

这样做,连接就不会中断,而是保持连接。当客户端发送另一个请求时,它会使用同一个连接。这一直继续到客户端或服务器端认为会话已经结束,其中一方中断连接。

比如我用chrome访问博客园首页

request header:

response header:

在 HTTP 1.1 中 

所有的连接默认都是持续连接,除非特殊声明不支持。

所以现在各个浏览器的高版本基本都是支持长连接。

4. 优势

  • 较少的CPU和内存的使用(由于同时打开的连接的减少了)
  • 允许请求和应答的HTTP pipelining
  • 降低网络阻塞 (TCP连接减少了)
  • 减少了后续请求的延迟(无需再进行握手)
  • 报告错误无需关闭TCP连接

其实它一切的优势就在于复用了原先建立的TCP连接,减少重新建立TCP连接的消耗

5. 劣势

  • 空闲的连接需要过段时间后才能被断开,可能影响整体性能(比如那些单次访问次数多的web 服务)

6. 浏览器

现在的高版本浏览器都是默认支持长连接的,而它们维护长连接的方式就是用“超时管理”。

在一段时间内没有请求和相应时,就自动将连接断掉。

参考文章:

维基百科介绍:英文

Persistent HTTP Connections in RFC 2616 “Hypertext Transfer Protocol — HTTP/1.1

[转载]jQuery获取CSS样式中的颜色值的问题,不同浏览器格式不同怎么办 - 八戒Done - 博客园

mikel阅读(784)

[转载]jQuery获取CSS样式中的颜色值的问题,不同浏览器格式不同怎么办 – 八戒Done – 博客园.

 使用JQuery获取样式中的background-color的值时发现在获取到的颜色值在IE10以下版本中是以HEX格式显示【#ffff00】,而IE10,、Chrome、Firefox中则是以GRB格式显示【rgb(255,0,0)】,由于需要对颜色值进行判断处理,所以需要得到统一的颜色格式,最好是HEX格式的,方便处理点。搜索了一下,从国外的一个网站上得到一段代码:

1
2
3
4
5
6
$.fn.getHexBackgroundColor = function() {
    var rgb = $(this).css('background-color');
    rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
    function hex(x) {return ("0" + parseInt(x).toString(16)).slice(-2);}
    return rgb= "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
}

上面定义的是一个JQuery函数,我们可以通过 $(“#bg”).getHexBackgroundColor(); 获取到标签id=”bg”的background-color的RGB值。

下面做一下小小的修改,就是加一个判断,如果是显示HEX值(IE10以下)就直接拿值,如果是非IE浏览器则将值转换成RGB格式:

复制代码
 1 $.fn.getBackgroundColor = function() {  2     var rgb = $(this).css('background-color');  3     if(rgb >= 0) return rgb;//如果是一个hex值则直接返回
 4    else{  5         rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);  6         function hex(x) {return ("0" + parseInt(x).toString(16)).slice(-2);}  7         rgb= "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);  8  }  9     return rgb; 10 }
复制代码

[转载]瀚海拾贝(一)HTTP协议/IIS 原理及ASP.NET运行机制浅析【图解】 - wenthink - 博客园

mikel阅读(609)

转载瀚海拾贝(一)HTTP协议/IIS 原理及ASP.NET运行机制浅析【图解】 – wenthink – 博客园.

前言

前一段在整理邮件的时候发现几年前和CDD老师交流时的一份邮件.下面是简单摘要:

“从技术角度来说,无论哪一个阵营,跟新技术都是不可避免的,也是很累的,当然作 为一个程序员来说,也是必须的。要想让技术的更新对自己的影响减小,基础就必须打牢。所以,底层的东西和抽象层的东西需要下一番功夫。因为说到底,无论什 么技术,无非就是架构和最终的实现,技术框架只是应用开发的一个平台一种技术,如果了解了具体的东西,技术更新对你来说就没什么影响了,或者换句话说,你 要学一种新的技术,速度和效率会非常之高。”

上面一段话对自己的影响很大,可能大家在踏入“程序人生”的时候都会存在一些迷茫和彷徨。尽管我是属于那种相当热爱Proramming的一份子,但是面对万花筒般的技术分支也曾徘徊犹豫过.徘徊之余要做的事情便是夯实基础,寻找自己的兴趣与方向.对技术的迭代,以不变应万变才是王道.

正因为如此,所以也不会存在银弹之说.如果真的有银弹的话那么我信奉的是:程序=数据结构+算法

我选择的方向是Web,也相信Web终究会是互联网的未来.这篇文章简单谈一下自己对.NET平台下Web基础的一些浅解,由于自己水平有限,不足之处烦请见谅.

HTTP协议

HTTP协议是浏览器和服务器双方共同遵循的规范.是一种基于TCP/IP(传输层协议,相对应的有UDP)的”应用层协议”

PS:TCP/UDP是广泛使用的网络通信协议,UDP协议具有不可靠性和不安全性,

相对来说TCP协议是基于连接和三次握手的(相对可靠与安全),然而B/S架构的网站,由于同时在线的人数会很多,如果都与服务器保持连接状态.服务器的承载是相当大的,

因而衍生出HTTP协议.简单的说:请求发起之后服务器端立刻关闭连接并释放资源.也正因为如此,HTTP协议通常被理解为”无状态”的.

当然维系”状态”的手段有很多;如 Session/Cookie等 这里暂且不多做讨论.

先来看一下典型的OSI七层模型 图解

OSI2

HTTP最通俗的理解 请求/响应.

图示:

Http

HTTP报文信息

HTTP Request Header

HttpRequest

HTTP Response Header

HttpRespond

当然,也可以通过设置改变浏览器的选项.这里不做详细说明.不清楚的可以Google.微笑

给出ASP.NET下添加P3P头信息的例子

HttpContext.Current.Response.AddHeader(“p3p”, “CP=\”IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\””); 

有兴趣详细了解的可以参考 MSDN 中关于部署 P3P的文章。

 

下面是老生常谈的内容了(熟悉的朋友,自行跳过,权当温习下了 : )   )

请求头(消息头)包含(客户机请求的服务器主机名,客户机的环境信息等):
Accept:用于告诉服务器,客户机支持的数据类型  (例如:Accept:text/html,image/*)
Accept-Charset:用于告诉服务器,客户机采用的编码格式
Accept-Encoding:用于告诉服务器,客户机支持的数据压缩格式
Accept-Language:客户机语言环境
Host:客户机通过这个服务器,想访问的主机名
If-Modified-Since:客户机通过这个头告诉服务器,资源的缓存时间
Referer:客户机通过这个头告诉服务器,它(客户端)是从哪个资源来访问服务器的(防盗链)
User-Agent:客户机通过这个头告诉服务器,客户机的软件环境(操作系统,浏览器版本等)
Cookie:客户机通过这个头,将Coockie信息带给服务器
Connection:告诉服务器,请求完成后,是否保持连接
Date:告诉服务器,当前请求的时间

一个http响应代表服务器端向客户端回送的数据,它包括:
一个状态行,若干个响应消息头,以及实体内容
状态行:  例如:  HTTP/1.1  200 OK   (协议的版本号是1.1  响应状态码为200  响应结果为 OK)

响应头(消息头)包含:
Location:这个头配合302状态吗,用于告诉客户端找谁
Server:服务器通过这个头,告诉浏览器服务器的类型
Content-Encoding:告诉浏览器,服务器的数据压缩格式
Content-Length:告诉浏览器,回送数据的长度
Content-Type:告诉浏览器,回送数据的类型
Last-Modified:告诉浏览器当前资源缓存时间
Refresh:告诉浏览器,隔多长时间刷新
Content- Disposition:告诉浏览器以下载的方式打开数据。例如: context.Response.AddHeader(“Content-Disposition”,”attachment:filename=icon.jpg”);                                        context.Response.WriteFile(“icon.jpg”);
Transfer-Encoding:告诉浏览器,传送数据的编码格式
ETag:缓存相关的头(可以做到实时更新)
Expries:告诉浏览器回送的资源缓存多长时间。如果是-1或者0,表示不缓存
Cache-Control:控制浏览器不要缓存数据   no-cache
Pragma:控制浏览器不要缓存数据          no-cache
Connection:响应完成后,是否断开连接。  close/Keep-Alive
Date:告诉浏览器,服务器响应时间

IIS运行过程

有了上面的HTTP协议的知识回顾,下面来让我们看下IIS是怎样工作的?

IIS 5.X 已经距离我们很远了.好吧 XP默认的好像是的… 为万恶的IE6 默哀下0.0  .

这里我们来看一下IIS 6 的图示

IIS6-3

 

根据上图简单分析下IIS6的运行过程

在 User Mode 下,http.sys 接收到 http request,然后它会根据 IIS 中的 Metabase 查看基于该 Request 的 Application 属于哪个 Application Pool, 如果该 Application Pool 不存在,则创建之。否则直接将 request 发到对应 Application Pool 的 Queue中。

每个 Application Pool 对应着一个 Worker Process — w3wp.exe,(运行在 User Mode 下)。在 IIS Metabase 中维护着 Application Pool 和 Worker Process 的Mapping。WAS(Web Administrative Service)根据这样一个 mapping,将存在于某个 Application Pool Queue 的 request 传递到对应的 Worker Process (如果没有,就创建这样一个进程)。在 Worker Process 初始化的时候,加载 ASP.NET ISAPI,ASP.NET ISAPI 进而加载 CLR。最后通过 AppManagerAppDomainFactory 的 Create 方法为 Application 创建一个 Application Domain;通过 ISAPIRuntime 的  ProcessRequest 处理 Request,进而将流程进入到 ASP.NET Http Runtime Pipeline。

PS几个知识点:

  1. HTTP.SYS:(Kernel)的一个 组件,它负责侦听(Listen)来自于外部的HTTP请求,根据请求的URL将其转发给相应的应用程序池 (Application Pool)。当此HTTP请求处理完成时,它又负责将处理结果发送出去.为了提供更好的性能,HTTP.SYS内部建立了一个缓冲区,将最近的HTTP请 求处理结果保存起来。
  2. Application Pool:  IIS总会保持一个单独的工作进程:应用程序池。所有的处理都发生在这个进程里,包括ISAPI dll的执行。对于IIS6而言,应用程序池是一个重大的改进,因为它们允许以更小的粒度控制一个指定进程的执行。你可以为每一个虚拟目录或者整个Web 站点配置应用程序池,这可以使你很容易的把每一个应用程序隔离到各自的进程里,这样就可以把它与运行在同一台机器上其他程序完全隔离。从Web处理的角度 看,如果一个进程死掉,至少它不会影响到其它的进程。
    当应用程序池接收到HTTP请求后,交由在此应用程序池中运行的工作者进程Worker Process: w3wp.exe来处理此HTTP请求。
  3. Worker Process: 当工作者进程接收到请求后,首先根据后缀找到并加载对应的ISAPI扩展 (如:aspx 对应的映射是aspnet_isapi.dll),工作者进程加载完aspnet_isapi.dll后,由aspnet_isapi.dll负责加载 ASP.NET应用程序的运行环境即CLR (.NET Runtime)。
    Worker Process运行在非托管环境,而.NET中的对象则运行在托管环境之上(CLR),它们之间的桥梁就是ISAPI扩展。
  4. WAS(Web Admin Service:这是一个监控程序,它一方面可以存取放在InetInfo元数据库(Metabase)中的各种信息,另一方面也负责监控应用程序池(Application Pool)中的工作者进程的工作状态况,必要时它会关闭一个老的工作者进程并创建一个新的取而代之。

再来看下网上对IIS7经典模式下的图解

IIS 7 应用程序池的托管管道模式“经典”模式也是这样的工作原理。这种模式是兼容 IIS 6 的方式, 以减少升级的成本。

2011101914255884

小插曲

场景假定:

截获客户端的请求,并对请求进行重写。在IIS6中,请求的截获动作只能被限制在IIS加载aspnet_isapi.dll后,也就是说:如果该请求不是明确针对asp.net资源的请求(比如这个请求只是一个静态文件的请求,如www.cnblogs.com/index.html, 这时我们就便不能在代码中编写截获请求的逻辑,因为IIS6是根据URL的后缀来映射并加载对应的isapi的,如果一个请求的url 是:www.cnblogs.com/index.aspx,根据”.aspx”这个后缀,IIS6可以得知这个请求是针对asp.net资源的,应该加 载aspnet_isapi.dll创建.net运行时并运行asp.net页面的代码,但很明显,诸 如”www.cnblogs.com/index.html”这种请求,IIS6通常认为不是对asp.net资源的请求,因此不会加载 aspnet_isapi.dll来运行asp.net,我们即使在asp.net页面中编写了拦截请求的代码,也不会被执行。当然,这里我说通常是有原 因的,因为我们可以在IIS6中添加通配符程序映射的方式,或者在web.config中对某种请求手动添加处理程序的方式,来迫使IIS6为非 asp.net资源类型的请求加载aspnet_isapi.dll。IIS6中对请求的执行流程如上.

咦,有木有人和我一样想到了URL Routing 和URL Rewriting ?

这里不做说明,大叔手记16传送门:http://www.cnblogs.com/TomXu/archive/2011/12/27/2303486.html

这个问题先放一下~~了解II7的集成模式也许可以有一些思绪 微笑

让我们再来看下IIS官网上对IIS7的图解

传送门 :http://www.iis.net/learn/get-started/introduction-to-iis/introduction-to-iis-architecture

introduction-to-iis-architecture-101-OverviewOfHTTPRequest

1、当客户端浏览器开始 HTTP 请求一个WEB 服务器的资源时,HTTP.sys 拦截到这个请求。

2、HTTP.sys 联系 WAS 获取配置信息。

3、WAS 向配置存储中心(applicationHost.config)请求配置信息。

4、WWW 服务接收到配置信息,配置信息指类似应用程序池配置信息,站点配置信息等等。

5、WWW 服务使用配置信息去配置 HTTP.sys 处理策略。

6、WAS为请求创建一个进程(如果不存在的话)

7、工作者进程处理请求并对HTTP.sys做出响应.

8、客户端接受到处理结果信息。

IIS  7 应用程序池的托管管道模式(集成模式)华丽的变身

IIS7ASP.NET

IIS7中对asp.net的请求不再是分两条处理管道,而是将asp.net和 IIS集成起来,这样做的好处是统一了请求验证工作,加强了asp.net对于请求的控制能力等等。在IIS7中,asp.net不再像IIS6一样只限 定于aspnet_isapi.dll中,而是被解放出来,从IIS接收到HTTP请求开始,即进入asp.net的控制范围,asp.net可以存在于一个请求在IIS中各个处理阶段。甚至可以为部署在IIS7中的PHP应用提供基于asp.net的验证身份验证功能(传送门:http://msdn.microsoft.com/zh-cn/magazine/cc135973.aspx)。

好吧,戛然而止一下,篇幅有限:IIS部分告一段落 留一些遐想空间.

再来分析ASP.NET的运行机制

ASP.NET运行机制

在IIS6图示中我们分析到“ AppManagerAppDomainFactory 的 Create 方法为 Application 创建一个 Application Domain;通过 ISAPIRuntime 的  ProcessRequest 处理 Request,进而将流程进入到 ASP.NET Http Runtime Pipeline。”

下面我们来看一下AppDomain运行过程图示

AppDomain-3

AppDomain的作用,相信大家都很了解了吧.这里简明扼要的写几点:

一个AppDomain中的代码创建的对象不能由另一个AppDomain中的代码直接访问(只能使用按引用封送或者按值封送,起到了很好的隔离作用).

AppDomain可以卸载 CLR不支持从AppDomain中卸载一个程序集的能力,但可以告诉CLR卸载一个AppDomain,从而达到卸载当前包含在该AppDomain内的所有程序集.

AppDomain 可以单独保护 当宿主加载一些代码之后,可以保证这些代码不会被破坏(或读取)宿主本身使用的一些重要的数据结构.

AppDomain可以单独配置 设置主要影响CLR在AppDomain中加载程序集的方式,涉及搜索路径、版本绑定重定向、卷影复制及加载器的优化。

由以上几点可以看出AppDomain确保了Windows系统及其中运行的应用程序的健壮性。AppDomain提供了保护、配置和终止其中每一个应用程序所需的隔离性。

再来看下ProcessRequest的过程

HttpApplication_Analysis

简单分析一下上图

ProcessRequest(HttpWorkerRequest wr)中判断wr是否为null,然后判断管线是否完整,再调用ProcessRequestNoDemand(wr)方法,

并判断当前RequestQueue 是否为null,接着计算等待时间并更新管线数 CalculateWaitTimeAndUpdatePerfCounter(wr);

重置wr开始时间wr.ResetStartTime();调用ProcessRequestNow(wr)方法,并调用ProcessRequestInternal(wr)方法

继续图例

ProcessRequestInternal

ProcessRequestInternal方法如下:

复制代码
 1  private void ProcessRequestInternal(HttpWorkerRequest wr)  2  {  3  HttpContext context;  4      try 
 5  {  6          context = new HttpContext(wr, false);//由HttpWorkerRequest生成HttpContext 
 7  }  8      catch 
 9  { 10  
11        //常见的400错误,就是在这里捕捉到滴 
12          wr.SendStatus(400, "Bad Request"); 13          wr.SendKnownResponseHeader(12, "text/html; charset=utf-8"); 14          byte[] bytes = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>"); 15  wr.SendResponseFromMemory(bytes, bytes.Length); 16          wr.FlushResponse(true); 17  wr.EndOfRequest(); 18          return; 19  } 20      wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, context); 21      Interlocked.Increment(ref this._activeRequestCount); 22  HostingEnvironment.IncrementBusyCount(); 23      try 
24  { 25          try 
26  { 27              this.EnsureFirstRequestInit(context); 28  } 29          catch 
30  { 31              if (!context.Request.IsDebuggingRequest) 32  { 33                  throw; 34  } 35  } 36  context.Response.InitResponseWriter(); 37          IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(context); 38  //得到HttpApplication 
39  
40  if (applicationInstance == null) 41  { 42              throw new HttpException(System.Web.SR.GetString("Unable_create_app_object")); 43  } 44          if (EtwTrace.IsTraceEnabled(5, 1)) 45  { 46              EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, context.WorkerRequest, applicationInstance.GetType().FullName, "Start"); 47  } 48          if (applicationInstance is IHttpAsyncHandler) 49  { 50              IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance; 51              context.AsyncAppHandler = handler2; 52              handler2.BeginProcessRequest(context, this._handlerCompletionCallback, context);//届时 HttpApplication处理请求 
53  } 54          else 
55  { 56  applicationInstance.ProcessRequest(context); 57              this.FinishRequest(context.WorkerRequest, context, null); 58  } 59  } 60      catch (Exception exception) 61  { 62  context.Response.InitResponseWriter(); 63          this.FinishRequest(wr, context, exception); 64  } 65  }
复制代码

再看下GetApplicationInstance(context) 实例化Application的方法

View Code

 

最后调用的GetNormalApplicationInstance方法中对当前空闲的application数目进行判断,调用

application.InitInternal(context, this._state, this._eventHandlerMethods)方法,

this.InitModules()初始化所有的Modules,包含用户自定义的HttpModules

this._stepManager.BuildSteps(this._resumeStepsWaitCallback);//管道事件序列

贴一下源码:

复制代码
 1 internal override void BuildSteps(WaitCallback stepCallback)  2 {  3     ArrayList steps = new ArrayList();  4     HttpApplication app = base._application;  5     bool flag = false;  6     UrlMappingsSection urlMappings = RuntimeConfig.GetConfig().UrlMappings;  7     flag = urlMappings.IsEnabled && (urlMappings.UrlMappings.Count > 0);  8     steps.Add(new HttpApplication.ValidatePathExecutionStep(app));  9     if (flag) 10  { 11         steps.Add(new HttpApplication.UrlMappingsExecutionStep(app)); 12  } 13  app.CreateEventExecutionSteps(HttpApplication.EventBeginRequest, steps); 14  app.CreateEventExecutionSteps(HttpApplication.EventAuthenticateRequest, steps); 15  app.CreateEventExecutionSteps(HttpApplication.EventDefaultAuthentication, steps); 16  app.CreateEventExecutionSteps(HttpApplication.EventPostAuthenticateRequest, steps); 17  app.CreateEventExecutionSteps(HttpApplication.EventAuthorizeRequest, steps); 18  app.CreateEventExecutionSteps(HttpApplication.EventPostAuthorizeRequest, steps); 19  app.CreateEventExecutionSteps(HttpApplication.EventResolveRequestCache, steps); 20  app.CreateEventExecutionSteps(HttpApplication.EventPostResolveRequestCache, steps); 21     steps.Add(new HttpApplication.MapHandlerExecutionStep(app)); 22  app.CreateEventExecutionSteps(HttpApplication.EventPostMapRequestHandler, steps); 23  app.CreateEventExecutionSteps(HttpApplication.EventAcquireRequestState, steps); 24  app.CreateEventExecutionSteps(HttpApplication.EventPostAcquireRequestState, steps); 25  app.CreateEventExecutionSteps(HttpApplication.EventPreRequestHandlerExecute, steps); 26     steps.Add(new HttpApplication.CallHandlerExecutionStep(app)); 27  app.CreateEventExecutionSteps(HttpApplication.EventPostRequestHandlerExecute, steps); 28  app.CreateEventExecutionSteps(HttpApplication.EventReleaseRequestState, steps); 29  app.CreateEventExecutionSteps(HttpApplication.EventPostReleaseRequestState, steps); 30     steps.Add(new HttpApplication.CallFilterExecutionStep(app)); 31  app.CreateEventExecutionSteps(HttpApplication.EventUpdateRequestCache, steps); 32  app.CreateEventExecutionSteps(HttpApplication.EventPostUpdateRequestCache, steps); 33     this._endRequestStepIndex = steps.Count; 34  app.CreateEventExecutionSteps(HttpApplication.EventEndRequest, steps); 35     steps.Add(new HttpApplication.NoopExecutionStep()); 36     this._execSteps = new HttpApplication.IExecutionStep[steps.Count]; 37     steps.CopyTo(this._execSteps); 38     this._resumeStepsWaitCallback = stepCallback; 39 }
复制代码

到这里想必能够使大家对ASP.NET管道机制能够有一个简单的回顾.当然还有很多地方没有详细分析.

 

再来总结一下IIS运行过程及ASP.NET管道机制:

Request→ (Internet )  HTTP.sys 监听 → WAS (IIS6 web Admin Service /IIS7 (Windows Activation Service) 接收请求

→(传入)Application Pool’s → w3wp.exe(检查URL后缀)

→(加载)ISAPI扩展[aspnet_isapi.dll] → 注册映射

构造HttpRuntime类 →ProcessRequest方法

HttpContext实例产生(Request,Response,Session  and so on…)

HttpRuntime 调用 HttpApplicationFactory加载HttpApplication对象

穿越HttpModule到达HttpHandler

简单用140个字符(即一条微博的字数微笑)概括:

Request→ (Internet ) HTTP.sys →(WAS)→Application Pool’s → w3wp.exe→ISAPI→ Map→ (Pipeline)HttpWorkerRequest→AppDomain→HttpRuntime→ProcessRequest()→ HttpContext(Request,Response)→ HttpRuntime→HttpApplicationFactory→HttpApplication→ HttpModule→HttpHandler→EndRequest

以上为个人学习摘要,如有错误,欢迎指正!!

补充

1:刚刚看到dudu发的一个闪存,里面提到了Application pool 与 AppDomain 的区别 来自stackoverflow,希望对大家有所帮助.

2:WAS缩写在IIS6中的指的是(Web Admin Service),在IIS7中指的是(Windows Activation Service)  缩写一样.这个在写文章的时候注意到过,但是没有深入考虑. 理解不是很到位.  暂不妄下断言.  欢迎斧正!! 🙂

参考资料:

http://www.cnblogs.com/tenghoo/archive/2009/11/04/IIS_And_ASPNET_Http_Runtime_Pipeline.html

http://msdn.microsoft.com/en-us/library/system.web.httpapplication(v=vs.80).aspx

http://www.cnblogs.com/panchunting/archive/2013/04/11/ASPNET_Architecture.html

后续

由于一些个人原因,放弃了原本在无锡相对安逸的工作,踏上了北漂之路,也希望在大环境下能够得到更多的锻炼与打磨.

当然,最近也正在找工作,如果您有合适的职位,烦请介绍一下,感谢之至!!

如果你觉得本文对您有所帮助,请点击一下推荐,感谢您的阅读!!红玫瑰

作者:wenthink
出处:http://www.cnblogs.com/wenthink/archive/2013/05/06/HTTP_IIS_ASPNET_Pipeline.html
本文版权归【wenthink】和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
我们每个人都是梦想家,当离开家了,就只剩梦想了。。。 道路,或许坎坷,但你终会到达!!!

[转载]Jquery实现中国地图插件 - 漫画之迷 - 博客园

mikel阅读(847)

[转载]原创Jquery实现中国地图插件 – 漫画之迷 – 博客园.

,此Jquery插件是一款原创作品,实现中国地图提示特效,现在只是第一个版本,会不断的完善,插件使用非常灵活,引入了JQuery.adamMap.1.0库之后在页面初始化一下插件代码即可。

 

JQuery插件初始化代码如下:

复制代码
AMAP.making( { img : "AMAP",//图片id
        imgWidth : 385,//图片宽
        imgHeight : 305,//图片高
        points : aPoint,// 坐标列表 aPoint
        fill : "#44ccee",//填充颜色
        fillOpacity : .5,//透明度
        strokeFill : "#00AADD",//边框颜色
        strokeWidth : 1,//边框宽度
        event : "mouseover",//绑定事件 
        def : "重庆"//是默认城市 
 } );
复制代码

 

页面使用代码如下:

<!-- 地图功能结构 -->
<img src="map.jpg" height="305" width="385" id="AMAP" usemap="#Adam01" style="border:0;" />
<!-- 地图功能结构 结束 -->

 

效果如下:

网友Adam分享原创Jquery实现中国地图插件

 

在线演示

[转载]Asp.net MVC 插件式应用框架 - Mobwiz - 博客园

mikel阅读(1051)

[转载]Asp.net MVC 插件式应用框架 – Mobwiz – 博客园.

这几年来做了很多个网站系统,一直坚持使用ASP.NET mvc建站,每次都从头开始做Layout,CSS,做权限管理等等,有点恼火,将这几年的代码好好整理了一下,准备搭建一个框架。

一、目标

0)面向领域:网站后台管理系统、MIS等结构相对规范的管理类应用;

a)基于ASP.NET MVC 4搭建系统框架

b)集成RBAC基于角色的权限管理机制,能够控制到控件级别

c)集成一些基础服务:数据字典、日志记录等

d)使用AJAX特性(使用JQuery与MVC的Partiview等实现)

e)插件机制:框架搭建完毕之后,新开发的功能模块能够直接以插件的形式安装到系统框架中运行

f)使用EF Codefirst

二、系统设计

A)基于RBAC的权限管理系统

参考了园子里一些权限管理系统,使用了  模块->资源->操作 作为最基础的权限元素,[资源,操作]作为 基本的权限原子,将权限原子赋给“角色”

为了配合RBAC的使用,继承 AuthorizeAttribute 来修饰Action

B)菜单框架

此应用框架主要面向管理系统,所以导航菜单设计为两级,顶部导航为一级菜单,左侧 边为二级菜单,菜单能与权限进行关联,配置权限时可影响到菜单;如图所示,Menu2根据所选择Menu1发生变化;

C)Ajax框架

此应用框架中,Content Area为Ajax更新内容,使用JQuery的 $.get 操作访问 Action,在Action中返回 PartialView,然后更新到 ContentArea中;

全部使用Ajax来载入,带来一个问题,无法使用前进、后退、还有刷新等操作,借鉴网上一些资料,使用了 “jQuery.bbq.js”的插件,通过url的hashchange使系统能够在浏览器中保持状态,能够使用 “前进“,”后退“,”刷新“等操作;通过将真实的请求代码加入到URL”#”后,来载入所需要的内容,JS代码如下:

复制代码
$(window).bind('hashchange', function (e) { var hash = window.location.hash || ''; if (oldHash == '') { // 需要选择默认菜单
 } if (hash != '' && hash != oldHash) { if (hash == "#top") { //TODO, Load the index page!!!!
                    return; }  $('#contentPanel').html('<div class="wp"><img src="@Url.Content("~/Content/loading.gif")" border="0" /> &nbsp;载入中...</div>'); oldHash = hash; $.get(hash.substr(1, hash.length - 1), {}, function (data, status) { if (status == "success") {                         if (data.substr(0, 7) == '"logon"') { window.location = "/Home/Login"; } else { $('#contentPanel').html(data); } //}
                    } else { $('#contentPanel').html('error.. - ' + status); } }, "html"); } }); $(window).trigger('hashchange');
复制代码

 

在这样的框架中,只需要关注Content Area中的部分视图即可,在该Content Area中还可以使用Ajax + JSON的方式实现更多的功能;

表单的处理

由于采用了上面的AJAX机制,不能再使用以往的表单提交方式,在本框架中,使用下面几种方式:

1)使用  $.post(url, $(‘#form’).serialize(), callback, “html”) 的方式,将返回值更新到 Content Area中;

2)使用  $.post(url, {‘field1′,’value1’} , callback, “json”) 的方式,根据结果,再通过JavaScript进行页面跳转;

D)配套的若干Helper

ajaxMenu,ajaxPager等:由于使用了上面的机制来载入部分视图,所有的URL都要以 /Home#/Real/Url?para1=value1 的方式来请求,所以写了一个Helper来完成该功能;分页也需要做同样的处理。

E)IoC Container

本例使用了Unity Block作为IoC容器,无他,因为这个比较熟悉;同时将MVC与Unity Container做了整合;

F)表单验证

由于使用了AJAX框架,unobtrusive扩展的验证大部分失效,所以直接使用 jQuery.validate进行客户端验证

G)插件机制

定义IPlugin接口,其中有Initialize方法,在系统启动时进行初始化,例如初始化Unity Container,MapRoute等等;

插件的使用:直接将插件复制到框架中的插件目录,并将插件所需要使用的Views文件复制到框架中的Views目录中。(暂无更好的解决方案,就这样搞定。考虑复制到插件目录,重写MVC中的视图引擎的代码来实现在插件目录中查找视图)

工作原理:系统启动时,通过反射载入插件中的Dll,并查找IPlugin接口,调用其初始化方法,插件在Unity容器中注册所需要使用的控制器,这样框架中就可以调用到所需要的控制器以及视图;

插件格式:插件也采用ASP.NET mvc 4 Web APP来写,最后只使用编译出的DLL与部分必要的View视图,其他资源均使用框架中的资源;

三、系统实现

1)数据库:采用了EntityFramework(Version 5.0) – CodeFirst完成,使用Enable Migrations;

2)数据访问层:定义仓储模式,IRepository实现基本的增删查改功能,

3)数据服务层:根据需要进行定义;

4)Portal:采用了ASP.NET MVC 4,Razor视图引擎,JQuery,JQuery UI, JQuery.Validate, jquery.bbq, Jquery BlockUI 等;

5)为面向内容管理,集成了CkEditor与CkFinder

——————-

目前系统代码还比较乱,先发个截图

 

[转载]尝试MVP模式 - 猴健居士 - 博客园

mikel阅读(889)

[转载]尝试MVP模式 – 猴健居士 – 博客园.

对MVP模式的接触,是我偶然一次在百度上搜MVC的时候开始,当时对MVC都不了解,甭说MVP了。后来MVC弄懂了,现在就来了解一下MVP。

MVP 是从经典的模式MVC演变而来的,难怪看那个结构图有点相像。

MVC模式的结构图,M,V,C各代表什么不说了

MVP模式的结构图,M和V的含义跟MVC中的结构一样,区别的就是C(Controller)和P(Presenter)。感觉这个区别就导 致了模式产生性质的变化。至少从几何角度来看,由一个稳定的三角型变成一条直线。在MVC中即使在Controller对View和Model的控制之 下,View和Model之间仍然有联系,至少View上控件绑定的数据是与Model的某个字段有关的。不过在MVP中Presenter则把原本 MVC中View与Model的联系砍断了,View上面那个控件绑定什么数据它本身不知道,Presenter才知道。这样View只是负责呈现部分, 使得它的职责更单一了。再者Presenter不是调用View本身,而是调用一个由View实现的接口,这样使得View与Presenter的联系更 松散了。这么说来,整个MVP模式中的成员一共有四个

  •   View(视图):实现IVew接口,负责界面呈现。
  •   IView(视图接口):提供一些方法,属性供展示器调用获取,从而得知视图的状态或某些信息或对视图进行某些操作,同时也外放了一些方法供展示器注册,使得视图能在需要的时刻对展示器发出某些请求。
  •   Presenter(展示器):整个MVP模式的核心,负责对视图的操作,数据的绑定,必要时响应来自视图的请求,在有需要的时候会借助模型完成一些业务。
  •   Model(模型):完成整个模式中必要的业务逻辑。

浏览了一些园友的博文后,我也尝试实现了一个MVP模式。项目的结构如下图。

从上图可以很明显的看出MVP的三部分,另外Common目录下存放的主要是MVP模式里的一些基类,接口等等,本项目还使用了一个轻量级的 Ioc框架Ninject,为了尽量改动Common里的类,使用Ninject时要绑定的接口是实现类以配置的形式来实现,配置的信息就存放在 BindingConfig.xml文件里面。

看一下Common里面包含的类

IocContainer.cs

Ioc的容器

IView.cs

视图接口的基接口

MyEventArgs.cs

扩展了事件和委托的参数

PresenterBase.cs

所有展示器的基类

PresenterManager.cs

通过展示器展示其视图

WinFormInjectModule .cs

Ioc的接口与实现类的绑定

由于对Ninject还不是很熟悉,对它的用法解释不了太多

IocContainer的定义如下

复制代码
 1     public class IocContainer  2  {  3         private static IKernel _kernel;  4 
 5         public static IKernel Container  6  {  7             get
 8  {  9                 if (_kernel == null) 10                     _kernel = new StandardKernel(new WinFormInjectModule()); 11                 return _kernel; 12  } 13  } 14     }
复制代码

  这里用到了WinFormInjectModule类,它继承了NinjectModule,里面就重写了Load方法实现绑定,由于这里的绑定时通过配置实现的,所以这里还涉及到读取和分析配置信息

复制代码
 1     public class WinFormInjectModule : Ninject.Modules.NinjectModule  2  {  3         public override void Load()  4  {  5 
 6             List<Tuple<string, string>> bindingList = GetBindingConfig();  7  Type bindType,toType;  8             foreach (Tuple<string,string> item in bindingList)  9  { 10                 bindType=Type.GetType(item.Item1); 11                 if (item.Item2.Length == 0) 12  { 13  Bind(bindType).ToSelf(); 14                     continue; 15  } 16                 toType = Type.GetType(item.Item2); 17  Bind(bindType).To(toType); 18  } 19  } 20 
21         private List<Tuple<string, string>> GetBindingConfig() 22  { 23             List<Tuple<string, string>> result = new List<Tuple<string, string>>(); 24             XmlDocument xmlDoc = new XmlDocument(); 25             if (!File.Exists("BindingConfig.xml")) throw new IOException("BindingConfig.xml 不存在"); 26             xmlDoc.Load("BindingConfig.xml"); 27             XmlNodeList nodelist = xmlDoc.SelectNodes("//BindingSetting/Binding"); 28             string bind,to; 29             foreach (XmlNode node in nodelist) 30  { 31                 bind=string.Empty; 32                 to=string.Empty; 33                 bind = node.Attributes["bind"].Value; 34                 if (node.Attributes["to"] != null) to = node.Attributes["to"].Value; 35                 result.Add(new Tuple<string, string>(bind,to)); 36  } 37             return result; 38  } 39     }
复制代码

配置的定义如下

1 <BindingSetting>
2   <Binding bind="TestMVP.Model.IUser" to="TestMVP.Model.UserModel"/>
3   <Binding bind="TestMVP.View.ILoginView" to="TestMVP.View.LoginView"/>
4   <Binding bind="TestMVP.Presenter.LoginPresenter"/>
5 </BindingSetting>

bind属性就是要绑定的类或者接口,to就是绑定到的类,如果只是绑定自己的话就在bind属性填类名则可,to不用填了。

展示器的基类定义如下

复制代码
 1     public class PresenterBase<T> where T : IView  2  {  3         private T _view;  4 
 5         public PresenterBase(T view)  6  {  7             this.View = view;  8  }  9 
10         public T View 11  { 12             get { return _view; } 13             set { _view = value; } 14  } 15     }
复制代码

以接口的形式对视图进行访问的话,就可以避免直接访问视图的实例,减少了对视图的依赖。

考虑到在展示器里打开别的展示器管理的视图时,原本可以构造一个展示器实例,然后获取其视图进行展示,可是在一个展示器里构造另一个展示器,这样的做法好像不妥,于是定义了一个类专门用于打开别的视图用的。

当要打开某个视图(也就是窗体)时,就可以调用PresenterManager的静态方法

复制代码
 1     public class PresenterManager  2  {  3         public static void ShowView(string presenterName,FormAction formAction)  4  {  5             Type type = Type.GetType("TestMVP.Presenter." + presenterName);  6             object p = Common.IocContainer.Container.GetService(type);  7             System.Windows.Forms.Form frm = type.GetProperty("View").GetValue(p, null) as System.Windows.Forms.Form;  8             switch (formAction)  9  { 10                 case FormAction.Run: System.Windows.Forms.Application.Run(frm); 11                     break; 12                 case FormAction.Show: frm.Show(); 13                     break; 14                 case FormAction.ShowDialog: frm.ShowDialog(); 15                     break; 16                 default: 17                     break; 18  } 19  } 20 
21         public static void ShowView(string presenterName) 22  { 23  ShowView(presenterName, FormAction.Show); 24  } 25  } 26 
27     public enum FormAction { Run,Show,ShowDialog }
复制代码

 

下面则做一个简单的Demo,是登录功能的

首先是模型的,先定义了一个IUser接口,届时展示器想调用模型的方法是就通过这个接口来调用,免除了对模型其他成员的访问

1     public interface IUser 2  { 3         bool CheckLogin(string user, string password); 4     }

再由一个IModelUser实现这个接口

复制代码
1     public class UserModel:IUser 2  { 3         public bool CheckLogin(string user, string password) 4  { 5             if (user == "admin" && password == "123456") 6                 return true; 7             return false; 8  } 9     }
复制代码

接着到展示器

复制代码
 1     public class LoginPresenter:PresenterBase<ILoginView>
 2  {  3  [Inject]  4         public IUser UserModel { set; get; }  5 
 6         public LoginPresenter(ILoginView view):base(view)  7  {  8             this.View = view;  9             this.View.OnLogin += new MyEventHandler(View_OnLogin); 10  } 11 
12         void View_OnLogin(object sender, MyEventArgs e) 13  { 14             bool result =  UserModel.CheckLogin(this.View.IDBoxText, this.View.PasswordBoxText); 15             e.OptionResult=result; 16             if(result) 17  { 18                 PresenterManager.ShowView("SystemPresenter"); 19                 (sender as Form).Hide(); 20  } 21  } 22     }
复制代码

在构造展示器实例时,给视图的事件绑定一个方法,相应登录视图的登录验证请求,在改方法内调用模型的方法验证用户名密码,把结果通过委托的参数传递给视图。如果验证通过了就隐藏登录视图,显示主界面。

 

最后到视图

复制代码
 1     public interface ILoginView:IView  2  {  3         event MyEventHandler OnLogin;  4         string IDBoxText { get; set; }  5 
 6         string PasswordBoxText { get; set; }  7  }  8 
 9     public partial class LoginView : Form,ILoginView 10  { 11         public LoginView() 12  { 13  InitializeComponent(); 14  } 15 
16         private void button1_Click(object sender, EventArgs e) 17  { 18             MyEventArgs args=new MyEventArgs(); 19             if (OnLogin != null) OnLogin(this, args); 20             if (!args.OptionResult) 21                 MessageBox.Show("Fail"); 22  } 23 
24         public event MyEventHandler OnLogin; 25 
26         public string PasswordBoxText 27  { 28             get { return this.tbPw.Text; } 29             set { this.tbPw.Text = value; } 30  } 31 
32         public string IDBoxText 33  { 34             get { return this.tbID.Text; } 35             set { tbID.Text = value; } 36  } 37     }
复制代码

视图这里ILoginView是继承了IView接口,里面声明了登录视图应该外放的事件和属性,那登录界面来说

虽然很明显看得出ID后的输入框的值是用户ID,Password后面的输入框的值是用户密码,但是这些对于一个视图来说都是不知其含义的,知 道含义的是展示器,视图只是把值外放出去给展示器获取。正如一位园友说的,视图就该尽量吧控件多外放出去。不过我觉得某些简单的界面逻辑还是放在视图上比 较好,例如单击了某个按钮使得另一个输入框变灰之类的。

 

这样就牵强地使用了一下MVP模式,有位园友在讨论MVC时说过,没发挥到MVC的优势时干脆用回以前的WebForm,MVP也一样吧,期待 能真正用上它的时候。由于最近都是从事C/S的开发,对C/S比较熟悉,做的这个小尝试也是用WinForm的,但转到WebForm上估计也不难,展示 器管理那里要更改一下。

[转载]25个增强iOS应用程序性能的提示和技巧 - easonoutlook - 博客园

mikel阅读(987)

[转载]25个增强iOS应用程序性能的提示和技巧 – easonoutlook – 博客园.

在开发iOS应用程序时,让程序具有良好的性能是非常关键的。这也是用户所期望的,如果你的程序运行迟钝或缓慢,会招致用户的差评。然而由于iOS设备的局限性,有时候要想获得良好的性能,是很困难的。在开发过程中,有许多事项需要记住,并且关于性能影响很容易就忘记。

本文收集了25个关于可以提升程序性能的提示和技巧,把性能优化技巧分为3个不同的等级:初级、中级和高级

初级

在开发过程中,下面这些初级技巧需要时刻注意:

1.使用ARC进行内存管理
2.在适当的情况下使用reuseIdentifier
3.尽可能将View设置为不透明(Opaque)
4.避免臃肿的XIBs
5.不要阻塞主线程

6.让图片的大小跟UIImageView一样
7.选择正确的集合
8.使用GZIP压缩

1) 使用ARC进行内存管理

ARC是在iOS 5中发布的,它解决了最常见的内存泄露问题——也是开发者最容易健忘的。ARC的全称是“Automatic Reference Counting”——自动引用计数,它会自动的在代码中做retain/release工作,开发者不用再手动处理。

下面是创建一个View通用的一些代码块:

  1. UIView *view = [[UIView alloc] init]; 
  2. // … 
  3. [self.view addSubview:view]; 
  4. [view release]; 

在上面代码结束的地方很容易会忘记调用release。不过当使用ARC时,ARC会在后台自动的帮你调用release。

ARC除了能避免内存泄露外,还有助于程序性能的提升:当程序中的对象不再需要的时候,ARC会自动销毁对象。所以,你应该在工程中使用ARC。

下面是学习ARC的一些资源:

苹果的官方文档
Matthijs Hollemans的初级ARC
Tony Dahbura的如何在Cocos2D 2.X工程中使用ARC
如果你仍然不确定ARC带来的好处,那么看一些这篇文章:8个关于ARC的神话——这能够让你相信你应该在工程中使用ARC!
值得注意的是,ARC并不能避免所有的内存泄露。使用ARC之后,工程中可能还会有内存泄露,不过引起这些内存泄露的主要原因是:block,retain循环,对CoreFoundation对象(通常是C结构)管理不善,以及真的是代码没写好。
这里有一篇文章是介绍哪些问题是ARC不能解决的 — 以及如何处理这些问题。

2) 在适当的情况下使用reuseIdentifier

在适当的情况使用reuseIdentifier
在iOS程序开发中一个普遍性的错误就是没有正确的为UITableViewCells、UICollectionViewCells和UITableViewHeaderFooterViews设置reuseIdentifier。

为了获得最佳性能,当在tableView:cellForRowAtIndexPath:方法中返回cell时,table view的数据源一般会重用UITableViewCell对象。table view维护着UITableViewCell对象的一个队列或者列表,这些数据源已经被标记为重用了。

如果没有使用reuseIdentifier会发生什么?如果你在程序中没有使用reuseIdentifier,table view每次显示一个row时,都会配置一个全新的cell。这其实是一个非常消耗资源的操作,并且会影响程序中table view滚动的效率。

自iOS 6以来,你可能还希望header和footer views,以及UICollectionView的cell和supplementary views。

为了使用reuseIdentifiers,在table view请求一个新的cell时,在数据源中调用下面的方法:

  1. static NSString *CellIdentifier = @“Cell”
  2. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; 

如果table view维护的UITableViewCell队列或列表中有可用的cell,则从队列从移除一个已经存在的cell,如果没有的话,就从之前注册的 nib文件或类中创建一个新的cell。如果没有可以重用的cell,并且没有注册nib文件或类,tableview的 dequeueReusableCellWithIdentifier:方法会返回一个nil。
3) 尽可能将View设置为不透明(Opaque)

http://s1.51cto.com/wyfs01/M01/07/01/wKioOVFs7KXCEIf9AAAs6bKr60Q336.jpg
尽量将view设置为Opaque

如果view是不透明的,那么应该将其opaque属性设置为YES。为什么要这样做呢?这样设置可以让系统以最优的方式来绘制view。opaque属性可以在Interface Builder或代码中设置。

苹果的官方文档对opaque属性有如下解释:

This property provides a hint to the drawing system as to how it should treat the view. If set to YES, the drawing system treats the view as fully opaque, which allows the drawing system to optimize some drawing operations and improve performance. If set to NO, the drawing system composites the view normally with other content. The default value of this property is YES.

(opaque属性提示绘制系统如何处理view。如果opaque设置为YES,绘图系统会将view看为完全不透明,这样绘图系统就可以优化一些绘制操作以提升性能。如果设置为NO,那么绘图系统结合其它内容来处理view。默认情况下,这个属性是YES。)

如果屏幕是静止的,那么这个opaque属性的设置与否不是一个大问题。但是,如果view是嵌入到scroll view中的,或者是复杂动画的一部分,不将设置这个属性的话肯定会影响程序的性能!可以通过模拟器的Debug\Color Blended Layers选项来查看哪些view没有设置为不透明。为了程序的性能,尽可能的将view设置为不透明!

4) 避免臃肿的XIBs

避免臃肿的XIB

在iOS 5中开始使用Storyboards,并且将替代XIBs。不过在有些情况下XIBs仍然有用。如果你的程序需要运行在装有iOS 5之前版本的设备上,或者要自定义可重用的view,那么是避免不了要使用XIBs的。

如果必须要使用XIBs的话,尽量让XIBs文件简单。并且每个view controller对于一个XIB文件,如果可以的话,把一个view controller的view不同的层次单独分到一个XIBs文件中。

(注意:当把一个XIB文件加载到内存时,XIB文件中的所有内容都将被加载到内存中,包括图片。如果有一个view还不立即使用的话,就会造成内 存的浪费。而这在storyboard中是不会发生的,因为storyboard还在需要的时候才实例化一个view controller。)

当加载XIB时,所有涉及到的图片都将被缓存,并且如果是开发的程序是针对OS X的话,声音文件也会被加载。苹果的官方文档这样说:

When you load a nib file that contains references to image or sound resources, the nib-loading code reads the actual image or sound file into memory and and caches it. In OS X, image and sound resources are stored in named caches so that you can access them later if needed. In iOS, only image resources are stored in named caches. To access images, you use the imageNamed: method of NSImage or UIImage, depending on your platform.

(当加载一个nib文件时,也会将nib文件涉及到的图片或声音资源加载到内存中,nib-loading代码会将实际的图片或声音文件读取到内存 中,并一直缓存着。在OS X中,图片和声音资源都存储在命名缓存中,这样之后如果需要的话,可以对其进行访问。在iOS中,只有图片资源被存储到命名缓存中。要访问图片的话,使用 NSImage或UIImage(根据不同的系统)的imageNamed:方法即可。)

显然,在使用storyboard时也会发生类似的缓存操作;不过我没有找到相关内容的任何资料。想要学习storyboard的更多知识吗?可以看看Matthijs Hollemans写的iOS 5中:初级Storyboard Part 1Part2

5) 不要阻塞主线程

永远都不要在主线程做繁重的任务。因为UIKit的左右任务都在主线程中进行,例如绘制、触摸管理和输入响应。

在主线程做所有任务的风险是:如果你的代码阻塞了主线程,那么程序将出现反应迟钝。这回招致用户在App Store上对程序的差评!

在执行I/O操作中,大多数情况下都会祖塞主线程,这些操作需要从读写外部资源,例如磁盘或者网络。

关于网络操作可以使用NSURLConnection的如下方法,以异步的方式来执行:

  1. + (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler 

或者使用第三方框架,例如AFNetworking

如果你需要做一些其它类型开销很大的操作(例如执行一个时间密集型的计算或者对磁盘进行读写),那么就使用GCD(Grand Central Dispatch),或NSOperations 和 NSOperationQueues。

下面的代码是使用GCD的一个模板:

  1. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
  2.     // switch to a background thread and perform your expensive operation 
  3.     dispatch_async(dispatch_get_main_queue(), ^{ 
  4.         // switch back to the main thread to update your UI 
  5.     }); 
  6. }); 

 

如上代码,为什么在第一个dispatch_async里面还嵌套了一个dispatch_async呢?这是因为关于UIKit相关的代码需要在主线程里面执行。

可以看看Ray Wenderlich中的教程:iOS中多线程和GCD—初级,以及Soheil Azarpour的如何使用NSOperations和NSOperationQueues教程

6) 让图片的大小跟UIImageView一样

http://s2.51cto.com/wyfs01/M01/07/01/wKioOVFs7LDytOxKAAAdyOKIZVg798.jpg1
确保图片和UIImageView大小一致
如果需要将程序bundle中的图片显示到UIImageView中,请确保图片和UIImageView的大小是一样的。因为图片的缩放非常耗费资源,特别是将UIImageView嵌入到UIScrollView中。

如果是从远程服务中下载图片,有时候你控制不了图片的尺寸,或者在下载之前无法在服务器上进行图片的缩放。这种情况,当图片下载完之后,你可以手动进行图片的缩放——做好是在后台线程中!——然后再在UIImageView中使用缩放过的图片。
7) 选择正确的集合

选择正确的集合
学习使用最适合的类或对象是编写高效代码的基础。特别是在处理集合数据时,尤为重要。
苹果的官网上有一篇文章:集合编程主题(Collections Programming Topics)——详细的介绍了在集合数据中可以使用的类,以及什么情况下使用哪个类。在使用集合时,每个开发者都应该阅读一下这个文档。
太长,不想阅读(TLDR)?下面是常见集合类型的一个简介:
•数组:是一个值按顺序排列的一个列表。根据索引可以快速查找,不过根据值进行查找就比较慢,另外插入和删除也比较慢。
•字典:  存储键/值对。根据键可以快速查找。
•Sets:  是一个值无序排列的列表,根据值可以快速查找,另外插入和删除也比较快。

8) 使用GZIP压缩

使用GZIP压缩
越来越多的程序依赖于外部数据,这些数据一般来自远程服务器或者其它的外部APIs。有时候你需要开发一个程序来下载一些数据,这些数据可以是XML,JSON,HTML或者其它一些文本格式。

问题是在移动设备上的网络是不确定的。用户的设备可能在EDGE网络一分钟,然后接着又在3G网络中。不管在什么情况下,都不要让用户等待。

有一个可以优化的选择:使用GZIP对网络传输中的数据进行压缩,这样可以减小文件的大小,并加快下载的速度。压缩对于文本数据特别有用,因为文本具有很高的压缩比。

iOS中,如果使用NSURLConnection,那么默认情况下已经支持GZIP压缩了,并且基于NSURLConnection的框架页支持GZIP压缩,如AFNetworking。甚至有些云服务提供商已经提供发送经压缩过的响应内容,例如 Google App Engine

这里有一篇关于GZIP压缩很好的文章,介绍了如何在Apache活IIS服务器中开启支持GZIP压缩。

在性能优化时,当你碰到一些复杂的问题,应该注意和使用如下技巧:

9.重用和延迟加载View
10.缓存、缓存、缓存
11.考虑绘制
12.处理内存警告
13.重用花销很大的对象
14.使用Sprite Sheets
15.避免重新处理数据
16.选择正确的数据格式
17.设置适当的背景图片
18.降低Web内容的影响
19.设置阴影路径
20.优化TableView
21.选择正确的数据存储方式

中级性能提升
现在,在进行代码优化时,你已经能够完成一些初级性能优化了。但是下面还有另外一些优化方案,虽然可能不太明显(取决于程序的架构和相关代码),但是,如果能够正确的利用好这些方案,那么它们对性能的优化将非常明显!
9) 重用和延迟加载View

程序界面中包含更多的view,意味着界面在显示的时候,需要进行更多的绘制任务;也就意味着需要消耗更多的CPU和内存资源。特别是在一个UIScrollView里面加入了许多view。

这种情况的管理技巧可以参考UITableView和UICollectionView的行为:不要一次性创建所有的subview,而是在需要的时候在创建view,并且当view使用完毕时候将它们添加到重用队列中。

这样就可以仅在UIScrollView滚动的时候才配置view,以此可以避免分配创建view的带来的成本——这可能是非常耗资源的。

现在有这样的一个问题:在程序中需要显示的view在什么时机创建(比如说,当用户点击某个按钮,需要显示某个view)。这里有两种可选方法:

在屏幕第一次加载以及隐藏的时候,创建view;然后在需要的时候,再把view显示出来。
直到需要显示view的时候,才创建并显示view。
每 种方法都有各自的优点和缺点。第一种方法需要消耗更多的内容,因为创建出来的view一直占据着内存,直到view被release掉。不过,使用这种 方法,当用户点击按钮时,程序会很快的显示出view,因为只需要修改一下view的可见性即可。而第二种方法则产生相反的效果;当需要的时候猜创建 view,这会消耗更少的内存;不过,当用户点击按钮的时候,不会立即显示出view。
10) 缓存、缓存、缓存

在开发程序时,一个重要的规则就是“缓存重要的内容”——这些内容一般不会改变,并且访问的频率比较高。

可以缓存写什么内容呢?比如远程服务器的响应内容,图片,甚至是计算结果,比如UITableView的行高。

NSURLConnection根据HTTP头的处理过程,已经把一些资源缓存到磁盘和内存中了。你甚至可以手动创建一个NSURLRequest ,让其只加载缓存的值。

下面的代码片段一般用在为图片创建一个NSURLRequest:

  1. + (NSMutableURLRequest *)imageRequestWithURL:(NSURL *)url { 
  2.     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; 
  3.     request.cachePolicy = NSURLRequestReturnCacheDataElseLoad; // this will make sure the request always returns the cached image 
  4.     request.HTTPShouldHandleCookies = NO; 
  5.     request.HTTPShouldUsePipelining = YES; 
  6.     [request addValue:@“image/*” forHTTPHeaderField:@“Accept”]; 
  7.     return request; 

注意:你可以使用NSURLConnection抓取一个URL请求,但是同样可以使用AFNetworking来抓取,这种方法不用修改所有网络相关的代码——这是一个技巧!

更多关于HTTP 缓存, NSURLCache, NSURLConnection 以及相关的内容, 那么看一下NSHipster中的the NSURLCache entry

如果你需要缓存的内容没涉及到HTTP请求,那使用NSCache。NSCache的外观和行为与NSDictionary类似, 但是,当系统需要回收内存时,NSCache会自动的里面存储的内容。Mattt Thompson 在NSHipster上写了一篇关于NSCache非常不错的文章

更多关于HTTP缓存的内容,建议读一下Google的这篇文章:best-practices document on HTTP caching
11) 考虑绘制

在iOS中制作漂亮的按钮有多种方法。可以使用全尺寸图片,可缩放图片,或使用CALayer, CoreGraphics, 甚至是OpenGL来手动测量和绘制按钮。

这些方法的复杂程度不同,性能也有区别。这篇关于iOS中图形性能的文章值得一读。其中Andy Matuschak(曾经是苹果的UIKit小组的组员)对这篇文章的评论中,对于不同的方法及其性能权衡有非常好的一个见解。

简单来说,使用预渲染图片技术是最快的,因为iOS中不用等到在屏幕上显示的时候才创建图形和对形状进行绘制(图片已经创建好了!)。这样带来的问 题是需要把所有的图片都放到程序bundle中,从而增加了程序的大小。因此使用可伸缩图片在这里将排上用场了:可以移除“浪费”空间的图片——iOS可 以重复利用。并且针对不同的元素(例如按钮)不需要创建不同的图片。

不过,使用图片的话会失去代码对图片的控制能力,进而针对不同的程序,就需要重复的生成每一个需要的图片,并反复的放到每个程序中。这个处理过程一 般会比较慢。另外一点就是如果你需要一个动画,或者许多图片都要进行轻微的调整(比如多个颜色的覆盖),那么需要在程序中加入许多图片,进而增加了程序 bundle的大小。

总的来说,要考虑一下什么才是最重要的:绘制性能还是程序大小。一般来说都重要,所以在同一个工程中,应该两种都应考虑。
12) 处理内存警告

当系统内存偏低时,iOS会通知所有在运行的程序。苹果的官方文档中介绍了如何处理低内存警告:

If your app receives this warning, it must free up as much memory as possible. The best way to do this is to remove strong references to caches, image objects, and other data objects that can be recreated later.

如果程序收到了低内存警告,在程序中必须尽量释放内存。最佳方法就是移除强引用的涉及到的缓存,图片对象,以及其它可以在之后使用时还可以重新创建的数据对象。

UIKit中提供了如下几种方法来接收低内存(low-memory)警告:

实现app delegate中的applicationDidReceiveMemoryWarning: 方法。
在UIViewController子类中重写(Override)didReceiveMemoryWarning方法。
在通知中心里面注册UIApplicationDidReceiveMemoryWarningNotificatio通知。
在收到以上任意的警告时,需要立即释放任何不需要的内存。

例如,UIViewController的默认情况是清除掉当前不可见的view;在UIViewController的子类中,可以清除一些额外的数据。程序中不没有显示在当前屏幕中的图片也可以release掉。

当收到低内存警告时,尽量释放内存是非常重要的。否则,运行中的程序有可能会被系统杀掉。

不过,在清除内存时要注意一下:确保被清除的对象之后还可以被创建出来。另外,在开发程序的时候,请使用iOS模拟器中的模拟内存警告功能对程序进行测试!

13) 重用花销很大的对象

http://s4.51cto.com/wyfs01/M01/07/02/wKioOVFs9ezTW4RpAAAwQF1sIqU235.jpg
有些对象的初始化非常慢——比如NSDateFormatter和NSCalendar。不过有时候可以避免使用这些对象,例如在解析JSON/XML中的日期时。

当使用这些对象时,为了避免性能上的瓶颈,可以尝试尽量重用这些对象——在类中添加一个属性或者创建一个静态变量。

注意,如果使用静态变量的话,对象会在程序运行的时候一直存在,就像单例一样。

下面的代码演示创建一个延迟加载的日期格式属性。第一次调用属性的时候,会创建一个新的日期格式。之后再调用的话,会返回已经创建好的实例对象:

  1. // in your .h or inside a class extension 
  2. @property (nonatomic, strong) NSDateFormatter *formatter; 
  3. // inside the implementation (.m) 
  4. // When you need, just use self.formatter 
  5. – (NSDateFormatter *)formatter { 
  6.     if (! _formatter) { 
  7.         _formatter = [[NSDateFormatter alloc] init]; 
  8.         _formatter.dateFormat = @“EEE MMM dd HH:mm:ss Z yyyy”// twitter date format 
  9.     } 
  10.     return _formatter; 

另外,还需要记住的是在设置NSDateFormatter的日期格式时,同样跟创建新的一个NSDateFormatter实例对象时一样慢!因此,在程序中如果需要频繁的处理日期格式,那么对NSDateFormatter进行重用是非常好的。

14) 使用Sprite Sheets

http://s1.51cto.com/wyfs01/M00/07/02/wKioJlFs9eyy6e-cAAA44XQc7Zg575.jpg
使用sprite sheets
你是一个游戏开发者吗?是的话那么sprite sheets是最佳选择之一。使用Sprite sheets跟常用的绘制方法比起来,绘制更快,并且消耗更少的内存。

下面是两个非常不错的sprite sheets教程:

如何在Cocos2D中使用动画和Sprite Sheets 

如何在Cocos2D中使用纹理包(Texture Packer)和像素格式来创建并优化Sprite Sheets  。(第二个教程详细的介绍了像素格式——在游戏中可以衡量性能的影响)

如果还不熟悉sprite sheets,可以看看这里的介绍:SpriteSheets – 视频, Part 1和 Part 2 。这两个视频的作者是Andreas Löw, 他是纹理包(Texture Packer)的创建者, 纹理包是创建sprite sheets的重要工具。

除了使用sprite sheets外,这里还介绍了一些用于游戏开发中的技巧,例如,如果你有很多sprite(比如射击类游戏中),那么可以重用sprite,而不用每次都创建sprite。
15) 避免重新处理数据

许多程序都需要从远程服务器中获取数据,以满足程序的需求。这些数据一般是JSON或XML格式。在请求和接收数据时,使用相同的数据结构非常重要。

为什么呢?在内存中把数据转换为适合程序的数据格式是需要付出额外代价的。

例如,如果你需要在table view中显示一些数据,那么请求和接收的数据格式最好是数组格式的,这样可以避免一些中间操作——将数据转换为适合程序使用的数据结构。

类似的,如果程序是根据键来访问具体的值,那么最好请求和接收一个键/值对字典。

在第一时间获得的数据就是所需要格式的,可以避免将数据转换为适合程序的数据格式带来的额外代价。
16) 选择正确的数据格式


选择正确的数据格式

将数据从程序传到网络服务器中有多种方法,其中使用的数据格式基本都是JSON和XML。你需要做的就是在程序中选择正确的数据格式。

JSON的解析速度非常快,并且要比XML小得多,也就意味着只需要传输更少数据。并且在iOS5之后,已经有内置的JSON反序列化API了,所以使用JSON是很容易的。

不过XML也有它自己的优势:如果使用SAX方法来解析XML,那么可以边读XML边解析,并不用等到全部的XML获取到了才开始解析,这与JSON是不同的。当处理大量数据时,这种方法可以提升性能并减少内存的消耗。
17) 设置适当的背景图片

在iOS编码中,跟别的许多东西类似,这里也有两种方法来给view设置一个背景图片:

1.可以使用UIColor的colorWithPatternImge方法来创建一个颜色,并将这个颜色设置为view的背景颜色。
2.可以给view添加一个UIImageView子视图。

如果你有一个全尺寸的背景图片,那么应该使用UIImageView,因为UIColor的colorWithPatternImge方法是用来创建小图片的——该图片会被重复使用。此时使用UIImageView会节省很多内存。

  1. // You could also achieve the same result in Interface Builder 
  2. UIImageView *backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@“background”]]; 
  3. [self.view addSubview:backgroundView]; 

不过,如果你计划用小图片当做背景,那么应该使用UIColor的colorWithPatternImge方法。这种情况下绘制速度会很快,并且不会消耗大量的内存。

  1. self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@“background”]]; 

 

18)降低Web、内容的影响

UIWebView非常有用。用它可以很容易的显示web内容,甚至可以构建UIKit空间难以显示的内容。

不过,你可以能已经注意到程序中使用的UIWebView组建没有苹果的Safari程序快。这是因为JIT编译限制了WebKit的Nitro引擎的使用。

因此为了获得更加的性能,需要调整一下HTML的大小。首先就是尽量的摆脱JavaScript,并避免使用大的矿建,例如JQuery。有时候使用原始的JavaScript要比别的框架快。

另外,尽量的异步加载JavaScript文件——特别是不直接影响到页面行为时,例如分析脚本。

最后——让使用到的图片,跟实际需要的一样大小。如之前提到的,尽量使用sprite sheets,以此节省内存和提升速度。

更多相关信息,可以看一下: WWDC 2012 session #601 – 在iOS中优化UIWebView和网站中的Web内容

19) 设置阴影路径

如果需要在view活layer中添加一个阴影,该如何处理呢?大多数开发者首先将QuartzCore框架添加到工程中,然后添加如下代码:

 

  1. #import &lt;QuartzCore/QuartzCore.h&gt; 
  2. // Somewhere later … 
  3. UIView *view = [[UIView alloc] init]; 
  4. // Setup the shadow … 
  5. view.layer.shadowOffset = CGSizeMake(-1.0f, 1.0f); 
  6. view.layer.shadowRadius = 5.0f; 
  7. view.layer.shadowOpacity = 0.6;

上面这种方法有一个问题,Core Animation在渲染阴影效果之前,必须通过做一个离屏(offscreen)才能确定view的形状,而这个离屏操作非常耗费资源。下面方法可以更容易地让系统进行阴影渲染:设置阴影路径!

 

  1. view.layer.shadowPath = [[UIBezierPath bezierPathWithRect:view.bounds] CGPath];

通过设置阴影路径,iOS就不用总是再计算该如何绘制阴影了。只需要使用你预先计算好的路径即可。有一点不好的是,根据view的格式,自己可能很难计算出路径。另外一个问题就是当view的frame改变时,必须每次都更新一下阴影路径。

如果你想了解更多相关信息,可参看Mark Pospesel的一篇文章:shadowPath
20) 优化TableView

Table views需要快速的滚动——如果不能的话,用户会感觉到停顿。为了让table view平滑的滚动,确保遵循了如下建议:

1.设置正确的reuseIdentifer以重用cell。
2.尽量将view设置为不透明,包括cell本身。
3.避免渐变,图像缩放以及离屏绘制。
4.如果row的高度不相同,那么将其缓存下来。
5.如果cell显示的内容来此网络,那么确保这些内容是通过异步来获取的。
6.使用shadowPath来设置阴影。
7.减少subview的数量。
8.在cellForRowAtIndexPath:中尽量做更少的操作。如果需要做一些处理,那么最好做过一次之后,就将结果缓存起来。
9.使用适当的数据结构来保存需要的信息。不同的结构会带来不同的操作代价。
10.使用rowHeight, sectionFooterHeight 和 sectionHeaderHeight 来设置一个恒定 高度,而不要从delegate中获取。
21) 选择正确的数据存储方式


选择正确的数据存储方式
当需要存储和读取大量的数据时,该如何选择存储方式呢?有如下选择:

1.使用NSUserDefaults进行存储
2.保存为XML,JSON或Plist格式的文件
3.利用NSCoding进行归档
4.存储到一个本地数据库,例如SQLite。
5.使用Core Data.
使用NSUserDefaults有什么问题呢? 虽然NSUserDefaults很好并且容易,不过只只针对于存储小量数据(比如你的级别,或者声音是开或关)。如果要存储大量的数据,最好选择别的存储方式。

大量数据保存为结构化的文件也可能会带来问题。一般,在解析这些结构数据之前,需要将内容全部加载到内存中,这是很消耗资源的。虽然可以使用SAX来处理XML文件,但是这有点复杂。另外,加载到内存中的所有对象,不一定全部都需要用到。

那么使用NSCoding来保存大量数据怎么样呢?因为它同样是对文件进行读写,因此依然存在上面说的问题。

要保存大量的数据,最好使用SQLite或Core Data。通过SQLite或Core Data可以进行具体的查询——只需要获取并加载需要的数据对象——避免对数据进行不合理的搜索。在性能方面,SQLite和Core Data差不大。

SQLite和Core Data最大的区别实际上就是用法上。Core Data代表一个对象模型,而SQLite只是一个DBMS。一般,苹果建议使用Core Data,不过如果你有特殊的原因不能使用Core Data的话,可以使用低级别的SQLite。

在程序中,如果选择使用SQLite,这里有个方便的库FMDB :可以利用该库操作SQLite数据库,而不用深入使用SQLite C API。

高级
当且仅当下面这些技巧能够解决问题的时候,才使用它们:

22.加速启动时间
23.使用Autorelease Pool
24.缓存图片 — 或者不缓存
25.尽量避免Date格式化

高级性能提升

寻找一些高明的方法,让自己变为一个全代码忍者?下面这些高级的性能优化技巧可以在适当的时候让程序尽可能的高效运行!

22) 加速启动时间

能快速的启动程序非常重要,特别是在用户第一次启动程序时。第一映像对程序来说非常重要!

让程序尽量快速启动的方法就是尽量以异步方式执行任务,例如网络请求,数据访问或解析。

另外,避免使用臃肿的XIBs,因为XIB的加载是在主线程中进行的。但是记住storyboard没有这样的问题——所以如果可以的话就使用storyboard吧!

注意:在利用Xcode进行调试时,watchdog不会运行,所在设备中测试程序启动性能时,不要将设备连接到Xcode。
23) 使用Autorelease Pool

NSAutoreleasePool负责释放一个代码块中的自动释放对象。一般都是由UIKit来创建的。不过有些情况下需要手动创建NSAutoreleasePool。

例如,如果在代码中创建了大量的临时对象,你将注意到内存使用量在增加,直到这些对象被释放。问题是只有当UIKit耗尽了 autorelease pool,这些对象才会被释放,也就是说当不再需要这些对象之后,这些对象还在内存中占据着资源。

不过这个问题完全可以避免:在@autoreleasepool代码块中创建临时对象,如下代码:

 

  1. NSArray *urls = &lt;# An array of file URLs #&gt;; 
  2. for (NSURL *url in urls) { 
  3.     @autoreleasepool { 
  4.         NSError *error; 
  5.         NSString *fileContents = [NSString stringWithContentsOfURL:url 
  6.                                          encoding:NSUTF8StringEncoding error:&amp;error]; 
  7.         /* Process the string, creating and autoreleasing more objects. */ 
  8.     } 

当每次迭代完之后,都会释放所有的autorelease对象。

关于NSAutoreleasePool的更多内容可以阅读苹果的官方文档
24) 缓存图片–或者不缓存

iOS中从程序bundle中加载UIImage一般有两种方法。

第一种比较常见:imageNamed。

第二种方法很少使用:imageWithContentsOfFile

为什么有两种方法完成同样的事情呢?imageNamed的优点在于可以缓存已经加载的图片。苹果的文档中有如下说法:

This method looks in the system caches for an image object with the specified name and returns that object if it exists. If a matching image object is not already in the cache, this method loads the image data from the specified file, caches it, and then returns the resulting object.

这种方法会在系统缓存中根据指定的名字寻找图片,如果找到了就返回。如果没有在缓存中找到图片,该方法会从指定的文件中加载图片数据,并将其缓存起来,然后再把结果返回。

而imageWithContentsOfFile方法只是简单的加载图片,并不会将图片缓存起来。这两个方法的使用方法如下:

  1. UIImage *img = [UIImage imageNamed:@“myImage”]; // caching 
  2. // or 
  3. UIImage *img = [UIImage imageWithContentsOfFile:@“myImage”]; // no caching 

那么该如何选择呢?

如果加载一张很大的图片,并且只使用一次,那么就不需要缓存这个图片。这种情况imageWithContentsOfFile比较合适——系统不会浪费内存来缓存图片。

然而,如果在程序中经常需要重用的图片,那么最好是选择imageNamed方法。这种方法可以节省出每次都从磁盘加载图片的时间。

25) 尽量避免Date格式化
如果有许多日期需要使用NSDateFormatter,那么需要小心对待了。如之前(重用花销很大的对象)所提到的,无论什么时候,都应该尽量重用NSDateFormatters。

 

然而,如果你需要更快的速度,那么应该使用C来直接解析日期,而不是NSDateFormatter。Sam Soffes写了一篇文章,其中提供了一些解析ISO-8601格式日期字符的串代码。你只需要简单的调整一下其中的代码就可以满足自己特殊的需求了。

这听起来不错把——不过,你相信这还有更好的一个办法吗?

如果你自己能控制处理日期的格式,那么可以选择 Unix timestamps(http://en.wikipedia.org/wiki/Unix_time)。Unix timestamps是一个简单的整数,代表了从新纪元时间(epoch)开始到现在已经过了多少秒,通常这个新纪元参考时间是00:00:00 UTC on 1 January 1970。

你可以很容易的见这个时间戳转换为NSDate,如下所示:

  1. – (NSDate*)dateFromUnixTimestamp:(NSTimeInterval)timestamp { 
  2.     return [NSDate dateWithTimeIntervalSince1970:timestamp]; 

上面这个方法比C函数还要快!

注意:许多网络APIs返回的时间戳都是毫秒,因此需要注意的是在将这个时间戳传递给dateFromUnixTimestamp之前需要除以1000。

何去何从?
强烈建议对程序性能优化感兴趣的读者看看下面列出来的WWDC视频。在看视频之前,你需要注册一个Apple ID(注册后就可以观看所有WWDC2012的视频):

#406: Adopting Automatic Reference Counting
#238: iOS App Performance: Graphics and Animations
#242: iOS App Performance: Memory
#235: iOS App Performance: Responsiveness
#409: Learning Instruments
#706: Networking Best Practices
#514: OpenGL ES Tools and Techniques
#506: Optimizing 2D Graphics and Animation Performance
#601: Optimizing Web Content in UIWebViews and Websites on iOS
#225: Up and Running: Making a Great Impression with Every Launch
下面这些视频来自WWDC 2011 ,也非常有用:

#308: Blocks and Grand Central Dispatch in Practice
#323: Introducing Automatic Reference Counting
#312: iOS Performance and Power Optimization with Instruments
#105: Polishing Your App: Tips and tricks to improve the responsiveness and performance
#121: Understanding UIKit Rendering

这里还有更多相关视频,大多数来自iOS 5技术讲座

Optimizing App Performance with Instruments
Understanding iOS View Compositing
基于 “Your iOS App Performance Hitlist” 视频,Ole Begemann写了一篇文章。苹果还提供了一篇非常好的文章:性能优化。其中提供的技巧和提示对程序性能提升很有帮助。