VS 2013驱动程序开发+VM双机调试(亲测+详解)

WIN10已上线,随之而来的是VS2015;微软在 “WDK7600” 以后就不再提供独立的内核驱动开发包了,而是必须首先安装微软集成开发环境VS,然后再从微软官网下载集成的WDK驱动程序开发包、或者离线安装的开发包

地址:

https://msdn.microsoft.com/zh-cn/windows/hardware/hh852365.aspx

离线下载地址:百度云链接:http://pan.baidu.com/s/1i3KdUSH 密码:vp41

安装WDK后,本人使用的是VS2013 +WDK8.1,无脑下一步安装完成后,建立新工程,即可看到新的驱动工程项目。

其中提供了很多的模板,(WDM WDF,KWDM等等),大家可能感觉这些框架形式很复杂,其实这些框架的区别,主要在于设备驱动开发,例如WDM相对NT增加了即插即用电源管理,WDF封装了WDM接口变成类形式,隐藏驱动设备等对象,其他的类似应用层驱动框架等(其实就是dll)。

因新框架的封装隐藏了底层对象信息等,有时候不够自由,让我们实现一些功能的时候不太方便,所以我们还是主要使用NT式驱动。

编写驱动:

第一个问题来了,WDK8.1提供的模板中根本没有提供NT驱动模板,我们如何创建NT驱动呢?

解决:其实虽然没有提供NT模板,但是我们可以建立WDM空模板工程,然后再自己添加文件,编译,得到的也就是NT驱动了。

建立工程后,首先会有两个工程,一个就是驱动工程,另外一个是package工程(这个是测试驱动安装的一个工程,对于我们来说其实没有什么用处,反正本人是没有使用过得,可以直接删除)。

驱动工程中会帮你建立一个inf文件,NT是使用不到的,当然新一代的过滤驱动,例如 minifilter 是使用的,VS2013支持直接创建minifilter工程,所以如你建的是 NT驱动,这个可以直接删除,如不想删除,就需要在inf文件中填写一些信息,避免生成程序时报错,可以按以下模板填写:

<strong><span style="font-size: 18px;">
  ;这个 DDK 文档是一个优秀的INF参考

  ;
  ;MyWDMDriverA.inf
  ;

  ;----------------------- 版本部分 ------------------------------------

  [Version]
  Signature="$YHMADE$"    ;签名
  Provider=YanHui     ;供应商,可选填写
  DriverVer=11/14/2015,20.46.24.01    ;驱动程序版本,格式自定义,可选填写
  CatalogFile=myWDMDriverA.cat  ;此处重点。生成的inf中没有填写,此处需要自己添加:名字.cat

  ; 如果设备符合其中一个标准类,填写使用的名称和GUID,否则要创建自己的设备类和GUID。如本例所示。

  Class=YH  ;随便改个就行
  ClassGUID={EF2934D45-1DD8-F672-5656-H7G1EE8A35FF}  ;随便改个就行,不要与已有的冲突class和calssguid都有个对应关系.网上可查

  ;--------- 源名称和源磁盘文件部分 -----------------------
  ;这部分确定安装源磁盘和文件,他们在这里显示作为一个例子,但可以注释掉。

  [SourceDisksNames]
  1 = %DiskName%,,,""

  [SourceDisksFiles]
  myWDMDriverA.sys = 1,,

  ;--------- 类安装/32位类部分 -------------------------------
  ;如果使用一个标准的类就不要

  ; 9X 风格
  [ClassInstall]
  Addreg=Class_AddReg

  ; NT 风格
  [ClassInstall32]
  Addreg=Class_AddReg

  [Class_AddReg]
  HKR,,,,%DeviceClassName%
  HKR,,Icon,,"-5"

  ;--------- 终点节目录-------------------------------------------

  [DestinationDirs]
  Default_Files_Driver = 12,System32\Drivers

  ;---------制造商和模型部分 ----------------------------------

  [Manufacturer]
  %ManufacturerName%=Standard,NT$ARCH$

  [Standard.NT$ARCH$]

  ; PCI硬件id使用表单
  ; PCI\VEN_aaaa
  ; DEV_bbbb
  ; SUBSYS_cccccccc
  ; REV_dd
  ; 改成你自己的ID
  %DeviceDesc%=Default_DDI, PCI\VEN_2DF4
  %DeviceDesc%=Default_DDI, PCI\DEV_6677
  %DeviceDesc%=Default_DDI, PCI\SUBSYS_7D9F873K
  %DeviceDesc%=Default_DDI, PCI\REV_HK

  ;----------Windows 9X  DD安装部分 -----------------------------------------------

  ; 实验表明,DD 安装的根是大于19字符的名称
  ; 在Windows 98造成问题

  [Default_DDI]
  CopyFiles=Default_Files_Driver
  AddReg=Default_9X_AddReg

  [Default_9X_AddReg]
  HKR,,DevLoader,,*ntkern
  HKR,,NTMPDriver,,myWDMDriverA.sys
  HKR, "Parameters", "BreakOnEntry", 0x00010001, 0

  ; --------- Windows NT -----------------

  [Default_DDI.NT]
  CopyFiles=Default_Files_Driver
  AddReg=Default_NT_AddReg

  [Default_DDI.NT.Services]
  Addservice = MyWDMDriverA, 0x00000003, Default_AddService

  [Default_AddService]
  DisplayName = %SvcDesc%
  ServiceType = 1 ; SERVICE_KERNEL_DRIVER
  StartType = 3 ; SERVICE_DEMAND_START
  ErrorControl = 1 ; SERVICE_ERROR_NORMAL
  ServiceBinary = %10%\System32\Drivers\myWDMDriverA.sys

  [Default_NT_AddReg]
  HKLM, "System\CurrentControlSet\Services\MyWDMDriverA\Parameters",  "BreakOnEntry", 0x00010001, 0

  ; --------- 文件 (常见) -------------

  [Default_Files_Driver]
  myWDMDriverA.sys

  ;--------- 截面字符串 ---------------------------------------------------

  [Strings]
  ProviderName="YanHui"
  ManufacturerName="YanHuiDriver"
  DeviceDesc="QQ14526854";设备管理器中的显示的实例的名字.在类的下边显示
  DeviceClassName="QT35623608";此为显示在设备管理器中的类的名字,修改一次类的名字,就需要更改一次classguid的值.
  SvcDesc="YanHui"

  ;inf结束. inf中没有的节的可以参照我的inf自己添加.
