iOS程序员面试分类模拟12_第1页
iOS程序员面试分类模拟12_第2页
iOS程序员面试分类模拟12_第3页
iOS程序员面试分类模拟12_第4页
iOS程序员面试分类模拟12_第5页
已阅读5页,还剩11页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

iOS程序员面试分类模拟12简答题1.

什么是线程?线程与进程有什么区别?为什么要使用多线程?正确答案:线程指程序在执行过程中,能够执行程序代码的一个执行单元。线程主要有4种状态:运行、就绪、挂起(江南博哥)、结束。

进程指一段正在执行的程序。而线程有时候也被称为轻量级进程,是程序执行的最小单元。一个进程可以拥有多个线程,各个线程之间共享程序的内存空间(代码段、数据段和堆空间)及一些进程级的资源(例如打开的文件),但是各个线程拥有自己的栈空间。进程与线程的关系如图所示。

进程与线程的关系

在操作系统级别上,程序的执行都是以进程为单位的,而每个进程中通常都会有多个线程互不影响地并发执行,那么为什么要使用多线程呢?其实,使用多线程为程序研发带来了巨大的便利。具体而言,有以下几个方面的内容:

1)使用多线程可以减少程序的响应时间。在单线程(单线程指程序执行过程中只有一个有效操作的序列,不同操作之间都有明确的执行先后顺序)的情况下,如果某个操作很耗时,或者陷入长时间的等待(如等待网络响应),那么此时程序将不会响应鼠标和键盘等操作,使用多线程后,可以把这个耗时的线程分配到一个单独的线程去执行,使得程序具备了更好的交互性。

2)与进程相比,线程的创建和切换开销更小。由于启动一个新的线程必须给这个线程分配独立的地址空间,建立许多数据结构来维护线程代码段、数据段等信息,而运行于同一进程内的线程共享代码段、数据段,所以线程的启动或切换的开销比进程要少很多。同时多线程在数据共享方面效率非常高。

3)多CPU或多核计算机本身就具有执行多线程的能力,如果使用单个线程,那么将无法重复利用计算机资源,造成资源的巨大浪费。因此,在多CPU计算机上使用多线程能提高CPU的利用率。

4)使用多线程能简化程序的结构,使程序便于理解和维护。一个非常复杂的进程可以分成多个线程来执行。

2.

如何理解多线程?正确答案:多线程(multithreading)指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件的支持而能够在同一时间执行多个线程,进而提升整体处理性能。要深入了解iOS中的多线程技术,先需要了解以下几个概念。

1.进程

每个正在系统上运行的程序都是一个进程。每个进程之间是相互独立的,每个进程均运行在其专用且受保护的内存空间内。进程在系统内存中的关系如图所示。

系统内存中的进程

通过“活动监视器”可以查看Mac系统中开启的所有进程(见图)。

活动监视器中的进程

2.线程

线程是一组指令的集合,或者是程序的特殊段,它可以在程序里独立执行。每个进程包含至少一个线程。线程基本上是轻量级的进程,它负责在单个程序中执行多个任务。通常由系统负责多个线程的调度和执行。

需要注意的是,在同一个线程中执行的任务是串行的。也就是说,在同一时间内,一个线程只能执行一个任务。

线程是程序中一个单一的顺序控制流程。在单个程序中同时运行多个线程完成不同的工作,称为多线程。多线程技术主要是为了充分利用CPU,提高程序的执行效率。多线程之间的关系如图所示。

多线程之间的关系

事实上,在同一时间,CPU只能处理一条线程,只有一条线程在执行。多线程的并发执行,其实就是CPU快速地在多条线程之间调度。如果CPU调度线程的速度足够快,那么就会产生多条线程并发执行的假象。

多线程技术的优点如下:

1)可以很大程度上提高程序的执行效率,提高程序的响应速度。

2)使用线程可以把占据时间长的程序中的任务放到后台去处理,这样就不会阻塞程序主线程,用户体验更好(耗时操作会卡住主线程,严重影响UI的流畅度)。

