【iOS】—— RunLoop和多线程相关问题总结

RunLoop

1. 讲讲RunLoop,项目中有用到过吗?

RunLoop 的基本作用:保持程序的持续运行,节省 CPU 的资源,提高程序的性能 ( 没有事情,就请休眠,不要功耗。有事情,就处理)。
简单举个例子,如果用Xcode的Command Line Tool文件来写OC,在代码里创建一个NSTimer,它并不能正常运行,因为这个程序和APP不同,程序运行一次直接结束,这时候我们需要把Runloop给Run起来,NSTimer就能用了。

2. RunLoop内部实现逻辑

请添加图片描述

3. runloop与线程的关系

  • 每条线程,都有唯一的一个,与之对应的 RunLoop 对象
  • runloops[thread] = runloop
    RunLoop 保存在一个全局的 Dictionary 里,线程作为 Key, RunLoop 作为 Value
  • 线程刚创建时,并没有 RunLoop 对象, RunLoop 会在第一次获取它时创建
  • RunLoop 会在线程结束时,销毁
  • 主线程的runloop对象是默认就存在且默认是打开的,其他线程的runloop对象只有在第一次获取runloop对象时才会创建而且默认是关闭状态,需要我们手动调用run方法运行。

4. timer与runloop的关系

我们创建timer时,之所以timer能运行,是因为创建timer时,一般情况下,是在主线程中创建,这时会默认将timer以defaultRunloopModel的类型加入主线程,而主线程的runloop对象默认是打开的,从而timer可以运行。

系统会将NSTimer自动加入NSDefaultRunLoopMode模式中,所以以下两段代码含义相同:

[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

对于NSTimer在滑动时停止工作的问题

  • 当我们不做任何操作的时候,RunLoop处于NSDefaultRunLoopMode
  • 当我们进行拖拽时,RunLoop就结束NSDefaultRunLoopMode,切换到了UITrackingRunLoopMode模式下,这个模式下没有添加该NSTimer以及其事件,所以我们的NSTimer就不工作了
  • 当我们松开鼠标时候,RunLoop就结束UITrackingRunLoopMode模式,又切换回NSDefaultRunLoopMode模式,所以NSTimer就又开始正常工作了

想要解决这个问题也很简单,我们直接让NSTimer在两种mode下都能工作就完了,这就用到我们之前不太清楚其用法的NSRunLoopCommonModes了:

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

当然你也可以把NSTimer分别加入到NSDefaultRunLoopModeUITrackingRunLoopMode,这两种写法是相同的,因为系统的mode是默认在_commonModes中的:

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];

5. 如何解决timer不准确的问题

可能造成NSTimer不准确的原因:

  • RunLoop循环过程中,被NSTimer触发事件阻塞了,导致循环不能及时进行下去,延误之后NSTimer触发时间。
  • RunLoop循环过程中,在某一时刻主线程发生了阻塞情况,导致循环不能及时进行下去,厌恶NSTimer触发时间。
  • RunLoop循环过程中,发生了模式的转换,(比如UIScrollView的滑动) 导致原有模式下的NSTimer不会正常触发。

以上情况都是由于NSTimer所依赖的run loops会被多种原因干扰正常循环,所以要想解决NSTimer精度问题,就要避免所依赖的run loops被外界干扰。

  • 注意:虽然第三种情况可以指定NSTimer所处模式为NSRunLoopCommonModes,但是这种解决方法并不能改变run loops在特定模式下不能处理其余模式事件的本质。

终极解决办法:

  • 尽量避免将NSTimer放入容易受到影响的主线程run loops中。
  • 尽量避免将耗时操作放入NSTimer依赖的线程中。
  • 尽量避免在NSTimer触发事件中进行耗时操作,如果不能避免,将耗时操作移至其余线程进行。

6. RunLoop是怎么响应用户操作的,具体流程是怎么样的

Objective-C 中,RunLoop 是一个事件循环机制,用于处理用户操作、定时器事件、网络请求等异步任务RunLoop 的主要作是监听事件并分发给相应的处理器。