</span></strong>

如图:

我们直接添加一个myWDMDriverA.cpp(我使用C++开发驱动,但我还是建议大家使用c开发比较适合,因为微软内核使用的也是C,而且C是能够直接操作内存,汇编之上的个人感觉最好的语言)

在编译驱动前,这里对 Windows 驱动程序做一个简要的说明:

1.Windows驱动程序分为两类,一类是不支持即插即用功能的NT式的驱动程序;另一类是支持即插即用功能的WDM式的驱动程序。

2.NT式的驱动程序要导入的头文件时ntddk.h,而WDM式的驱动要导入的头文件为wdm.h

3.DriverEntry需要放在INIT标志的内存中。INIT标志指明该函数只是在加载的时候需要载入内存,而当驱动程序加载成功后,该函数可以从内存中卸载掉。

4.C++编写驱动需要注意下面的编写格式:

#ifdef _cplusplus
extern "C"
{
#endif
#include <ntddk.h>
#ifdef _cplusplus
}
#endif                         

#define PAGEDCODE  code_seg("PAGE")     

#pragma PAGEDCODE
VOID Unload(IN PDRIVER_OBJECT pDriverObject);
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath)
{
    KdPrint(("......"));
    return "......." ;
}

如果在 C++ 中编写驱动入口时,不按照这样的格式写,当加载或卸载驱动时会出现蓝屏

并且在编译代码是要特别注意字母的大小写,C 和 C++ 对大小写字母特别敏感!!

添加一些简单的代码。

下面,编译,报错。。。。。没有关系,查看出错原因,无外乎一些警告被当做错误,或者一些函数参数没有被使用,导致编译不过,这些都是因为安全警告等级太高了,我们可

其实以前的旧版本,在编写驱动前需要对项目进行属性配置,才能正确的生成。

但微软已在 VS2013 上做了很大的改进,让我们几乎不用改写属性都可以顺利生成,以前的就以前yi以如果出现编译错误,可以修改两个地方来解决:

(一) 将所有的警告和安全措施,全部都做到,例如没有使用的参数使用宏UNREFERENCED_PARAMETER等等,当然做到这些,有时候基本没有办法写程序。