3)提高计算机资源的利用率,如:更加充分利用内存,多CPU计算机上提高CPU利用率。

4)将进程分块,优化简化程序逻辑结构。

多线程技术的缺点如下:

1)多线程程序的代码会更加复杂、难读,增加交接和维护难度。

2)创建和调度线程会有额外的开销,线程越多,开销越大,甚至反而降低程序的性能。

3)通常模型数据是在多个线程间共享的,需要防止线程死锁情况的发生。

因此,在实际使用多线程时,需要适当开启线程,当线程使用完成后,需要及时释放资源。在最新的iOS技术中,已经很少需要直接操作线程了,因为苹果公司已经将大部分复杂的操作封装好。

3.

如何理解HTTP协议?正确答案:超文本传输协议(HyperTextTransferProtocol,HTTP)是互联网上应用最为广泛的一种网络协议。简单来说,HTTP是客户端和服务器端之间请求和应答的标准,它可以使浏览器(或其他客户端)更加高效,使网络传输减少。它不仅保证计算机正确快速地传输超文本文档,还确定传输文档中的哪一部分,以及哪部分内容优先显示(如文本先于图形)等。

一次HTTP操作称为一个事务,其工作过程可分为4步:

1)客户端与服务器需要建立连接。例如,单击某个超链接后,浏览器和服务器将建立通信连接。

2)建立连接后,客户端发送一个请求给服务器,请求方式的格式为:统一资源标识符(URL)、协议版本号,后边是MIME信息包括请求修饰符、客户端信息和可能的内容。

3)服务器接到请求后,给予相应的响应信息,其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是MIME信息包括服务器信息、实体信息和可能的内容。

4)客户端接收服务器所返回的信启、通过浏览器显示在用户的显示屏上,然后客户机与服务器断开连接。

如果在以上过程中的某一步出现错误,那么产生错误的信息将返回到客户端,由显示屏输出。对于用户来说,这些过程是由HTTP自己完成的,用户只要用鼠标单击,等待信息显示就可以了。

通常HTTP消息包括客户端向服务器的请求消息和服务器向客户端的响应消息。这两种类型的消息由一个起始行,一个或者多个头域,一个指示头域结束的空行和可选的消息体组成。HTTP的头域包括通用头、请求头、响应头和实体头4个部分。每个头域由一个域名、冒号(:)和域值组成。域名是大小写无关的,域值前可以添加任何数量的空格符,头域可以被扩展为多行,在每行开始处,使用至少一个空格或制表符。

一个完整的由客户端发给服务器的HTTP请求包含以下两部分内容。

1)请求头:包含了对客户端的环境描述、客户端请求信息等。

GET/minion.pngHTTP/1.1

//包含了请求方法、请求资源路径、HTTP协议版本

Host:86:32812

//客户端想访问的服务器主机地址

User-Agent:Mozilla/5.0

//客户端的类型,客户端的软件环境

Accept:text/html

//客户端所能接收的数据类型

Accept-Language:zh-cn

//客户端的语言环境

Accept-Encoding:gzip

//客户端支持的数据压缩格式

2)请求体:客户端发给服务器的具体数据,例如文件数据(POST请求才会有)。

同样,一个完整的HTTP响应中应该包含以下两部分内容。

1)响应头:包含了对服务器的描述、对返回数据的描述。

HTTP/1.1200OK

//包含了HTTP协议版本、状态码、状态英文名称

Server:Apache-Coyote/1.1

//服务器的类型

Content-Type:image/jpeg

//返回数据的类型

Content-Length:56811

//返回数据的长度

Date:Mon,23Jun201412:54:52GMT

//响应的时间

2)响应体:服务器返回给客户端的具体数据,例如文件数据。

常见的响应状态码如图所示。

常见的响应状态码

此外,在iOS开发中发送HTTP请求有以下几种方案:

1)NSURLConnection。iOS7之前,通常使用NSURLConnection及关联类,这是iOS开发中最经典的网络请求方案。

2)NSURLSession。iOS7之后,逐渐取代NSURLConnection,现在已经是iOS开发中最常用的网络请求方式。