下面是 RunLoop 响应用户操作的大致流程:

  • 当用户进行某个操作(例如点击按钮)时,操作系统会将该事件发送给应用程序。
  • 应用程序主线程会收到这个事件,并将其添加当前线程的 RunLoop 中。
  • RunLoop 开始运行,并进入一个循环状态,不断地检查是否事件需要处理。
  • 如果有事件需要处理,RunLoop 会将事件从队列中取出,并将其分发给相应的处理器(例如事件响应方法)。
  • 处理器执行相应的代码处理事件,可能包括更新用户界面、执行业务逻辑等操作。
  • 处理完事件后,RunLoop 继续循环,等下一个事件的到来。
  • RunLoop 的关键是在循环中不断检查事件,并及时分发给处理器这样可以保证用户操作的响应速度,并且免阻塞主线程。

需要注意的是RunLoop 并不是只处理用户操作,它还负责处理其他步任务,如定时器事件、网络请求等。RunLoop 的设计目的是为了提供一种效的事件处理制,使应用程序能够时响应用户操作其他异步任务,同时保持界面的畅性。

7. 说说RunLoop的几种状态

五种状态:

  • kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
  • UITrackingRunLoopMode:界面跟踪Mode,用于ScrollView追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
  • UIInitializationRunLoopMode:在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用,会切换到kCFRunLoopDefaultMode
  • GSEventReceiveRunLoopMode:接受系统事件的内部 Mode,通常用不到
  • kCFRunLoopCommonModes:并不是一种模式
    只是一个标记,当mode标记为common时,将mode添加到runloop中的_commonModes中。runloop中的_commonModes实际上是一个Mode的集合,可使用CFRunLoopAddCommonMode()将Mode放到_commonModes中。每当RunLoop的内容发生变化时,RunLoop都会将_commonModeItems里的同步到具有Common标记的所有的Mode

8. RunLoop的mode有什么作用

用来控制一些特殊操作只能在指定模式下运行,一般可以通过指定操作的运行mode 来控制执行时机,以提高用户体验。
系统默认注册了 5 个 Mode

  • kCFRunLoopDefaultMode:App 的默认 Mode,通常主线程是在这个 Mode下运行,对应 OC 中的:NSDefaultRunLoopMode
  • UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
  • kCFRunLoopCommonModes:这是一个标记 Mode,不是一种真正的 Mode,事件可以运行在所有标有 common modes 标记的模式中,对应 OC 中的NSRunLoopCommonModes , 带 有 common modes 标 记 的 模 式 有 :UITrackingRunLoopModekCFRunLoopDefaultMode
  • UIInitializationRunLoopMode:在启动 App 时进入的第一个 Mode,启动完成后就不再使用
  • GSEventReceiveRunLoopMode:接受系统事件的内部 Mode,通常用不到

AutoreleasePool

在RunLoop这块讲到了很多AutoreleasePool的知识,在这补充一下。
自动释放池的PUSH流程:
请添加图片描述
自动释放池的POP流程:
请添加图片描述

1.临时变量什么时候释放?

  • 如果在正常情况下,一般是超出其作用域就会立即释放
  • 如果将临时变量加入了自动释放池,会延迟释放,即在runloop休眠或者autoreleasepool作用域之后释放

2.AutoreleasePool原理

  • 自动释放池的本质是一个AutoreleasePooIPage结构体对象,是一个栈结构存储的页,每一个AutoreleasePoolPage都是以双向链表的形式连接
  • 自动释放池的压栈和出栈主要是通过结构体的构造函数和析构函数调用底层的obic autoreleasePoolPudhobic_autoreleasePoolPop,实际上是调用AutoreleasePoolPagepushpop两个方法
  • 每次调用push操作其实就是创建一个新的AutoreleasePooIPage,而AutoreleasePoolPage的具体操作就是插入一个POOL BOUNDARY,并返回插入POOL BOUNDARY的内存地址。而push内部调用autoreleaseFast方法处理,主要有以下三种情况
    • page存在,且不满时,调用add方法将对象添加至pagenext指针处,并next递增
    • page存在,且已满时,调用autoreleaseFulIPage初始化一个新的page,然后调用add方法将对象添加至page栈中
    • page不存在时,调用autoreleaseNoPage创建一个hotPage,然后调用add方法将对象添加至page栈中
  • 当执行pop操作时,会传入一个值,这个值就是push操作的返回值,即POOL_BOUNDARY的内存地址token,所以pop内部的实现就是根据token找到哨兵对象所处的page中,然后使用 objc_release释放token之前的对象,并把next指针到正确位置

