透過WinDBG的視角看String

摘要 : 最近在博客園里面看到有人在討論 C# String的一些特性. 大部分情況下是從CODING的角度來討論String. 本人覺得非常好奇, 在運行時態, String是如何與這些特性聯系上的. 本文將側重在通過WinDBG來觀察String在進程內的布局, 以此來解釋C# String的一些特性.

?

問題

C# String有兩個比較有趣的特性.

  1. String的恒定性. 字符串橫定性是指一個字符串一經創建,就不可改變。那么也就是說當我們改變string值的時候,便會在托管堆上重新分配一塊新的內存空間,而不會影響到原有的內存地址上所存儲的值。
  2. String的駐留. CLR runtime通過維護一個表來存放字符串,該表稱為拘留池,它包含程序中以編程方式聲明或創建的每個唯一的字符串的一個引用。因此,具有特定值的字符串的實例在系統中只有一個。

對應著兩個特性, 我產生了一些疑問.

  • String的恒定性是怎么樣讓string進行比較的時候出現有趣的結果的? 它的比較結果為什么會與其他引用類型的結果不一樣?
  • 什么樣的String會被放到拘留池中?
  • 拘留池是怎樣的數據結構? 它真是個Hashtable嗎?
  • 駐留在拘留池內的String會不會被GC,? 它的生命周期會有多長(什么時候才會被回收)?

?

String的恒定性

先看一下下面的例子 :

?

private static void Comparation()
{string a = "Test String";string b = "Test String";string c = a;Console.WriteLine("a vs b : " + object.ReferenceEquals(a, b));Console.WriteLine("a vs c : " + object.ReferenceEquals(a, c));SimpleObject smp1 = new SimpleObject(a);SimpleObject smp2 = new SimpleObject(a);Console.WriteLine("smp1 vs smp2 : " + object.ReferenceEquals(smp1, smp2));Console.ReadLine();}class SimpleObject
{public string name = string.Empty;public SimpleObject(string name){this.name = name;}
}


?

image

從結果上看, 雖然是不同的變量 a, b, c. 由于字符串的內容是相同的, 所以比較的結果也是完全相同的. 對比SimpleObject的實例, smp1和smp2的值雖然也是相同的,但是比較的結果為false.

下面看一下運行時, 這些objects的的情況.

在運行時態, 一切皆是地址. 判斷兩個變量是否是相同的對象, 直觀的可以從它地址是否是相同的地址來進行判斷.

用dso命令打印出棧上對應的Objects. 可以看到Test String”雖然出現了3次, 但是他們都對應了一個地址0000000002473f90 . SimpleObject的對象實例出現了2次, 而且地址不一樣, 分別是00000000024776700000000002477688 .

所以, 在使用String的時候, 實質上是重用了相同的String 對象. 在new一個SimpleObject的實例時候, 每一次new都會在新的地址上初始化該對象的結構. 每次都是一個新的對象.

?

?