3)CFNetwork。它是NSURLConnection和NSURLSession的底层C语言实现,很少在开发中使用。

4)其他第三方网络请求框架,如Objective-C中的AFNetworking和Swift中的Alamofire等。这些网络框架也基本上都是对其他3种方案的封装与拓展。

4.

iOS9以后如何使用HTTP请求?正确答案:在iOS9之后引入了新特性AppTransportSecurity(ATS)。新特性要求App内访问的网络必须使用HTTPS协议。由于当前很多项目仍然使用HTTP协议和私有加密协议进行网络请求,所以需要在项目的info.plist文件中做如下配置:

1)在Info.plist中添加NSAppTransportSecurity类型Dictionary。

2)在NSAppTransportSecurity下添加NSAllowsArbitraryLoads类型Boolean,值设为YES。或者,如果有部分请求需要使用HTTP协议,那么可以考虑添加白名单,即NSExceptionDomains。用鼠标右键单击info.plist,选择OpenAs选项下的SourceCode,在打开的文件中添加如下代码,其中ExceptionDomain是需要设置的白名单域名。

<key>NSAppTransportSecurity</key>

<dict>

<key>NSExceptionDomains</key>

<dict>

<key>ExceptionDomain</key>

<dict>

<key>NSIncludesSubdomains</key>

<true/>

</dict>

<key></key>

<dict>

<key>NSIncludesSubdomains</key>

<true/>

</dict>

</dict>

</dict>

5.

HTTPS协议与HTTP协议有什么区别与联系?正确答案:HTTP协议被用于在Web浏览器和网站服务器之间传递信息。HTTP协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了Web浏览器和网站服务器之间的传输报文,那么就可以直接读懂其中的信息。因此,HTTP协议不适合传输一些敏感信息,如信用卡号、密码等。

为了解决HTTP协议的这一缺陷,需要使用另一种协议:安全套接字层超文本传输协议(HTTPS)。为了数据传输的安全,HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。

HTTPS和HTTP的区别主要为以下4点:

1)HTTPS协议需要从CA(CertificateAuthority)申请一个用于证明服务器用途类型的证书,一般免费证书很少,需要交费。

2)HTTP是超文本传输协议,信息是明文传输,HTTPS则是具有安全性的SSL加密传输协议。

3)HHTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。

4)HTTP的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比HTTP协议安全。

6.

UIKit类要在哪一个应用线程上使用?正确答案:UIKit的界面类只能在主线程上使用,对界面进行更新,多线程环境中要对界面进行更新必须要切换到主线程上。

例如下面的问题代码:

@interfaceTTWaitController:UIViewController

@property(strong,nonatomic)UILabel*alert;

@end

@implementationTTWaitController

-(void)TiewDidLoad

{

CGRectframe=CGRectMake(20,200,200,20);

self.alert=[[UILabelalloc]initWithFrame:frame];

self.alert.text=@"Pleasewait10seconds...";

self.alert.textColor=[UIColorwhiteColor];

[self.viewaddSubview:self.aleft];

NSOperationQueue*waitQueue=[[NSOperationQueuealloc]init];

[waitQueueaddOperationWithBlock:^{

[NSThreadsleepUntilDate:[NSDatedateWithTimeIntervalSinceNow:10]];

self.alert.text=@"Thanks!";

}];

}

@end

@implementationTTAppDelegate

-(BOOL)application:(UIApplication*)application

didFinishLaunchingWithOptions:(NSDictionary*)launchOptions

{

self.window=[[UIWindowalloc]initWithFrame:[[UIScreenmainScreen]bounds]];

self.window.rootViewController=[[TTWaitControlleralloc]init];

[self.windowmakeKeyAndVisible];

returnYES;

}

这段代码是想提醒用户等待10s,10s后在标签上显示“Thanks”,但多线程代码部分NSOperationQueue的addOperationWithBlock函数不能保证block里面的语句是在主线程中运行的,UILabel显示文字属于UI更新,必须要在主线程进行,否则会有未知的操作,无法在界面上及时正常显示。