3.AutoreleasePool能否嵌套使用?

  • 可以嵌套使用,其目的是可以控制应用程序的内存峰值,使其不要太高
  • 可以嵌套的原因是因为自动释放池是以栈为节点,通过双向链表的形式连接的,且是和线程一一对应的
  • 自动释放池的多层嵌套其实就是不停的pushs哨兵对象,在pop时,会先释放里面的,在释放外面的

4.哪些对象可以加入AutoreleasePool?alloc创建可以吗?

  • MRC下使用newalloccopy关键字生成的对象和retain了的对象需要手动释放,不会被添加到自动释放池中
  • MRC下设置为autorelease的对象不需要手动释放,会直接进入自动释放池
  • 所有autorelease的对象,在出了作用域之后,会被自动添加到最近创建的自动释放池中
  • ARC下只需要关注引用计数,因为创建都是在主线程进行的,系统会自动为主线程创建AutoreleasePool,所以创建会自动放入自动释放池

5.AutoreleasePool的释放时机是什么时候?

在没有手动添加AutoreleasePool的情况下,Auturelease对象是在当前的runloop迭代结束的时候进行释放;而它能否释放的原因是系统在每个runloop的迭代中都加入了自动释放池的push和pop。

  • App启动后,苹果在主线程RunLoop里注册了两个Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()
  • 第一个0bserver监视的事件是Entry即将进入 Loop,其回调内会调用_objc_autoreleasePooIPush()创建自动释放池,其order是-2147483647,优先级最高保证创建释放池发生在其他所有回调之前。
  • 第二个Observer监视了两个事件:BeforeWating(准备进入休)时调用 obic_autoreleasePoolPop()obic autoreleasePooPush释放旧的池井创建新池; ·Exit·即将很出Loop)时调用 obic_autoreleasePooIPop(来释放自动释放池。这个Obsererorder是 2147483647优先级最低,保证其释放池子发生在其他所有回调之后。

6.thread和AutoreleasePool的关系

  • 每个线程,包括主线程在内都维护了自己的自动释放池堆栈结构
  • 新的自动释放池在被创建时,会被添加到栈顶;当自动释放池销毁时,会从栈中移除
  • 对于当前线程来说,会将自动释放的对象放入自动释放池的栈顶;在线程停止时,会自动释放掉与该线程关联的所有自动释放池

总结:每个线程都有与之关联的自动释放池堆栈结构。新的pool在创建时会被压栈到栈顶,pool销毁时,会被出栈。对于当前线程来说。释放对象会被压栈到栈顶线程停止时,会自动释放与之关联的自动释放池

7.RunLoop和AutoreleasePool的关系

  • 主程序的RunLoop在每次事件循环之前,会自动创建一个AutoreleasePool
  • 并且会在事件循环结束时,执行drain操作,释放其中的对象。

多线程

1. 你理解的多线程

好处:

  • 1.使用线程可以把占据时间长的程序中的任务放到后台去处理
  • 2.用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以d出一个进度条来显示处理的进度
  • 3.程序的运行速度可能加快
  • 4.在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。

缺点:

  • 1.如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换。
  • 2.更多的线程需要更多的内存空间。
  • 3.线程的中止需要考虑其对程序运行的影响。
  • 4.通常块模型数据是在多个线程间共享的,需要防止线程死锁情况的发生。

2. iOS多线程的方式有哪几种,你更倾向于哪一种

实现多线程的方法:

请添加图片描述
更倾向于GCD和NSOperation,封装更高级,使用起来更方便。

3. 有用过GCD吗?

  • 1.在其他线程请求完数据,在主线程刷新UI
    //让处理在主线程中执行
	dispatch_async(dispatch_get main_queue(), ^{
		/*
		 *只在主线程可以执行的处理
		 *例如用户界面更新
		 */
	});
  • 2.Manager封装网络请求时候,可以用GCD实现单例
- (void)once {        //GCD的一次性代码(只执行一次)
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 只执行1次的代码(这里面默认是线程安全的)
    });
}
  • 3.GCD的定时器