0:000> !dso
OS Thread Id: 0x3f0c (0)
RSP/REG          Object           Name
......000000000043e730 0000000002473f90 System.String
000000000043e738 0000000002473f90 System.String
000000000043e740 0000000002473f90 System.String
000000000043e748 0000000002477670 ConsoleApplication3.SimpleObject
000000000043e750 0000000002477688 ConsoleApplication3.SimpleObject
.......0:000> !do 0000000002473f90 
Name: System.String
MethodTable: 00007ffdb0817df0
EEClass: 00007ffdb041e560
Size: 48(0x30) bytes
GC Generation: 0
(C:\windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: Test String
Fields:MT            Field           Offset                 Type VT             Attr            Value Name
00007ffdb081f060  4000096        8         System.Int32  1 instance               12 m_arrayLength
00007ffdb081f060  4000097        c         System.Int32  1 instance               11 m_stringLength
00007ffdb0819838  4000098       10          System.Char  1 instance               54 m_firstChar
00007ffdb0817df0  4000099       20        System.String  0   shared           static Empty>> Domain:Value  0000000000581880:0000000002471308 <<
00007ffdb08196e8  400009a       28        System.Char[]  0   shared           static WhitespaceChars>> Domain:Value  0000000000581880:0000000002471be0 <<

?

當字符串內容發生改變的時候, 任何微小的變化都會重新創建出一個新的String對象. 在我們調用這段代碼的時候

??? Console.WriteLine("a vs b : " + object.ReferenceEquals(a, b));

CLR runtime實際上做了兩件事情. 為字符"a vs b"分配了到了一個新的地址. 將對比結果與剛才的字符拼接到了一起, 分配到了另外一個新的地址. 如果多次拼接字符串, 就會分配到更多的新地址上, 從而可能會快速的占用大量的虛擬內存. 這就是為什么微軟建議在這種情況下使用StringBuilder的原因.

?

0:000> !dsoListing objects from: 0000000000435000 to 0000000000440000 from thread: 0 [3f0c]Address          Method Table    Heap Gen      Size Type
…..
0000000002473fc0 00007ffdb0817df0   0  0         44 System.String a vs b : 
0000000002474138 00007ffdb0817df0   0  0         52 System.String a vs b : True…..

?

String的駐留

CLR runtime通過維護一個表來存放字符串,該表稱為拘留池,它包含程序中以編程方式聲明或創建的每個唯一的字符串的一個引用。因此,具有特定值的字符串的實例在系統中只有一個。 我們看一下如何來理解這句話.

下面是示例代碼 :

?

static void Main(string[] args)
{int i = 0;while (true){SimpleString(i++);Console.WriteLine( i + " : Run GC.Collect()");GC.Collect();Console.ReadLine();}
}private static void SimpleString(int i)
{string s = "SimpleString method ";string c = "Concat String";Console.WriteLine(s + c);Console.WriteLine(s + i.ToString());Console.ReadLine();
}

?

這是第一次的執行結果. 此時只執行到了SimpleString里面, 還沒有從這個方法返回.

image

我們可以看到stack上有4個string. 分別是按照代碼邏輯拼接起來的string的內容. 從這里我們就可以當我們在拼接字符串的時候, 實際上會在Heap上創建出多個String的對象, 以此來完成這個拼接動作.

0:000> !dsoListing objects from: 0000000000386000 to 0000000000390000 from thread: 0 [3f50]…..
0000000002a93f70 00007ffdb0817df0   0  0         66 System.String SimpleString method 
0000000002a93fb8 00007ffdb0817df0   0  0         52 System.String Concat String
0000000002a93ff0 00007ffdb0817df0   0  0         92 System.String SimpleString method Concat String
0000000002a97a90 00007ffdb0817df0   0  0         28 System.String 0
0000000002a97ab0 00007ffdb0817df0   0  0         68 System.String SimpleString method 0……

?

隨意用其中一個來檢查它的引用情況.

從!gcroot的結果看, 這個string被兩個地方引用到. 一個是當前的線程. 因為正在被當前線程使用到, 所以能夠看到這個非常正常.

另外一個是root在一個System.Object[]數組上. 這個數組被PINNED在了App Domain 0000000000491880 上面. 這里顯示出來, String其實是駐留在一個System.Object[]上面, 而不是很多人猜測的Hashtable. 不過料想CLR 應該有一套機制可以從這個數組中快速的獲取正確的String. 不過這點不在本篇的討論范圍之內.

?

0:000> !gcroot 0000000002a93f70
Note: Roots found on stacks may be false positives. Run "!help gcroot" for
more info.
Scan Thread 0 OSTHread 81a0
RSP:b9e9b8:Root:0000000002a93f70(System.String)
Scan Thread 2 OSTHread 7370
DOMAIN(0000000000C51880):HANDLE(Pinned):217e8:Root:0000000012a93030(System.Object[])->
0000000002a93f70(System.String)

?

我們可以檢查一下這個System.Object[]里面都有什么.

從這個數組里面可以看到代碼中顯示聲明的的字符串. 第一個元素是一個空值, 這個里面保留的是我們最常用的String.Empty的實例. 第二個元素是”Run GC.Collect()”. 這個在code的里面的main函數中. 當前還沒有被執行到, 但是已經被JITed到了該數組中. 其他兩個被顯示定義的字符串也能夠在這個數組中被找到. 另外可以確認的是, 拼接出來的字符串, 臨時生成的字符串都沒有在這里出現. 然而, 通過拼接出來的String并不在這個數組里面. 雖然拼接出來的String同樣分配到了heap上面, 但是不會被收納到數組中.

0:000> !dumparray -details 0000000012a93030
Name: System.Object[]
MethodTable: 00007ffdb0805be0
EEClass: 00007ffdb041eb88
Size: 1056(0x420) bytes
Array: Rank 1, Number of elements 128, Type CLASS
Element Methodtable: 00007ffdb08176e0
[0] 0000000002a91308Name: System.StringMethodTable: 00007ffdb0817df0EEClass: 00007ffdb041e560Size: 26(0x1a) bytes(C:\windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)String:         Fields:MT    Field   Offset                 Type VT     Attr            Value Name00007ffdb081f060  4000096        8         System.Int32  1 instance                1 m_arrayLength00007ffdb081f060  4000097        c         System.Int32  1 instance                0 m_stringLength00007ffdb0819838  4000098       10          System.Char  1 instance                0 m_firstChar00007ffdb0817df0  4000099       20        System.String  0   shared           static Empty>> Domain:Value  0000000000c51880:0000000002a91308 <<00007ffdb08196e8  400009a       28        System.Char[]  0   shared           static WhitespaceChars>> Domain:Value  0000000000c51880:0000000002a91be0 <<
[1] 0000000002a93f30Name: System.StringMethodTable: 00007ffdb0817df0EEClass: 00007ffdb041e560Size: 64(0x40) bytes(C:\windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)String:      : Run GC.Collect()    Fields:MT    Field   Offset                 Type VT     Attr            Value Name00007ffdb081f060  4000096        8         System.Int32  1 instance               20 m_arrayLength00007ffdb081f060  4000097        c         System.Int32  1 instance               19 m_stringLength00007ffdb0819838  4000098       10          System.Char  1 instance               20 m_firstChar00007ffdb0817df0  4000099       20        System.String  0   shared           static Empty>> Domain:Value  0000000000c51880:0000000002a91308 <<00007ffdb08196e8  400009a       28        System.Char[]  0   shared           static WhitespaceChars>> Domain:Value  0000000000c51880:0000000002a91be0 <<
[2] 0000000002a93f70Name: System.StringMethodTable: 00007ffdb0817df0EEClass: 00007ffdb041e560Size: 66(0x42) bytes(C:\windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)String:     SimpleString method     Fields:MT    Field   Offset                 Type VT     Attr            Value Name00007ffdb081f060  4000096        8         System.Int32  1 instance               21 m_arrayLength00007ffdb081f060  4000097        c         System.Int32  1 instance               20 m_stringLength00007ffdb0819838  4000098       10          System.Char  1 instance               53 m_firstChar00007ffdb0817df0  4000099       20        System.String  0   shared           static Empty>> Domain:Value  0000000000c51880:0000000002a91308 <<00007ffdb08196e8  400009a       28        System.Char[]  0   shared           static WhitespaceChars>> Domain:Value  0000000000c51880:0000000002a91be0 <<
[3] 0000000002a93fb8Name: System.StringMethodTable: 00007ffdb0817df0EEClass: 00007ffdb041e560Size: 52(0x34) bytes(C:\windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)String:     Concat String    Fields:MT    Field   Offset                 Type VT     Attr            Value Name00007ffdb081f060  4000096        8         System.Int32  1 instance               14 m_arrayLength00007ffdb081f060  4000097        c         System.Int32  1 instance               13 m_stringLength00007ffdb0819838  4000098       10          System.Char  1 instance               43 m_firstChar00007ffdb0817df0  4000099       20        System.String  0   shared           static Empty>> Domain:Value  0000000000c51880:0000000002a91308 <<00007ffdb08196e8  400009a       28        System.Char[]  0   shared           static WhitespaceChars>> Domain:Value  0000000000c51880:0000000002a91be0 <<

?

繼續讓代碼執行下去, 我們需要來幾次GC. 驗證一下駐留的字符串是否會在不使用之后被GC掉.

GC完成之后, 按照所設想的, CallStack上面的String都已經被清除掉了.同時因為已經做過了GC動作, GC heap進過了壓縮, 沒有被PINNED住的對象地址會發生改變. 所以要驗證駐留的String是否會被回收, 可以從駐留數組下手. 由于該數組是被PINNED住, 所以即使發生了GC的動作, 它的地址也不會發生改變. 所以可以通過相同的命令把數組里面駐留的String都列出來.

結果是與我的預期是一致的. 只有被顯示定義的String保留在該數組內, 而這些String不會被回收. 通過拼接零時生產的String, 則不會加入到這個數組內, 在GC發生后, 由于沒有被引用而被回收掉.

?

0:000> !dumparray -details 0000000012a93030
Name: System.Object[]
MethodTable: 00007ffdb0805be0
EEClass: 00007ffdb041eb88
Size: 1056(0x420) bytes
Array: Rank 1, Number of elements 128, Type CLASS
Element Methodtable: 00007ffdb08176e0
[0] 0000000002a91308Name: System.StringMethodTable: 00007ffdb0817df0EEClass: 00007ffdb041e560Size: 26(0x1a) bytes(C:\windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)String:         
...
[1] 0000000002a93f30Name: System.StringMethodTable: 00007ffdb0817df0EEClass: 00007ffdb041e560Size: 64(0x40) bytes(C:\windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)String:      : Run GC.Collect()    …
[2] 0000000002a93f70Name: System.StringMethodTable: 00007ffdb0817df0EEClass: 00007ffdb041e560Size: 66(0x42) bytes(C:\windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)String:     SimpleString method     ...
[3] 0000000002a93fb8Name: System.StringMethodTable: 00007ffdb0817df0EEClass: 00007ffdb041e560Size: 52(0x34) bytes(C:\windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)String:     Concat String 

?

?

所以經過上面的觀察, 可以得出的結論是駐留的String生命周期非常長. 那么, 在什么時候他才會被回收?

從上面gcroot的結果, 可以看到主流數組是被PINNED住. 而引用這個數組的App Domain 0000000000C51880.

用!dumpdomain -stat的命令將所有的app domain信息打印出來. 可以看到這個App Domain是我們代碼運行的Domain (ConsoleApplication3.exe). 這個駐留數組是由CLR 來維護, 并且與當前的App Domain聯系到一起. 所以, 理論上這些駐留數組的生命周期跟這個App Domain是一致的.

?

0:000> !dumpdomain -stat
--------------------------------------
System Domain: 00007ffdb1f16f60
LowFrequencyHeap: 00007ffdb1f16fa8
HighFrequencyHeap: 00007ffdb1f17038
StubHeap: 00007ffdb1f170c8
Stage: OPEN
Name: None
--------------------------------------
Shared Domain: 00007ffdb1f17860
LowFrequencyHeap: 00007ffdb1f178a8
HighFrequencyHeap: 00007ffdb1f17938
StubHeap: 00007ffdb1f179c8
Stage: OPEN
Name: None
Assembly: 000000000047fa60
--------------------------------------
Domain 1: 0000000000491880
LowFrequencyHeap: 00000000004918c8
HighFrequencyHeap: 0000000000491958
StubHeap: 00000000004919e8
Stage: OPEN
SecurityDescriptor: 0000000000494140
Name: ConsoleApplication3.exe
Assembly: 000000000047fa60 [C:\windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll]
ClassLoader: 000000000047f820
SecurityDescriptor: 000000000047f9a0Module Name
00007ffdb03e1000 C:\windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll

?

寫在最后面

  1. String的恒定性. 字符串橫定性是指一個字符串一經創建,就不可改變。那么也就是說當我們改變string值的時候,便會在托管堆上重新分配一塊新的內存空間,而不會影響到原有的內存地址上所存儲的值。
  2. String的駐留. CLR runtime通過維護一個表來存放字符串,該表稱為拘留池,它包含程序中以編程方式聲明或創建的每個唯一的字符串的一個引用。因此,具有特定值的字符串的實例在系統(App Domain)中只有一個。
    直接在CODE里面聲明的String會被CLR runtime維護在一個Object[]內.
    臨時生成的string或者拼接出來的String不會維護在這個駐留數組中.
    駐留數組的生命周期跟它位于的App Domain一樣長. 所以GC并不會影響駐留數組所引用的String, 它們不會被GC.

可以參考下面這個鏈接來對這兩個特性加深理解.

http://blog.csdn.net/fengshi_sh/article/details/14837445

http://www.cnblogs.com/charles2008/archive/2009/04/12/1434115.html

http://www.cnblogs.com/instance/archive/2011/05/24/2056091.html

轉載于:https://www.cnblogs.com/developersupport/p/4212102.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/375372.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/375372.shtml
英文地址,請注明出處:http://en.pswp.cn/news/375372.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Win32ASM學習[8]: 進制轉換的庫函數

在 masm32.inc 中有這樣幾個函數的聲明: byt2bin_ex PROTO :BYTE, :DWORD wrd2bin_ex PROTO :WORD, :DWORD dw2bin_ex PROTO :DWORD, :DWORD dw2hex_ex PROTO :DWORD, :DWORD bin2byte_ex PROTO :DWORD -------------------------------------------------------------…

SOJ 2800_三角形

真的是O不是0【看了discuss才發現。。。。。一個大寫的蠢 【題意】多個黑白三角形組成的倒三角&#xff0c;求白三角形組成的最大倒三角的面積 【分析】由于問的是倒三角個數&#xff0c;所以只需看與行數奇偶性相同的白色倒三角形&#xff0c;設v[i][j]為以第i行第j列的倒三角…

ueditor富文本編輯器 修改框寬度和高度的方法

在使用ueditor的時候&#xff0c;用的textarea <textarea name"content" id"myEditor">這里寫這條規則的回復內容</textarea> 給它加style"width:300" 屬性的時候&#xff0c;發現不起作用。 正確的方法應該是&#xff1a; <scri…

Win32ASM學習[9]: 標志寄存器

TF(Trap Flag)——位8&#xff0c;跟蹤標志。置1 則開啟單步執行調試模式&#xff0c;置0 則關閉。在單步執行模式下&#xff0c;處理器在每條指令后產生一個調試異常&#xff0c;這樣在每條指令執行后都可以查看執行程序的狀態。如果程序用POPF、POPFD 或者ET 指令設置TF 標志…

TCP多進程并發服務端 Linux socket編程入門(2)

這里很簡單的使用了fork()函數&#xff0c;在執行了fork()以后的所有代碼都會由子進程和父進程同時執行。 他們同時擁有相同的資源&#xff08;兩份拷貝&#xff09;&#xff0c;所以在子進程執行的過程中&#xff0c;子進程需要先close掉listenfd&#xff08;監聽套接字&#…

ArcEngine 打開shape文件

IWorkspaceFactory wsf new ShapefileWorkspaceFactory(); IWorkspace pWorkspace wsf.Open(filePath, 0) ;//filePath為shapefile所在的文件夾 IFeatureWorkspace pFeatureWorkspace pWorkspace ; IFeatureClass pFeatureClass pFeatureWorkspace.OpenFeatureClass(&quo…

Win32ASM學習[10]:傳送指令

匯編指令的一般性要求: 1、兩個操作數的尺寸必須一致; 2、操作數不能同為內存. --------------------------------------------------------------------------------------------------------------- ;mov ;該指令不影響 EFlags ;指令格式: (其中的 r、m、i 分別表示: 寄存器、…

SQL Server 中關于 @@error 的一個小誤區

SQL Server 中關于 error 的一個小誤區 原文:SQL Server 中關于 error 的一個小誤區在SQL Server中&#xff0c;我常常會看到有些前輩這樣寫&#xff1a; if(error<>0)ROLLBACK TRANSACTION T elseCOMMIT TRANSACTION T 一開始&#xff0c;我看見別人這么寫&#xff0c;我…

Win32ASM學習[11]:邏輯運算

--------------------------------------------------------------------------------------------------------------------------- 一.邏輯與運算指令 AND 格式: AND OPRD1,OPRD2其中目的操作數OPRD1為任一通用寄存器或存儲器操作數.源操作數OPRD2為立即數、任一通用寄存器…

JavaScript消息框

1.警告框 function myTest(){alert("這里的內容會彈出");} 2.確認框 其返回的值是 true 或 false 。 function myTest(){confirm("這里的內容會彈出");} 3.提示框 prompt prompt(參數1&#xff0c;參數2)&#xff1a;其參數1 是顯示提示要輸入的信息&…

.Net 事務

在分布式應用程序中&#xff0c;不可避免地會經常使用到事務控制。事務有一個開頭和一個結尾&#xff0c;它們指定了事務的邊界&#xff0c;事務在其邊界之內可以跨越進程和計算機。事務邊界內的所有資源都參與同一個事務。要維護事務邊界內資源間的一致性&#xff0c;事務必須…

Win32ASM學習[12]:位測試指令位掃描指令

----------------------------------------------------------------------------------------------------------------------- 一.BT 指令 格式: BT OPD,OPS 功能: 目的操作數OPD中由源操作數OPS指定的位送CF標志 說明: 1. 在指令中,目的操作數OPD只能是16/32位通用寄存器…

Android WifiDisplay分析一:相關Service的啟動

網址&#xff1a;http://www.2cto.com/kf/201404/290996.html 最近在學習Android 4.4上面的WifiDisplay(Miracast)相關的模塊&#xff0c;這里先從WifiDisplay用到的各個Service講起&#xff0c;然后再從WifiDisplaySettings里面講解打開wfd的流程。首先看下面的主要幾個Servic…

mvc controller跳轉頁面方法

1、直接Redirect后加 Controller/Action Response.Redirect("/User/Edit"); // return Redirect("/User/Edit"); return RedirectToAction("about","Home"); Response.Redirect("/User/Edit"); 2、直接r…

Win32ASM學習[13]:移位指令SHL,SHR,SAL,SAR,ROL,ROR,RCL,RCR,SHLD,SHRD

一. SHL、SHR、SAL、SAR: 移位指令 ---------------------------------------------------------------------------------------------------- ;SHL(Shift Left): 邏輯左移 ;SHR(Shift Right): 邏輯右移 ;SAL(Shift Arithmetic Left): 算術左移 ;SAR(Shift Ari…

angular中的表單驗證

angular中的表單驗證很強大&#xff0c; 一共有5中驗證信息&#xff0c;$valid,$invalid,$pristine,$dirty,$error. $valid-----當驗證通過的時候&#xff0c;為true,不通過的時候為false $invalid----當驗證不通過的時候&#xff0c;為true&#xff0c;通過的時候為true $pris…

Cortex-A15 Memory Hierarchy

ARM 平臺為實現速度和成本的平衡&#xff0c;使用多個層次的內存架構。對于多核 CPU 組成的 SOC&#xff0c;每個CPU 內部都有一組高速緩存&#xff0c;包含&#xff1a;ICache、DCache 和 TLB。多個 CPU 共享一個更大的 L2 緩存。L2緩存再和 CPU 外部的DDR3 內存交互。ICache …

Win32ASM學習[14]:符號擴展指令: CBW,CWDE,CDQ,CWD

----------------------------------------------------------------------------------------------------------------------------------------------------------------- ;CBW(Convert Byte to Word): 將 AL 擴展為 AX ;CWDE(Convert Word to Extended Double): 將 …

Win32ASM學習[15]:加減指令: INC、DEC、NEG、ADD、ADC、SUB、SBB、CMP

------------------------------------------------------------------------------------------------------------------------------------------------------------------- ;INC(Increment): 加一 ;DEC(Decrement): 減一 ;NEG(Negate): 求補(求反) ;ADD(Add): 加 ;A…

UINavigationController的簡單使用

UINavigationController的使用步驟初始化UINavigationController設置UIWindow的rootViewController為UINavigationController根據具體情況&#xff0c;通過push方法添加對應個數的子控制器UINavigationController的子控制器UINavigationController以棧的形式保存子控制器proper…