解决方法是将UI更新的代码写在主线程上。代码同步到主线程上主要有3种方法:NSThread、NSOperationQueue和GCD,3个层次的多线程都可以获取主线程并同步。

7.

iOS中有哪几种从其他线程回到主线程的方法?正确答案:iOS中只有主线程才能立即刷新UI。如果是通过侦听异步消息,触发回调函数,或者调用异步方法,请求刷新UI,那么都会产生线程阻塞和延迟的问题。如果要在其他线程中执行刷新UI的操作,那么就必须切换回主线程。主要由以下3种方式。

1.NSThreadPerformAdditions协议

这个协议提供了两个切换到主线程的API。

-(void)performSeleetorOnMainThread:(SEL)aSelectorwithObject:

(nullableid)argwaitUntilDone:(BOOL)waitmodes:(nullableNSArray<NSString*>*)array;

-(void)performSelectorOnMainThread:(SEL)aSelectorwithObject:

(nullableid)argwaitUntilDone:(BOOL)wait;

2.GCD

使用GCD的dispatch_get_main_queue()方法可以获取主队列,主队列中的任务必定是在主线程中执行的。

dispacch_async(dispatch_get_main_queue(),^{

});

3.NSOperationQueue

和GCD一样,使用NSOperationQueue提供+mainQueue方法可以获取主队列,再将刷新UI的任务添加到主队列中。

[[NSOperationQueuemainQueue]addOperationWithBlock:^{

}];

示例代码如下:

-(void)goToMainThread{

/*开启子线程下载图片*/

dispatch_async(dispatch_get_global_queue(0,0),^{

NSData*imageData=[[NSDataalloc]initWithContentsOfURL:[NSURLURLWithString:@"/pe0060886.jpg"]];

_image=[UIImageimageWithData:imageData];

/*切换到主线程显示*/

//1.NSThread

//[selfperformSeteetorOnMainThread:@selector(changeBg)withObject:nilwaitUntilDone:NO];

//2.GCD

//dispatch_async(dispatch_get_main_queue(),^{

//[selfehangeBg];

//});

//3.NSOperationQueue

[[NSOperationQueuemainQueue]addOperationWithBlock:^{

[selfchangeBg];

}];

});

}

8.

用户下载一个大图片,分成很多份下载,如何使用GCD实现?正确答案:使用DispatchGroup追加block到GlobalGroupQueue,这些block如果全部执行完毕,那么就会执行通过dispatch_group_notify添加到主队列中的block,进行图片的合并处理。

dispatch_queue_tqueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

dispatch_group_tgroup=dispatch_group_create();

dispatch_group_async(group,queue,^{/*加载图片1*/});

dispatch_group_async(group,queue,^{/*加载图片2*/});

dispatch_group_async(group,queue,^{/*加载图片3*/});

dispatch_group_notify(group,dispatch_get_main_queue(),^{

//合并图片......

});

9.

项目中什么时候选择使用GCD?什么时候选择NSOperation?正确答案:下图简单概括了NSOperation和GCD各自的特点。NSOperation是对线程的高度抽象,提供Objective-C语言接口,具备面向对象特性(复用、封装),可子类化NSOperation进行操作对象的复用,使程序逻辑结构更清晰。比起GCD,NSOperation可以直接设置操作之间的依赖关系实现线程同步,而且可以通过调用cancel方法中途取消操作,而在GCD中不可取消。NSOperation的线程逻辑控制更加精细灵活,同时复杂度较GCD要高,因此建议在有特殊需求的复杂项目中使用。

NSOperation和GCD的特点

GCD的最大优点是非常简单、易用,经过了官方的优化更加安全高效(多数函数是线程安全的)。对于不复杂的多线程操作,会节省代码量,尤其block参数的使用,使代码更为易读,建议在简单项目中广泛使用。

10.

NSOpertion如何实现线程依赖?正确答案:对于a、b、c3个线程,如何使用NSOpertion和NSOpertionQueue实现执行完a和b后再执行c的结果?