- (void)after {
    NSLog(@"run -- 0");
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 2秒后异步执行这里的代码...
       NSLog(@"run -- 2");
    });
}
  • 4.信号量加锁保证线程安全
    dispatch_semaphore_t semalook = dispatch_semaphore_create(1);
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        dispatch_semaphore_wait(semalook, DISPATCH_TIME_FOREVER);
        NSLog(@"1开始");
        sleep(2);
        NSLog(@"1结束");
        dispatch_semaphore_signal(semalook);
    });
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        dispatch_semaphore_wait(semalook, DISPATCH_TIME_FOREVER);
        NSLog(@"2开始");
        sleep(2);
        NSLog(@"2结束");
        dispatch_semaphore_signal(semalook);
    });

4. GCD的队列类型

可以使用dispatch_queue_create来创建对象,需要传入两个参数,第一个参数表示队列的唯一标识符,用于DEBUG,可为空;第二个参数用来识别是串行队列还是并发队列。DISPATCH_QUEUE_SERIAL表示串行队列,DISPATCH_QUEUE_CONCURRENT表示并发队列。

// 串行队列的创建方法
dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
// 并发队列的创建方法
dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);

5. 说一下OperationQueue和GCD的区别,以及各自的优势

区别

  1. GCD执行效率更高,而且由于队列中执行的是由block构成的任务,这是一个轻量级的数据结构,写起来更方便
  2. GCD只支持FIFO的队列而NSOperationQueue可以通过设置最大并发数,设置优先级,添加依赖关系等调整执行顺序
  3. NSOperationQueue甚至可以跨队列设置依赖关系,但是GCD只能通过设置串行队列,或者在队列内添加barrier(dispatch_barrier_async)任务,才能控制执行顺序,较为复杂
  4. NSOperationQueue因为面向对象,所以支持KVO,可以监测operation是否正在执行(isExecuted)、是否结束(isFinished)、是否取消(isCanceld
  • 实际项目开发中,很多时候只是会用到异步操作,不会有特别复杂的线程关系管理,所以苹果推崇的且优化完善、运行快速的GCD是首选
  • 如果考虑异步操作之间的事务性,顺序行,依赖关系,比如多线程并发下载,GCD需要自己写更多的代码来实现,而NSOperationQueue已经内建了这些支持
  • 不论是GCD还是NSOperationQueue,我们接触的都是任务和队列,都没有直接接触到线程,事实上线程管理也的确不需要我们操心,系统对于线程的创建,调度管理和释放都做得很好。而NSThread需要我们自己去管理线程的生命周期,还要考虑线程同步、加锁问题,造成一些性能上的开销

6. 线程安全的处理手段有哪些?

  • 互斥锁(Mutex):使用互斥锁可以确保同一时间只有一个线程访问共享资源。在Objective-C中,可以使用@synchronized关键字来创建互斥锁。
@synchronized (self) {
    // 访问共享资源的代码
}
  • 自旋锁(Spin Lock):自旋锁一种忙等待的锁,它会不断地尝试获取锁,直到成功为止。在Objective-C中,可以使用os_unfair_lock来创建自旋锁。
os_unfair_lock_t lock = &(OS_UNFAIR_LOCK_INIT);
os_unfair_lock_lock(lock);
// 访问共享资源的代码
os_unfair_lock_unlock(lock);
  • 信号量(Semaphore):信量是一种数器,用于控制同时访问某个资源的线程数量。在Objective-C中可以使用dispatch_semaphore来创建信号量。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 访问共享资源的代码
dispatch_semaphore_signal(semaphore);
  • 原子操作(Atomic Operations):原子操作是一种特的操作,可以确保对共享资源的读写操作是原子的,即不会被其他线程中断。在Objective-C中,可以使用atomic键字来声明属性原子类型。
@property (atomic, strong) NSObject *sharedObject;
  • 串行队列(Serial Queue):使用串行队列可以确保任务按顺序执行,从而避多个线程同时访问共享资源。可以使用GCD(Grand Central Dispatch)来创建串行队列。
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.serialQueue DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{
    // 访问共享资源的代码
});