(二) 降低警告等级哦,

如果你实在不放心,我可以参照下面的说明来修改项目属性来达到因程序配置问题而影响驱动的生成问题:这里只记录一些配置和操作步骤!

1.设置VC++路径

<我把wdk安装在E盘下>

(1).  配置可执行文件目录:E:\WinDDK\7600.16385.1\bin\x86;

(2).  配置包含目录:

E:\WinDDK\7600.16385.1\inc\ddk

E:\WinDDK\7600.16385.1\inc\crt

E:\WinDDK\7600.16385.1\inc\api

(3).  配置库目录:

E:\WinDDK\7600.16385.1\lib\win7\i386

新建C/C++文件 不然无C/C++设置选项 <刚开始我们创建了一个空的项目所以项目里没有c++文件,现在要做的就是在空的项目-源文件-添加一个新建项c++文件>

常规

目标文件扩展名:.sys     //必选

2设置C/C++选项

常规选项卡

(1) 调试信息格式(C7 兼容(/Z7)   //可选

(2) 警告等级 (2 级(/W2)   //可选

(3 )将警告视为错误  (是(/wx)    //可选

优化选项卡

优化(禁用/Od)         //可选

预处理器

预处理器定义:WIN32=100;_X86_=1;WINVER=0x501;DBG=1    //必选

代码生成

(1) 启用最小重新生成: 否      //可选

(2 )基本运行时检查: 默认值      //可选

(3) 运行时库: 多线程调试(/MTd) 或 多线程(/MT) //建议选

(4 )缓冲区安全检查: 否  //可选 (可避免出现  LINK : error LNK2001:  无法解析外部符号  __security_cookie)

高级

调用约定:  __stdcall(/Gz)      //必选

3.  链接器设置

常规 :

启用增量链接:否(/INCREMENTAL:NO)    //建议 选上

忽略导入库:是  // 可选 (设置为此值时,必须在附加库目录中加: E:\WinDDK\7600.16385.1\lib\win7\i3865这样项目就不会依赖 IDE 环境的设置)

输入 :

附加依赖项 :ntoskrnl.lib;Hal.lib;wdm.lib;wdmsec.lib;wmilib.lib;ndis.lib;MSVCRT.LIB;LIBCMT.LIB    //必选 //NT式驱动ntoskrnl.lib;WDM式驱动wdm.lib ( HalXXX 函数在Hal.lib, WmiXXX 函数在wmilib.lib,NdisXXX函数在ndis.lib ) (必要时需要增加微软的标准库MSVCRT.LIB MSVCRTD.LIB(调试库)LIBCMT.LIBIBCMTD.LIB(调试库) ) (如果源码中有source文件,那么该文件的TARGETLIBS字段会列出该项 目需要的库)

忽略所有默认库:是 (/NODEFAULTLIB)  //必选

清单文件: 启用用户账户控制(UAC) 否  //必选不然会出现>LINK : fatal error LNK1295: “/MANIFESTUAC”与“/DRIVER”规范不兼容;链接时不使用“/MANIFESTUAC”

调试:

生成调试信息: 是(/DEBUG)   //可选

生成映像文件:是(/MAP)     //可选

映像文件名:$(TargetDir)$(TargetName).map  //可选

系统(System):

子系统:  控制台(/SUBSYSTEM:CONSOLE //必选

堆栈保留大小:4194304     //可选

堆栈提交大小:  4096       //可选

驱动程序:    驱动程序(/DRIVER)  //必选

高级:

入口点:DriverEntry    //必选

随机基址: 清空  //把框里的数据删掉。(yes也不是no也不是就是要一个干干净净的文本框)//必选 不然会出现  e:\xxx.sys : fatal error LNK1295: “/DYNAMICBASE”与“/DRIVER”规范不兼容;链接时不使用“/DYNAMICBASE”

数据执行保护(DEP):  清空 //把框里的数据删掉。(yes也不是no也不是就是要一个干干净净的文本框) //必选不然会出现e:\xxx.sys : fatal error LNK1295:“/NXCOMPAT:NO”与“/DRIVER”规范不兼容;链接时不使用“/NXCOMPAT:NO”

设置效应和:是(/RELEASE)   //可选

基址:0x10000     //建议选上

命令行:/SECTION:INIT,D /IGNORE:4078  (建议不要写进去,会报错!)

好了,我们再次尝试编译生成,生成过了。

现在来设置虚拟机的端口:至于虚拟机的系统安装我就不解释了,网上一搜一大把!

按照网上的解说,虚拟机中添加一个虚拟串口(\\.\pipe\com_1),因为虚拟机中自带的虚拟打印机,占用虚拟串口一.

所以,你有两个选择,一个删除该虚拟打印机,一个,添加别的虚拟串口(例如:\\.\pipe\com_2),这里,我选择的是删除虚拟打印机。

好了,虚拟机已经开启了串口监听。

调试驱动:

VS2013提供集成IDE调试驱动,网上面有很多配置调试环境的帖子,在这里我先啰嗦解释两句:

(一) VS2013 调试启动,首先需要建立一个调试机工程

(二) 选择添加一个目标计算机(Add New Computer),这里的目标计算机就是按装用 Windbg VS2013 等调试软件的主机,我们基本上都用本机为目标机,点击下一步,能够选择多种目标计算机类型.

例如:

第一项Prevision Computer and Automatically Configura Debuggers:选择一个远程的真机,VS会在远程计算机上安装调试工具,设置调试环境,会重启几次。

第二项Provision Computer and choose debugger setting和第三项:可以自己选择调试方式(网络,串口,1394,USB)。即使不是使用网络进行调试,也要使用网线连接调试机和被调试机,因为VS在配置环境的时候需要使用网络进行配置和传输文件。

在这里我选择使用第三项,并且配置串口调试,因为,以前我们使用Windbg的时候也是配置串口调试的。

按上图配置即可:

Connection Type选为Serial (串口)

(波特率)Baud Rate 115200

(管道)Pipe勾选

(重连接)Reconnect勾选

(管道名)Pipe name  \\.\pipe\com_1

这几个选项根据你虚拟机设置来设置,最后点击完成。

OK,到这里,总结一下,我们能够编译驱动,然后直到调试驱动需要创建一个目标计算机,目标计算机可以是真机,也可能是虚拟机,当然现在不能证明。因为微软告诉我们说,VS2012以后,不支持xp平台驱动编译,这个我们在选择驱动编译环境的时候,我们也看到了。完全就没有xp,

不要紧,那我们先使用win7 32来调试实验下。

打开虚拟机进入系统后,设置系统的启动项为:(调试模式),设置完后,重启虚拟机。

我也可以直接在系统启动的时候按F8键进入启动模式选择界面,选中“调试模式”回车:

选择后,虚拟机的系统就正常进入,速度会有点慢,需耐心等待。

然后VS2013工程中我们选择要上面设置的COM1端口的配置电脑:

选择菜单上的“调试”选择“附加到进程”选项:

传输: Windows Kernel Mode Debugger

限定符: 就是我们添加的计算机名

点击附加 VS2013会自动进入调试状态,如果没有我们点击“Debuging Tools for windows”选项:

这时会弹出确认界面:

选择:“I Know”,然后 VS2013 自带的 Windbg 软件就会启动,进入到等待连接的状态:

这时检测虚拟机的系统,看是否正常进入了桌面,(这里速度有点慢,需稍微等待一下)。

有反应了,说明系统正在登陆….。稍等片刻后,再此查看,如果进入了系统,我们点击VS2013 编译器的暂停按钮,中断与虚拟机的连接:

点击后这里可能会有点卡,速度有点慢,等一下就好了。如果真的卡死了,我们用任务管理器把 VS2013 编译器结束掉,再重新打开,并打开驱动工程项目,且按自己的需要下好断点。

再重新选择菜单上的“调试”->”附加到进程”按上次的设置来开启驱动调试。注意,开启的虚拟机不能关闭!

当重新与虚拟机连接,并运行到: int 3 这里后:

好了,一切准备就绪,现在我们在命令框中输入 “g” 按回车,让虚拟机不受控制,自己接受主权,自动跑起来…

在这之前,切记我们已在代码中下了断点,不然后面加载驱动后,调试器无法拦截上….

现在可以把生成的驱动文件 DriverName.sys 考到虚拟系统的桌面上,这里我的驱动文件是MyWDMDriverA.sys。

然后我们用驱动加载工具DriverMonitor 打开要加载驱动文件,然后点击 go 继续加载:

看到了吧!当驱动被加载到系统后,VS 2013 编译器和Windbg调试器便立刻有了反应,且驱动调试截取到断点的位置!

这时VS2013 可能会弹出“帧不在模块中的警告界面(是个人情况而定),而我的两台电脑就只有一点出现这种情况:

原因是我们没对Windbg 设置源文件和符号路径。

网上说:出现该错误就有可能是下面中的一种,读者可以一一试试:

1. 在项目中引用另一个项目,而修改了被引用项目的代码后,引用的dll没有更新,所以出现上述错误。

2. 试图调试托管代码的转储文件。Visual Studio 仅支持使用 SOS 工具,在“即时”窗口中对托管的小型转储进行调试。

3. 在解决方案属性页中,可以更改调试器查找源文件的目录,并通知调试器忽略选定的源文件。请检查"解决方案属性页"对话框中的- >"通用属性"- >"调试源文件"- >"不查找这些源文件",看你要调试的源文件是不是在该列表中,如果在,请删除后确定。

4. 在工具- >选项- >调试中禁用源代码不可用时显示反汇编。

5. 由于代码的上下文件限制了要调试的源代码的加载(特别是在调试JavaScript代码时出现该错误时此种可能性最大)。

6. 你要调试的源文件处在一个封装好的dll文件中,所以按F11是跟不进去的。

7. 如果你有备份文件,试试你的备份文件。

8. 重写你要调试的源代码。(当然不推荐这种方法了,不过这也是最后没有办法的办法。)

我们可以尝试对源文件进行第3、4点设置, Windbg 符号的路径我们点击“选项”对话框:

我们选择“调试”->符号->勾选符号文件里面的两个选项,让它自动在微软的服务器上下载所需要的符号。

我们再选缓存符号的目录,如你自己有下载安装 Windbg Symbols 符号文件,就可以在这里直接指定到安装目录,如果没有安装就,随便指定到一个空文件夹,点击确定后,Windbg 会继续运行,在服务器上下载符号,并自动选择调试模式并进入。

我们来调出VS201的寄存器,就可以看到驱动动态指针地址:

如果源码地址正确,我们就能在反汇编调试窗中看到指针指向和运行跳转步骤!

到这,就说明 VS2013 的部署+VM双机调试测试成功!

这里多说点废话!!

在2013中驱动项目的符号文件是不用特殊指定的,编译器会自动在刚刚勾选的微软服务器上找到符号文件并下载到指定的位置。

但我也要知道另一个方法是用命令的形式来加载或下载符号文件。

如果系统中没有单独安装符号文件,在Debugger Immediate Windows窗口中输入命令:

.sympath srv*c:\Symbols*http://msdl.microsoft.com/download/symbols

*c:\Symbols*之间的路径就是你要保存的符号文件的路径,如果你已经下载并单独安装了符号文件可以用如下形式的命令来指定:

.sympath d:\Symbols 回车后显示:

我们再   .reaload 一下(重新加载符号文件)

哈哈,这里输入命令还有智能提示哦!

部分符号文件没有找到也属于正常情况,指定目录下没有这些驱动的符号文件!可以指定到官方网站去下载符号文件

.sympath srv*c:\MyServerSymbols*[url=http://msdl.microsoft.com/download/symbols]http://msdl.microsoft.com/download/symbols

时间: 2024-10-13 11:13:15

VS 2013驱动程序开发+VM双机调试(亲测+详解)的相关文章

YUM安装调试以及命令详解

背景,需要安装cacti,google了很多安装资料,需要先yum安装一些准备lib包,比如snmp以及openssl等等. [[email protected] ~]# yum install net-snmp-devel Loaded plugins: rhnplugin, security This system is not registered with RHN. RHN support will be disabled. Setting up Install Process No p

使用 jQuery Mobile 与 HTML5 开发 Web App —— jQuery Mobile 事件详解

在前文<使用 jQuery Mobile 与 HTML5 开发 Web App —— jQuery Mobile 默认配置与事件基础>中,Kayo 对 jQuery Mobile 事件的基础作出了一些说明,建议在阅读本文前首先阅读前文,这里 Kayo 再引用前文的重要内容. “jQuery Mobile 在基于本地事件上,创建了一系列的自定义事件,大部分事件是基于触摸设备的使用情况开发的,当然这些事件对于桌面环境也会有适当的处理,开发者可以使用 bind() 函数绑定到需要的页面对象中. 值得

Web开发典藏大系:Web性能测试实战详解 PDF扫描版

本书主要讲解了大数据背景下的Web性能测试的特点和方法,以及使用业内应用非常广泛的工具――LoadRunner 11进行性能测试的具体技术与技巧.本书理论结合实践,讲解图文并茂,并且将IT技术与生活场景结合起来,生动而又形象. 本书共17章,分为5篇.第1篇介绍软件测试的定义.方法和过程.Web应用开发与测试等内容:第2篇介绍Web应用技术.Web性能测试的基础.Web性能测试的方法和各操作系统性能计数器的获取等内容:第3篇介绍如何使用LoadRunner进行Web应用性能测试,包括LoadRu

keepalived 双机热备配置文件详解

配置示例:https://blog.51cto.com/14227204/2438902一.keepalived工作原理及作用: keepalived最初是专门针对LVS设计的一款强大的辅助工具,主要用来提供故障切换和健康检查功能--判断LVS负载调度器.节点服务器的可用性,及时隔离并替换为新的服务器,当故障主机回复后将其重新加入群集.单独部署LVS环境的话,调度器发生宕机的话,整个群集就失效了,某一个web节点宕机后,客户端在访问时,总会碰上访问不到网页的情况,所以,将keepalived和L

iOS开发——网络编程Swift篇&amp;Alamofire详解

Alamofire详解 预览图 Swift Alamofire 简介 Alamofire是 Swift 语言的 HTTP 网络开发工具包,相当于Swift实现AFNetworking版本. 当然,AFNetworking非常稳定,在Mac OSX与iOS中也能像其他Objective-C代码一样用Swift编写.不过Alamofire更适合Swift语言风格习惯(Alamofire与AFNetworking可以共存一个项目中,互不影响). Alamofire 取名来源于Alamo Fire fl

iOS开发中的UDID和UUID详解

今天突然想和大家聊聊UDID和UUID的问题,虽然平时我们对这两个东西很忽视,往往也很难区分这两个东西.今天就来好好谈谈. [UDID] UDID的全名为 Unique Device Identifier :设备唯一标识符.从名称上也可以看出,UDID这个东西是和设备有关的,而且是只和设备有关的,有点类似于MAC地址.我在上一篇博客中<iOS应用发布流程详解>提到了真机调试,然后需要把UDID这个东西添加到Provisoning Profile授权文件中,也就是把设备唯一标识符添加进去,以此来

iOS开发——实用篇&amp;KVO与KVC详解

KVO与KVC详解 由于ObjC主要基于Smalltalk进行设计,因此它有很多类似于Ruby.Python的动态特性,例如动态类型.动态加载.动态绑定等.今天我们着重介绍ObjC中的键值编码(KVC).键值监听(KVO)特性: 键值编码KVC 键值监听KVO 键值编码KVC 我们知道在C#中可以通过反射读写一个对象的属性,有时候这种方式特别方便,因为你可以利用字符串的方式去动态控制一个对象.其实由于ObjC的语言特性,你根部不必进行任何操作就可以进行属性的动态读写,这种方式就是Key Valu

iOS开发——代码生成TabBar与视图切换详解

我在之前多篇博客中讲解了在不使用storyboard而使用nib文件的情况下,使用代码生成导航栏并进行跳转,具体可以参考<iOS开发--界面跳转与返回及视图类型详解><iOS纯代码实现界面建立.跳转.导航栏(无storyboard.无nib)(Objective-C)>.今天我来讲解下在使用nib搭建界面的情况下,用代码生成TabBar,并进行界面之间的跳转.代码示例已经上传至:https://github.com/chenyufeng1991/TabBarTest   . (1)

《招一个靠谱的移动开发》iOS面试题及详解(上篇)

多线程.特别是NSOperation 和 GCD 的内部原理. 运行时机制的原理和运用场景. SDWebImage的原理.实现机制.如何解决TableView卡的问题. block和代理的,通知的区别.block的用法需要注意些什么. strong,weak,retain,assign,copy nomatic 等的区别. 设计模式,mvc,单利,工厂,代理等的应用场景. 单利的写法.在单利中创建数组应该注意些什么. NSString 的时候用copy和strong的区别. 响应值链. NSTi