对于上述问题,其实可以通过NSOpertion的依赖特性实现,即让c依赖于a和b,这样只有a和b都执行完后,c才可以开始执行。

/*获取主队列(主线程)*/

NSOperationQueue*queue=[NSOperationQueuemainQueue];

/*创建a、b、c操作*/

NSOperation*a=[NSBlockOperationblockOperationWithBlock:^{

NSLog(@"OperationAStart!");

[NSThreadsleepForTimeInterval:3.0];

NSLog(@"OperationADone!");

}];

NSOperation*b=[NSBlockOperationblockOperationWithBlock:^{

NSLog(@"OperationBStart!");

[NSThreadsleepForTimeInterval:3.0];

NSLog(@"OperationBDone!");

}];

NSOperation*C=[NSBlockOperationbloekOperationWithBlock:^{

NSLog(@"OperationCStart!");

//......

}];

/*添加操作依赖,c依赖于a和b*/

[caddDependency:a];

[caddDependency:b];

/*添加操作a、b、c到操作队列queue(特意将c在a和b之前添加)*/

[queueaddOperation:c];

[queueaddOperation:a];

[queueaddOperation:b];

程序的输出结果为:

2017-03-1813:51:37.770SingleView[15073:531745]OperationAStart!

2017-03-1813:51:40.772SingleView[15073:531745]OperationADone!

2017-03-1813:51:40.775SingleView[15073:531745]OperationBStart!

2017-03-1813:51:43.799SingleView[15073:531745]OperationBDone!