7. OC的锁有哪些

【iOS】—— iOS中的相关锁

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/46500.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

二、SQL-6.DCL-1).用户管理

一、DCL介绍 Data Control Language 数据控制语言 用来管理数据库 用户、控制数据库的 访问权限。 二、语法 1、管理用户 管理用户在系统数据库mysql中的user表中创建、删除一个用户,需要Host(主机名)和User(用户名&#xff0…

json-server创建静态服务器2

上次写的 nodejs创建静态服务器 这次再来个v2.0 利用json-server很方便就可以实现。 vscode打开文件夹,文件夹所在终端: json-server.cmd --watch db.json 这里视频教程是没有上述命令标红的,但是会报错,具体不详&#xff0c…

Docker私有仓库部署与管理

目录 Docker--harbor Harbor 简介 Harbor 部署 1. 部署 Docker-Compose 服务 2. 部署 Harbor 服务 维护管理Harbor 1. 通过 Harbor Web 创建项目 2. 创建 Harbor 用户 3. 查看日志 4. 修改 Harbor.cfg 配置文件 5. 移除 Harbor 服务容器同时保留镜像数据/数据库&…

Unity Profiler或UPR连接WebGL应用出错

问题 在使用Unity Build出WebGL应用进行性能测试的时候,勾选上了 Development Build和Autoconnect Profiler,分别使用Profiler和UPR进行测试 现象 使用Profiler测试时,就收到几帧,然后就没了 使用UPR进行测试时,在…

python简单入门

python简单入门 文章目录 python简单入门0. 地址链接1. 官网2.2. 下载地址3. 官方文档 1. 第一章1.1 python解释器1.2 基础语法1.2.1 常见数据类型1.2.2 强制类型转换1.2.3 注释1.2.4 运算符1.2.5 字符串1.2.5.1 字符串的定义1.2.5.2 字符串拼接1.2.5.3 格式化字符串1.2.5.3 精…

jenkins部署Springboot项目

文章目录 jenkins部署Springboot项目步骤创建一个jenkins任务开始配置准备步骤添加shell脚本(两个)你放一个执行也行构建成功后执行脚本 如何在linux上安装jenkins可以看上一篇 linux安装jenkins(详细步骤) jenkins部署Springboot项目步骤 创建一个jenkins任务 开始配置 ![在这…

error: /tmp/ccxy1wo0.o: multiple definition of ‘tgt_flow_thread_init‘

linux 项目使用Makefile 编译代码时,一直报错 从报错意思上看很明确,就是重复定义 tgt_flow_thread_init函数 但是我从全局搜索代码看根本不存在重复定义问题。 从网上看是说可能存在头文件有重复的定义或者头文件被重复的引用,但是我看了…

el-table树形表格实现复选框多选效果

2023.7.26今天我学习了如何使用树形表格的时候进行复选框的多选效果。 当我们使用树形结构表格需要进行多选功能操作的时候会发现点击全选的时候,只有一级表格数据会被选中,问题如图: 我们需要实现的是点击全选的不管是几级表格数据都可以被…

策略模式的实现与应用:掌握灵活算法切换的技巧

文章目录 常用的设计模式有以下几种:一.创建型模式(Creational Patterns):二.结构型模式(Structural Patterns):三.行为型模式(Behavioral Patterns):四.并发…

k8s使用helm部署Harbor镜像仓库并启用SSL

1、部署nfs存储工具 参照:https://zhaoll.blog.csdn.net/article/details/128155767 2、部署helm 有多种安装方式,根据自己的k8s版本选择合适的helm版本 参考:https://blog.csdn.net/qq_30614345/article/details/131669319 3、部署Harbo…

matplotlib从起点出发(4)_Tutorial_4_Lifecycle

1 一幅图像的生命周期 本教程旨在揭示使用matplotlib绘制的一幅图像的生命周期,包括它的开始、中间和结束。我们将从一些原始数据开始,最后保存自定义可视化的图形。在此过程中,我们尝试使用matplotlib突出一些简洁的功能和最佳实践。 2 关…

三子棋(超详解+完整码源)

三子棋 前言一,游戏规则二,所需文件三,创建菜单四,游戏核心内容实现1.棋盘初始化1.棋盘展示3.玩家下棋4.电脑下棋5.游戏胜负判断6.game()函数内部具体实现 四,游戏运行实操 前言 C语言实现三子棋…

maven

一、为什么需要使用maven 如今我们构建一个项目需要用到很多第三方的类库 ,例如我们在开发项目中 需要引入 这些依赖jar包 一个项目Jar包的数量之多往往让我们瞠目结舌,并且Jar包之间的关系非常复杂,一个Jar包往往又会引用其他Jar包&#x…

安全学习DAY07_其他协议抓包技术

协议抓包技术-全局-APP&小程序&PC应用 抓包工具-Wireshark&科来分析&封包 TCPDump: 是可以将网络中传送的数据包完全截获下来提供分析。它支持针对网络层、协议、主机、网络或端口的过滤,并提供and、or、not等逻辑语句来帮助你去掉无用…

stable diffusion如何确保每张图的面部一致?

可以使用roop插件,确定好脸部图片后,使用roop固定,然后生成的所有图片都使用同一张脸。 这款插件的功能简单粗暴:一键换脸。 如图所示: 任意上传一张脸部清晰的图片,点击启用。 在其他提示词不变的情况下…

赛多利斯Sartorius天平java后端对接

业务场景 要将赛多利斯天平的数据读出来解析并且显示到对应的数字框,支持一台设备连接多种精度的天平 后端实现 通过协议解析数据,然后将数据存储 详细代码就不贴了,感兴趣的可以私聊我

GPT-AI 使用的技术概览

ChatGPT 使用的技术概览 智心AI-3.5/4模型,联网对话,MJ快速绘画 从去年 OpenAI 发布 ChatGPT 以来,AI 的能力再次惊艳了世人。在这样的一个时间节点,重新去学习相关技术显得很有必要。 ChatGPT 的内容很多,我计划采用…

3ds Max图文教程: 创建致命的冠状病毒动画

推荐: NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 病毒建模 步骤 1 打开 3ds Max。 打开 3ds Max 步骤 2 在透视视口中创建一个半径为 50,线段为 20 的 GeoSphere。 创建地球 步骤 3 打开修改器列表并将置换修改器应用于地理 球。 置换…

Linux系统MySQL中用户的权限管理

本节主要学习用户权限管理的概述,用户权限类型,用户赋权,权限删除,用户删除等。 目录 一、概述 二、用户权限类型 三、用户赋权 四、权限删除 五、用户删除 一、概述 数据库用户权限管理是数据库系统中非常重要的一个方面&am…

Dockerfile 创建镜像,构建LNMP+wordpress架构

目录 一、Dockerfile 构建镜像 1.Dockerfile 构建 nginx镜像 1.1创建 nginx Dockerfile 目录 1.2编写 Dockerfile 文件 1.3构建nginx镜像 2.Dockerfile 构建 mysql 镜像 2.1创建 mysql Dockerfile 目录 2.2修改mysql配置文件 2.3编写 Dockerfile 文件 2.4构建mysql镜…
最新文章