• iOS 中这些是否熟练掌握——(2)


    接上一篇博文,本篇博文是作者原创,用于记录从网上查阅的一些资料,并对自己的知识体系进行一下总结,成文以供学习使用。

    1.Cocoa Touch 包含了什么?不包含什么?与 Cocoa 有什么区别?

    • 相同之处:两者都包含OC运行时的两个核心框架:

    • cocoa包含Foundation和AppKit框架,可用于开发Mac OS X系统的应用程序

    • cocoa touch包含Foundation和UIKit框架,可用于开发iPhone OS 系统的应用程序

    • Cocoa时Mac OS X的开发环境,cocoa Touch是 Iphone OS的开发环境

    2.响应者连是什么(Responder chain)?他是如何发挥作用的?

    凡是直接或者间接的继承自 UIResponder 类创建的对象都是相应者,如:UIView、UIViewController 、UIApplication 由响应者对象组成的链叫响应者连。

    在视图被触摸的时候,系统会有碰撞检测过程,查找该触摸是发生在那个视图上。

    UIApplication->UIViewController->UIView ->视图1->视图2->视图3.

    碰撞检测之后,接下来对触摸事件进行处理,首先去看视图3是否对该事件作出相应,如果视图3不处理,就看视图2是否处理,以此往上面去请求询问,直到该触摸事件被处理,如果没有任何视图或者响应者对象去处理该触摸事件,则丢弃对该触摸事件的相应。

    3.什么是 method swizzling ? 参考

     method swizzling 就是改变一个已经存在的选择器对应的实现过程,它依赖于OC中的方法的调用能够在运行时进行改变———通过改变类的调度表(dispatch_table)中选择器到最终函数间的映射关系。

    4.什么是 Base64 、MD5 算法,什么是对称加密、什么是非对称加密 ?

     base64 是一种编码方式,将3字节的数据按照6比特去重新分组,分成4组,变成4字节的数据,高位使用0填充。一般用于电子邮件的编码。

     MD5算法:

    a.压缩性:任意长度的数据,计算出的MD5值的长度都是固定的。

    b.容易计算:从原数据计算出MD5值很容易。

    c.抗修改性:对原来的数据,哪怕是修改了一个字节的数据,所得到的MD5值都是有很大的区别。

    d.弱抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。

    e.强抗碰撞:想找到两个不同的数据,使它们具有相同的MD5值,是非常困难的。

    对称加密:

    需要对加密和解密使用相同密钥的加密算法。由于其速度快,对称性加密通常在消息发送方需要加密大量数据时使用。对称性加密也称为密钥加密。常用的对称加密算法有:DES、RC2、AES等等。

    非对称加密:

    与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。支付宝中的加密技术就是用的非对称加密技术。常用的非对称加密算法有:RSA、ECC、D-H等等。

    5.如何使用KVC(参考)去取得一个对象的属性值 ,什么是KVC、KVO、通知中心?

    KVC是OC语言的一个特性,可以对对象进行动态的读写操作。KVC的操作方法由 NSKeyValueCoding协议提供,而NSObject已经实现了这个协议,因此OC中的几乎所有对象都可以使用KVC操作。

    常用的KVC操作方法有:

    (1)设置属性值

      setValue:value forKey:key (用于简单路径,也就是直接属性)

      setValue: value forKeyPath:key (用于复杂路径,也就是间接属性)

    (2)获取属性值

      valueForKey: key  (用于获取简单路径的属性值)

      valueForKeyPath: key (用于获取复杂路径的属性值)

    KVC使用对应的函数即可设置值、获得值。那么,KVC是如何查找一个属性进行读取呢?KVC遵循下面的规则,假设要对name属性进行读取:

    (1)设置属性:优先考虑setName方法;如果没有,则搜索成员变量_name;如果没找到,则搜索成员变量name;如果还没有找到,则调用 setValue: forUndefineKey: 方法。

    (2)读取属性:优先考虑getName方法;如果没有,则搜索成员变量_name;如果没找到,则搜索成员变量name;如果还没有找到,则调用 valueForUndefineKey: 方法。

    KVC中有一个非常重要的方法: setValuesForKeysWithDictionary:dict ,该方法可以将一个字典映射到一个对象,省去了给对象一一赋值的步骤。

    实际上,setValuesForKeyWithDictionary:dict 方法就是遍历dict,对dict 中的每个键值调用 setValue: forKey: 方法。

    使用setValuesForKeyWithDictionary:dict一个需要注意的地方

    当字典中有某个值,而对象没有相应的属性时,会发生崩溃。这时候需要使用一个方法去避免这个崩溃的发生。该方法为: setValue: forUndefineKey: 方法

    KVO:

    KVO全称 Key Value Observing。使用KVO可以实现视图组件和数据模型的分离,视图作为监听器,当模型的属性值发生变化后,监听器可以做相应的处理。KVO的方法由NSKeyValueObserving协议提供,同样NSObject已经实现了该协议,因此几乎所有的对象都可以使用KVO。

    使用KVO操作常用的方法如下:

    注册制定路径的监听器: addObserver:  forKeyPath: option: context:

    删除制定路径的监听器:removeObserver: forKeyPath:

    触发监听时的方法:observeValueForKeyPath: ofObject: change: context: 

     通知中心不做过多啰嗦:参考

    说到通知中心必定要与代理进行区别:通知中心可以:1对1;1对多;但是代理只能是:1对1的。

    6.什么是 Sqllite ,Sqlite 与 coreData 的区别 ?参考

     Sqlite是一种轻量级的数据库,是一种关联式的数据管理系统,多使用在嵌入式应用系统中,在嵌入式系统应用中占用很少的内存(少到只有几百k的内存),但是作用非常的重要,可以说是 Sql Sever 的极大简化。

    CoreData 是一种数据框架,为我们提供了一个通用的数据管理解决方案。不论是什么样的程序,只要牵扯到数据管理,几乎都可以使用到 CoreData 。苹果的用户面板上已经为我们创建了关于CoreData 的控制面板,我们可以很方便的去对数据库进行迁移升级的操作。编程人员不需要写苦逼的SQL语句,这一切苹果都在我们创建CoreData 应用的时候已经创建完成,将我们的应用程序要是用的数据在内存中以数据对象模型的形式展示给系统使用。

    CoreData 对于处理对象的生命周期、对象之间的关系它做了一系列的自动化的解决方案,对于数据库的更新的时候,每次新增一个属性字段,数据库里面所有的数据字段都要跟着进行一下迁移(升级),这个数据库的升级可以通过Xcode 的控制面板来快速的升级。(参考4对应的博文里说的非常的好)

    Core Data 为数据变更管理、对象存储、对象读取恢复的功能提供了支持。 它可以使用SQLite作为持久化存储的类型。 它本身并不是一个数据库(这点很重要,比如,你可以使用Core Data来记录数据变更,管理数据,但并不能用它向文件内存储数据)。

    CoreData 支持的4种数据类型的存储:SQLitestore、XMLStore、BinaryStore、InMemoryStore.(这里与 博文对比学习

    下面是来自互联网的解说。上面是自己的总结: 参考1  餐卡2  参考3  参考4

    用于处理所有应用程序的数据模型需求,不论程序的规模大小。您可以在此基础上构建任何应用程序,从联系人管理程序到矢量图演示程序。只有您想不到的,没有什么做不到。几乎所有需要管理数据对象的应用程序都能获益于 Core Data。 Interface Builder 是苹果的图形用户界面编辑器,提供了预制的 Core Data 控制器对象,用于消除应用程序的用户界面和数据模型之间的大量粘合代码。您不必担心 SQL 语法,不必维护逻辑树来跟踪用户行为,也不必创建一个新的持久化机制。这一切都已经在您将应用程序的用户界面连接到 Core Data 模型时自动完成了。 Core Data 将您的应用程序模型层组织为一组在内存中定义好的数据对象。Core Data 跟踪这些对象的变化,并可以根据需要恢复这些改变,比如用户可以执行撤销命令。然后,当需要保存应用程序数据对象的改变时,Core Data 负责将这些对象保存至持久性的存储介质。而且,它会将数据保存到普通文件中,用户可以通过 Finder 管理这些文件、用 Spotlight 搜索这些文件、将文件备份到 CD 以及通过电子邮件将它们发送给朋友和家人。 使用Core Data 框架,您可以创建一个托管对象模型,它为您的应用程序所使用的模型对象,即实体提供了抽象定义。这种实体-关系模型是采用 Xcode 的“数据模型设计”工具定义的,该工具为模型实体以及它们之间的关系提供了内容丰富的环境。它就像一套详细的方案为应用程序的数据类型和这些数据类型之间的关系服务。

    7.Block的内存管理?

    Block什么都不做的话,在栈区。当在Block内部去调用全局变量或者属性的时候在栈区。这时候需要去拷贝Block,在写属性的时候要用Copy,需要release,在MRC下如果在Block的内部使用了 self 就会导致对该控制器的引用计数加1,最后导致不能释放该 Block 就会造成不能释放的后果。有一个bloock 专门 release 的API,释放block写在 dealloc里面.在 block 内调用一个对象需要使用 __Block 去修饰该对象,这是在 MRC 的情况下。

    ARC 下需要将 __Block 改为 __weak.

    8.代理中为什么不用 weak 而用 assign?

    实际上 weak 会比 assign 更好 , 因为最后 weak 会将代理置为 nil ,但是 assign 不会这么做,代理中在写 assign 有的情况下会出现崩溃,而 weak 更好些。在 MRC下会用 assign 在 ARC  下当然要用 weak.

    9.多线程在GCD中的使用?参考

    GCD是一个管理线程的线程池,如何创建、如何回收、如何分配线程,都是由 GCD 自己安排的,只需要编程人员将应该做的任务放到对应的队列里即可。GCD的全称是 Grand Central Dispatch 。

    GCD中有多种队列,其中自定义队列有两种:串行队列、并行队列。无论是串行队列还是并行队列,他们都是 FIFO 先进先出的。

     串行队列:一次只能够执行一次任务的队列,执行完一次任务之后才能执行洗一次的任务。

     并行队列:一次可以执行多个任务。

    GCD中的操作有两种,同步操作,异步操作。

    同步操作:不会开辟新的线程。

    异步操作:  会新开辟线程。

    两种操作和两种队列,组合为4种情况,实际上,在开发中,有些组合基本上是不会用到的。

    组合一:串行队列 同步操作  任务一个一个的执行,实际上是顺序执行

    - (void)gcdDemo1

    {
        //串行队列+同步操作
        dispatch_queue_t queue = dispatch_queue_create("gcddemo", DISPATCH_QUEUE_SERIAL);
        for(int i = 0; i < 10; ++i){
            dispatch_sync(queue, ^{
                NSLog(@"%@ %d",[NSThread currentThread],i);
            });
        }
    }

    组合二:串行队列 异步操作   任务一个一个的执行,但是因为是异步操作,所以会开辟一个新的线程,所有的任务都会在新的线程上执行

    - (void)gcdDemo1
    {
    dispatch_queue_t queue = dispatch_queue_create("gcddemo", DISPATCH_QUEUE_SERIAL);
    //串行队列+异步操作
    for (int i = 0; i < 10; ++i){
    dispatch_async(queue, ^{
           NSLog(@"%@ %d",[NSThread currentThread],i);
        });
      }
    }

    组合三:并行队列 同步操作   并行队列一次可以开启多个线程,但是是同步操作,这样一次就只能在主线程中执行一个任务,并且是按照顺序执行的。

    - (void)gcdDemo2
    {
    dispatch_queue_t queue = dispatch_queue_create("gcddemo", DISPATCH_QUEUE_CONCURRENT);
    //并行队列+同步任务
    for(int i = 0; i < 10; ++i){
    dispatch_sync(queue, ^{
      NSLog(@"%@ %d",[NSThread currentThread],i);
          });
        }
    }

    组合四:并行队列 异步操作   并行队列一次可以开启多个线程;异步操作可以开启多个线程,这样一次能够执行多个任务,开启多个线程,每个任务的结束时间是不能够确定的,并且任务也不是顺序执行的。

    - (void)gcdDemo2
    {
    dispatch_queue_t queue = dispatch_queue_create("gcddemo", DISPATCH_QUEUE_CONCURRENT);
    //并行队列+异步任务
    for(int i = 0; i < 10; ++i){
    dispatch_async(queue, ^{
          NSLog(@"%@ %d",[NSThread currentThread],i);
         });
       }
    }

     注意:为了开发方便,苹果提供了全局队列,全局队列实际上是一个并行队列。

    全局队列 + 同步任务: 执行效果是一条一条执行,每一条执行的时候,都会开启新的线程,在新的线程中执行任务。

    - (void)gcdDemo3
    {
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //全局队列+同步任务
    for (int i = 0; i < 10; ++i){
    //同步任务
    dispatch_sync(queue, ^{
    NSLog(@"%@ %d",[NSThread currentThread],i);
         });
       }
    }

    全局队列 + 异步任务: 执行效果是与上面的组合四是同样的。

     - (void)gcdDemo3

    {
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //全局队列+异步任务
    for(int i = 0; i < 10; ++i){
    dispatch_async(queue, ^{
         NSLog(@"%@ %d",[NSThread currentThread],i);
         });
       }
    }

    注意:苹果还提供了一种队列是主队列,主队列是串行队列,但是与单一的串行队列有差别,该主队列上的任务都是在主线程中执行,即便是异步任务在主队列上执行,系统也不会开启新的线程。

    主队列+异步任务:

    - (void)gcdDemo4
    {
    dispatch_queue_t queue = dispatch_get_main_queue();
    //主队列+异步任务
    for(int i = 0; i < 10; ++i){
    dispatch_async(queue,^{
       NSLog(@"%@ %d",[NSThread currentThread],i);
         });
       }
    }

    主队列+同步任务(会阻塞线程)

    - (void)gcdDemo4
    {
    dispatch_queue_t queue = dispatch_get_main_queue();
    //主队列+同步任务,会阻塞
    for(int i = 0; i < 10; ++i){
    dispatch_sync(queue, ^{
       NSLog(@"%@ %d",[NSThread currentThread],i);
         });
       }
    }

    主队列中本身是有一个任务A的(主任务),且该任务A还没有执行完。在执行任务A的过程中,又插入了新的同步任务B。我们知道,串行队列中,必须先执行完一个任务后,才能继续执行另一个任务。此时的情况时:

    若想继续执行任务A,需要先把任务B执行完,若想继续执行任务B,需要先把任务A执行完,因此造成了阻塞。

    在开发中,应该避免这种阻塞的情况。

    10.如何对 UITableView  做性能优化? 参考  参考

    a.使用不透明的视图。不透明的视图能够极大的提高渲染速度。

        如果可以的话,将 UItableView 的 cell 以及 其子视图的 opaque 设置为 YES (参考)这样设置 ios 可以让系统以最优的方式来绘制view。包括图像的 alpha 值也要设置为1.不要 使用这个 clearColor。

    b.不要创建不必要的重复的 cell 。可以设置一个重用的 cellid

    static NSString *CellIdentifier = @"xxx";

    UITableViewCell *cell = [tableViewdequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil) {

        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];

    }

    在cell 重用的时候,它的内部绘制的内容并不会被自动的清除,我们可以调用 setNeedsDisplay 方法。

    c.尽可能的减少视图的数目

    d.不要做多余的绘制工作

    e.预渲染图像

    f.不要阻塞主线程

    g.也可以设置一下,提前去加载数据,解决用户的等待加载数据的情况,可以在用户浏览到倒数第10条数据的时候,让线程去加载数据。

    11.常见的设计模式?参考1 参考2 

    代理设计模式、观察设计模式、MVC设计模式、单例设计模式、策略设计模式、工厂设计模式。

     对于单利设计模式,苹果API中 UIAccelerometer 和 UIAppdeleagte 类都是单例类。

    观察设计模式,可以让View作为监听者,让其去监听数据模型的变化,如果数据模型的值变化,就对相应去改变监听者的的UI。

    MVC设计模式就是经常用的到的,没啥可啰嗦了。

    代理设计模式也是非常常用的,UIPickView 、UITableView 等等都用到了代理设计模式,传值中也常用。

    12.关于Block 的使用?参考

    13.什么是 MVVM设计模式? 参考

    在理解MVC的设计模式基础上,M层顾名思义,就是数据层,数据传输对象的封装;V:就是展示层,就是ViewController层,他的任务就是从ViewModel层获取数据,然后显示;VM:就是View和Model层的粘合剂,他是一个放置用户输入验证逻辑,视图显示逻辑,发起网络请求和其他各种各样的代码的极好的地方。说白了,就是把原来ViewController层的业务逻辑和页面逻辑等剥离出来放到ViewModel层。

    MVVM 其实是一个 MVC 的增强版,并将表示逻辑从 Controller 移出放到一个新的对象里,即 View Model。在 iOS 上使用 MVVM 的动机,就是让它能减少 View Controller 的复杂性并使得表示逻辑更易于测试。

    14.XML 与 Json 的区别?

     对于JSON数据的解析,网上一直有人说,它要代替xml,成为网络数据的通用格式。从 IOS 5 之后苹果推出了原生的json 解析 API(NSJSONSerialization)。我们依旧可以使用一些第三方去解析如:SBJson、JSONKit、TouchJson。使用原生的解析类解析数据最快,JSONKit的速度次之。

    XML的解析主要有:SAX 解析:基于事件的回调的解析机制,(主要区别于 DOM 解析)一行一行的解析 (缺点:效率低),适合大数据解析。系统提供好的类 NSXMLParser。.DOM 解析 : 把解析数据全部读入内存,初始化为树形结构,然后再进行逐层的分析,相对于 SAX 解析,效率高,不适合大数据的读取。一般都是采用谷歌提供的第三方类 GDataXMLNode。

    XMl适合于较复杂的数据结构,而JSON 适合简单的数据结构。前者传输速度较慢,后者较快。

    在解析数据的时候,对于 Html 的解析要使用到 XPath ,对于数据类型为 NSNumber 类的数据要小心,首先要做一下格式化处理。

    15.NSURLConnection 与 NSURLSession 的区别? 参考

    NSURLConnection:

    NSURLConnection 是一组系统的构成 Foundation 框架中的 URL  加载系统的互相关联的组件:NSURLRequest, NSURLResponse, NSURLProtocol , NSURLCache,NSHTTPCookieStorage,NSURLCredentialStorage,以及和它同名的 NSURLConnection。

    NSURLRequest对象被传递给一个NSURLConnection对象。委托(遵守从前的非正式<NSURLConnectionDelegate> 和  <NSURLConnectionDataDelegate>协议)作为一个NSURLResponse异步响应,任何相关的NSData从服务器发送。

    在一个请求发送到服务器之前,首先去访问共享的高速缓存,然后根基策略和可用性,一个缓存的响应可以立即被返回过来,如果所有缓存的响应都不可用,则该请求根据选项,被用于为任何后续请求缓存它的响应。

    在协商发送一个请求到服务器的过程中,该服务器可发出验证质询,这可以由共享的cookie,证书存储(credential storage)或通过连接委托自动处理。必要的时候,为了无缝地改变装载行为,传出请求也可以被注册的NSURLProtocol对象截获。**

    NSURLSession:

    与NSURLConnection类似,除了同名类NSURLSession,NSURLSession也是指一组相互依赖的类。NSURLSession包括与之前相同的组件,例如NSURLRequest, NSURLCache等。NSURLSession的不同之处在于,它把 NSURLConnection替换为NSURLSession, NSURLSessionConfiguration,以及3个NSURLSessionTask的子类:NSURLSessionDataTask, NSURLSessionUploadTask, 和NSURLSessionDownloadTask.

    与NSURLConnection相比,NSURLSession最直接的改善就是提供了配置每个会话的缓存,协议,cookie和证书政策(credential policies),甚至跨应用程序共享它们的能力。这使得框架的网络基础架构和部分应用程序独立工作,而不会互相干扰。每一个NSURLSession对象都是根据一个NSURLSessionConfiguration初始化的,该NSURLSessionConfiguration指定了上面提到的政策,以及一系列为了提高移动设备性能而专门添加的新选项。

    NSURLSession 的另一个重要的组成部分是会话任务,他负责处理数据的加载,以及客户端与服务器端之间的文件和数据的上传和下载服务。

     NSURLSessionTask与NSURLConnection是及其相似的,因为它负责加载数据,而主要的区别在于,任务共享它们父类NSURLSession的共同委托(common delegate)。 
    NSURLSessionTask是一个抽象子类,它有三个具体的子类是可以直接使用的:NSURLSessionDataTask,NSURLSessionUploadTask和NSURLSessionDownloadTask。这三个类封装了现代应用程序的三个基本网络任务:获取数据,比如JSON或XML,以及上传下载文件。

    当一个NSURLSessionDataTask完成时,它具有关联的数据,而一个NSURLSessionDownloadTask完成时,它具有一个已下载文件的临时文件路径。

    NSURLSessionUploadTask 继承了 NSURLSessionDataTask,因为服务器响应一个上传请求时,往往伴随着相关联的数据。 所有任务均可撤销,也可以暂停和恢复。当一个下载任务被取消时,它可以选择创建恢复数据,然后可以传递给下一次新创建的下载任务,以便继续之前的下载。

    16.什么是 NSKeyedArchiver?

    17.我们在使用 XIB 和 Storyboard 的时候 有四个文件 View.xib 、 Empty.xib 、Launch Screen.storyboard、Storyboard.storyboard 他们都有什么区别?

     XIB 与 Storyboard 的区别:两者都是用来描述软件界面的设计,前者是轻量级的,主要用于一些局部的UI的设计,后者是重量级的,主要用于复杂页面的设计,可以设计模块级别的页面UI设计。

    View.xib 与  Empty.xib 都是 XIB 轻量级的用户界面编程组件。

    Launch Screen.storyboard 与 Storyboard.storyboard 都是 Storyboard 重量级的用户界面编程组件。

    NIB 和 XIB 相同点:都是 可视化编程的图形界面设计文档。可视化编程 Interface Bulider 将窗口,菜单,以及窗口上的各种组件都”冻“在了一个 nib 文档中,在程序运行的期间,这些文档就会被激活。

    NIB 和 XIB 异同点:NIB为二进制文件,XIB是纯文本文件,后者方便于我们版本控制和对文件的比较。XIB 文件可以被编译成二进制文件。

    18.在 XIB 的构成成分中有 3 个图标,分别是什么?具有什么样的功能?

    File's Owner 就是所有 nib 文件中的每一个图标,标示的是从系统的磁盘读取的文件。

    First Responder 是当前用户正在交互的交互对象。

    View 是用户用于显示用户交互事件的视图,完成用户的交互,是 UIView 类以及其之类。

    19.关于公钥、私钥、数字证书的理解? 参考

    20.类目与继承的区别,在什么情况下使用继承不用类目? 参考

    21.野指针是什么,如何理解? 参考

    所谓野指针:指向一个已删除的对象或未申请访问受限内存区域的指针。
    与空指针不同,野指针无法通过简单地判断是否为 NULL避免,而只能通过养成良好的编程习惯来尽力减少。对野指针进行操作很容易造成程序错误。
    野指针的成因主要有两种: 
    (1)指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。 
    (2)指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。别看free和delete的名字恶狠狠的(尤其是delete),它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。通常会用语句if (p != NULL)进行防错处理。很遗憾,此时if语句起不到防错作用,因为即便p不是NULL指针,它也不指向合法的内存块。
    例: 
    char *p = (char *) malloc(100); 
    strcpy(p, “hello”); 
    free(p); // p 所指的内存被释放,但是p所指的地址仍然不变 
    if(p != NULL) // 没有起到防错作用 
    strcpy(p, “world”); // 出错

  • 相关阅读:
    异或(^/XOR)的研究
    FastJson的使用
    KindEditor使用
    Java之Collection一
    Java之String
    Github基本使用
    Ubuntu jekyll git使用小记
    使用html editor 打开freemarker文件
    struts.xml 标签顺序
    multipart/form-data ajax 提交问题(未解决)
  • 原文地址:https://www.cnblogs.com/benpaobadaniu/p/5211607.html
Copyright © 2020-2023  润新知