2017-03-1813:51:43.800SingleView[15073:5317451OperationCStart!

11.

dispatch_barrier_(a)sync的作用是什么?正确答案:通过dispatch_barrier_async添加的操作会暂时阻塞当前队列,即等待前面的并发操作都完成后执行该阻塞操作,待其完成后后面的并发操作才可继续(见图)。可以将其比喻为一根霸道的独木桥,是并发队列中的一个并发障碍点,或者说中间瓶颈,临时阻塞并独占。注意,dispatch_barrier_async只有在并发队列中才能起作用,在串行队列中队列本身就是独木桥,将失去其意义。

dispatch_barrier_async阻塞队列

可见使用dispatch_barrier_async可以实现类似dispatch_group_t组调度的效果,同时主要的作用是避免数据竞争,高效访问数据。

/*创建并发队列*/

dispatch_queue_tconcurrentQueue=dispatch_queue_create("test.concurrent.queue",DISPATCH_QUEUE_CONCURRENT);

/*添加两个并发操作A和B,即A和B会并发执行*/

dispatch_async(concurrentQueue,^(){

NSLog(@"OperationA");

});

dispatch_async(concurrentQueue,^(){

NSLog(@"OperationB");

});

/*添加barrier障碍操作,会等待前面的并发操作结束,并暂时阻塞后面的并发操作直到其完成*/

dispatch_barrier_async(concurrentQueue,^(){

NSLog(@"OperationBarrier!");

});

/*继续添加并发操作C和D,要等待barrier障碍操作结束才能开始*/

dispatch_async(concurrentQueue,^(){

NSLog(@"OperationC");

});

dispatch_async(concurrentQueue,^(){

NSLog(@"OperationD");

});

程序的输出结果为:

2017-04-0412:25:02.344SingleView[12818:3694480]OperationB

2017-04-0412:25:02.344SingleView[12818:3694482]OperationA

2017-04-0412:25:02.345SingleView[12818:3694482]OperationBarrier!

2017-04-0412:25:02.345SingleView[12818:3694482]OperationD

2017-04-0412:25:02.345SingleView[12818:3694480]OperationC

12.

如何理解线程安全?正确答案:在实际开发中,如果在程序中使用了多线程技术,那么有可能会遇到同一块资源被多个线程共享的情况,也就是多个线程可能会访问同一块资源,如多个线程访问同一对象、变量、文件等。因此,当多个线程访问同一块资源时,很容易会发生数据错误及数据不安全等问题。

要避免这种因争夺资源而导致的数据安全问题,需要使用“线程锁”来解决,即在同一时间段内,只允许一个线程来使用资源。在iOS开发中主要使用如下几种线程锁技术。

1.@synchronized关键字

使用@synchronized能够很方便地隐式创建锁对象。示例代码如下:

-(void)testSynchronized{

Animal*someone=[Animalnew];

/*线程A*/

dispatch_async(dispatch_get_global_queue(0,0),^{

@synchronized(someone){

NSLog(@"线程A=%@",[NSThreadcurrentThread]);

=@"□";

[NSThreadsleepForTimeInterva!:5];

}

});

/*线程B*/

dispatch_async(dispatch_get_global_queue(0,0),^{

@synchronized(someone){

NSLog(@"线程B=%@",[NSThreadcurrentThread]);

=@"

";

}

});

}

程序的输出结果为:

2017-04-1217:00:49.495test[91509:22528009]线程A=<NSThread:0x608000261a00>{number=5,name=(null)}

2017-04-1217:00:54.570test[91509:22529035]线程B=<NSThread:0x600000265840>{number=6,name=(null)}

可以发现线程B访问资源的时间比线程A要晚5s。关键字@synchronized将实例对象someone设定为锁的唯一标识,只有标识相同时,才满足互斥。如果线程B中锁的标识改为其他对象,那么线程B将不会被阻塞。

2.NSLock

NSLock对象是iOS中为了保证临界资源的原子性操作和临界区的原子性执行而封装的。需要注意的是,NSLock对象中的unlock操作必须与lock操作使用相同的线程,否则会出现未知错误。此外,同一线程在lock之后,未unlock之前,再次lock会导致永久性死锁。

示例代码如下:

-(void)testNSLock{

Animal*someone=[Animalnew];

/*创建锁对象*/

NSLock*alock=[[NSLockalloc]init];

/*线程A*/

dispatch_async(dispatch_get_global_queue(0,0),^{

/*尝试上锁*/

if([alocktryLock]){//[alocklock];

NSLog(@"线程A=%@",[NSThreadcurrentThread]);

=@"□";

[NSThreadsleepForTimeInterval:5];

/*开锁*/

[alockunlock];

}

});

/*线程B*/

dispatch_async(dispatch_get_global_queue(0,0),^{

if([alocktryLock]){//[alocklock];

YSLog(@"线程B=%@",[NSThreadcurrentThread]);

=@"

";

[alockunlock];

}

});

}

程序的输出结果为:

2017-04-1316:29:41.086test[15054:24437220]线程A=<NSThread:0x6000002697c0>{number=3,name=(null)}

2017-04-1316:29:46.088test[15054:24437846]线程B=<NSThread:0x600000274980>{number=4,name=(null)}

结果显示线程B在等待线程A解锁之后,才会执行程序。如果线程B将lock和unlock方法去掉之后而不会被阻塞,那么这个和synchronized的一样,需要使用相同的锁对象才会互斥。

除了NSLock之外,iOS还提供了NSRecursiveLock类和NSConditionLock类,前者通常在递归操作中使用,后者可以创建一个指定开锁条件的锁对象,只有满足条件,才能开锁。

3.GCD

GCD提供了一种信号的机制,使用它可以创建锁。信号量是一个整型值并且具有一个初始计数值,可支持两个操作:信号通知和等待。当一个信号量被信号通知,其计数会被增加。当一个线程在一个信号量上等待时,线程会被阻塞(如果有必要),直至计数器大于零,然后线程会减少这个计数。

在GCD中有3个函数是semaphore的操作(见表)。semaphore操作的3个函数函数名作用dispatch_semaphore_create刨建一个semaphoredispatch_semaphore_signal发送一个信号dispatch_semaphore_wait等待信号

简单地介绍这3个函数,第一个函数有一个整型的参数,可以理解为信号的总量;dispatch_semaphore_signal是发送一个信号,自然会让信号总量加1;dispatch_semaphore_wait是等待信号,当信号总量少于0的时候就会一直等待,否则就可以正

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论