在开发 iOS 应用时,多线程技巧的重要性不言而喻。尤其是在涉及到大量数据操作,或在 UI 界面中需要大批量数据的情况下,使用多线程可以增强应用的稳定性和性能。其中,在子线程中解析数据库,是一种常见的多线程操作,在本文中将介绍子线程解析数据库落地实现的具体步骤。
1. 数据库的创建
在整个过程中需要用到一个数据库。SQLite 数据库是一个非常流行的轻量级关系型数据库,它可以轻松地集成到 iOS 应用中。我们可以使用 SQLite 提供的 API 来实现数据存储并进行相应的操作。
在开始写代码之前,我们需要安装 sqlite3.h 和 sqlite3.c 两个文件,并将其添加至项目中。然后,我们需要创建一个 SQLite 数据库并建立一个表格,以存放我们接下来要使用的数据。
下面是建立一个名为 DataDB 的数据库,并在其中建立一个名为 Data 表格的 SQLite 语句:
“`SQL
CREATE TABLE Data
(
id INTEGER PRIMARY KEY,
data TEXT
);
“`
以上语句定义的表格中包含两个字段:id 和 data。其中,id 是一个自增的整数字段,并作为主键;data 是一个文本字段,用来存放我们的数据。
2. 创建数据模型
接下来,我们需要创建一个数据模型,用来描述所需的数据。在本文中,我们将定义一个名为 DataModel 的模型:
“`Swift
class DataModel {
var id: Int
var data: String
init(id: Int, data: String) {
self.id = id
self.data = data
}
}
“`
该模型有两个属性:id 和 data,分别代表表格中的两个字段。我们通过添加不同的数据来创建多个 DataModel 实例。
3. 解析数据
在正式开始多线程操作之前,需要先实现数据库中的数据读取和解析操作。我们可以在主线程中写一个函数,将需要的数据从数据库中读出,并创建相应的数据模型。
以下是从数据库中读取数据的代码:
“`Swift
func fetchDataFromDB() -> [DataModel] {
var results = [DataModel]()
var sqlite: OpaquePointer?
if sqlite3_open(DataDBPath, &sqlite) == SQLITE_OK {
var statement: OpaquePointer?
let sql = “SELECT * FROM Data”
if sqlite3_prepare_v2(sqlite, sql, -1, &statement, nil) == SQLITE_OK {
while sqlite3_step(statement) == SQLITE_ROW {
let id = Int(sqlite3_column_int(statement, 0))
let dataString = String(cString: sqlite3_column_text(statement, 1))
let data = DataModel(id: id, data: dataString)
results.append(data)
}
}
sqlite3_finalize(statement)
}
sqlite3_close(sqlite)
return results
}
“`
以上代码实现了读取所有数据,并将数据解析为 DataModel 的实例。
4. 显示数据
在本例中,我们需要将数据分别显示在 UITableView 和 UICollectionView 中,因此需要分别对两种情况进行显示。
以下是 UITableView 的显示代码:
“`Swift
// Table view data source
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: “cell”, for: indexPath)
let data = dataList[indexPath.row]
cell.textLabel?.text = data.data
return cell
}
“`
以下是 UICollectionView 的显示代码:
“`Swift
// Collection view data source
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataList.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: “cell”, for: indexPath)
let data = dataList[indexPath.item]
cell.backgroundColor = UIColor.lightGray
let label = UILabel(frame: cell.contentView.bounds)
label.textAlignment = .center
label.textColor = UIColor.white
label.text = data.data
cell.contentView.addSubview(label)
return cell
}
“`
以上代码将 dataList 数组中的每个元素分别显示在 UITableViewCell 和 UICollectionViewCell 中。
5. 子线程操作
在上述操作中,我们仅仅是在主线程中对数据进行操作,并未多线程处理,因此需要添加多线程操作。子线程中处理操作可以避免阻塞主线程,并提高应用性能。
以下是解析数据并存储到 dataList 数组中的子线程代码:
“`Swift
DispatchQueue.global(qos: .background).async {
let tempList = fetchDataFromDB()
DispatchQueue.mn.async {
self.dataList = tempList
self.tableView.reloadData()
self.collectionView.reloadData()
}
}
“`
以上代码使用了 GCD(Grand Central Dispatch)的全局队列,以处理 fetchDataFromDB 函数的调用。即,在子线程中完成数据库数据的解析并存储到 dataList 中,并使用主线程更新 UI。
通过以上步骤,我们成功实现了在子线程中解析数据库并在 UI 中显示解析结果的操作。这样,就可以有效避免数据操作过程的卡顿和应用程序的不稳定,提高了应用程序的响应速度,同时用户体验也得到了提升。
相关问题拓展阅读:
- ios本地写文件 应该在主线程么
- 想从事游戏开发,现在需要学什么?
ios本地写文件 应该在主线程么
大家都知道,在开发过程中应该尽可能减少用户等待时间,让程序尽可能快的完成运算。可是无论是哪种语言开发的程序最终往往转换成汇编语言进而解释成机器码来执行。但是机器码是按顺序执行的,一个复杂的多步操作只能一步步按顺序逐个执行。改变这种状况可以从两个角度出发睁启历:对于单核处理器,可以将多个步骤放到不同的线程,这样一来用户完成UI操作后其他后续任务在其他线程中,当CPU空闲时会继续执行,而此时对于用户而言可以继续进行其他操作;对于多核处理器,如果用户在UI线程中完成某个操作之后,其他后续操作在别的线程中继续执行,用户同样可以继续进行其他UI操作,与此同时前一个操作的后续任务可以分散到多个空闲CPU中继续执行(当然具体调度顺序要根据程序设计而定),及解决了线程阻塞又提高了运行效率。苹果从iPad2 开始使用双核A5处理器(iPhone中从iPhone 4S开始使用),A7中还加入了协处理器悉搜,如何充分发挥这些处理器的性能确实值得思考。今天将重点分析iOS多线程开发:
多线程
简介
iOS多线程
NSThread
解决线程阻塞问题
多线程并发
线程状态
扩展-NSObject分类扩展
NSOperation
NSInvocationOperation
NSBlockOperation
线程执行顺序
GCD
串行队列
并发队列
其他任务执行方法
线程同步
NSLock同步锁
@synchronized代码块
扩展–使用GCD解决资源抢占问题
扩展–控制线程通信
总结
目 录
多线程
简介
当用户播放音频、下载资源、进行图像处理时往往希望做这些事情的时候其他操作不会被中断或者希望这些操作过程中更加顺畅。在单线程中一个线程只能做一件事情,一件事情处理不完另一件事就不能开始,这样势必影响用户体验。早在单核处理器时期就有多线程,这个时候多线程更多的用于解决线程阻塞造成的用户等待(通常是操作完UI后用户不再干涉,其他线程在等待队列中,CPU一旦空闲就继续执行,不影响用户其他UI操作),其处理能力并没有明显的变化。如今无论是移动操作系统还是PC、服务器都是多核处理器,于是“并行运算”就更多的被提及。一件事情我们可以分成多个步骤,在没有顺序要求的情况下使用多线程既能解决线程阻塞又能充分利用多核处理器运行能力。
下图反映了一个包含8个操作的任务在一个有两核心的CPU中创建四个线程运行的情况。假设每个核心有两个线程,那么每个CPU中两个线程会交替执行,两个CPU之间的操作会并行运算。单就一个CPU而言两个线程可以解决线程阻塞造成的不流畅问题,其本身运行效率并没有提高,多CPU的并行运算才真正旁坦解决了运行效率问题,这也正是并发和并行的区别。当然,不管是多核还是单核开发人员不用过多的担心,因为任务具体分配给几个CPU运算是由系统调度的,开发人员不用过多关心系统有几个CPU。开发人员需要关心的是线程之间的依赖关系,因为有些操作必须在某个操作完成完才能执行,如果不能保证这个顺序势必会造成程序问题。
iOS多线程
在iOS中每个进程启动后都会建立一个主线程(UI线程),这个线程是其他线程的父线程。由于在iOS中除了主线程,其他子线程是独立于Cocoa Touch的,所以只有主线程可以更新UI界面(新版iOS中,使用其他线程更新UI可能也能成功,但是不推荐)。iOS中多线程使用并不复杂,关键是如何控制好各个线程的执行顺序、处理好资源竞争问题。常用的多线程开发有三种方式:
1.NSThread
2.NSOperation
3.GCD
三种方式是随着iOS的发展逐渐引入的,所以相比而言后者比前者更加简单易用,并且GCD也是目前苹果官方比较推荐的方式(它充分利用了多核处理器的运算性能)。做过.Net开发的朋友不难发现其实这三种开发方式 刚好对应.Net中的多线程、线程池和异步调用,因此在文章中也会对比讲解。
NSThread
NSThread是轻量级的多线程开发,使用起来也并不复杂,但是使用NSThread需要自己管理线程生命周期。可以使用对象方法+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument直接将操作添加到线程中并启动,也可以使用对象方法- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(id)argument 创建一个线程对象,然后调用start方法启动线程。
解决线程阻塞问题
在资源下载过程中,由于网络原因有时候很难保证下载时间,如果不使用多线程可能用户完成一个下载操作需要长时间的等待,这个过程中无法进行其他操作。下面演示一个采用多线程下载图片的过程,在这个示例中点击按钮会启动一个线程去下载图片,下载完成后使用UIImageView将图片显示到界面中。可以看到用户点击完下载按钮后,不管图片是否下载完成都可以继续操作界面,不会造成阻塞。
//
// NSThread实现多线程
// MultiThread
//
// Created by Kenshin Cui on.
// Copyright (c) 2023年 Kenshin Cui. All rights reserved.
//
#import “KCMainViewController.h”
@interface KCMainViewController (){
UIImageView *_imageView;
}
@end
@implementation KCMainViewController
– (void)viewDidLoad {
;
;
}
#pragma mark 界面布局
-(void)layoutUI{
_imageView =initWithFrame:.applicationFrame>;
_imageView.contentMode=UIViewContentModeScaleAspectFit;
;
UIButton *button=;
button.frame=CGRectMake(50, 500, 220, 25);
//添加方法
;
}
#pragma mark 将图片显示到界面
-(void)updateImage:(NSData *)imageData{
UIImage *image=;
_imageView.image=image;
}
#pragma mark 请求图片数据
-(NSData *)requestData{
//对于多线程操作建议把线程操作放到@autoreleasepool中
@autoreleasepool {
NSURL *url=
“>;
NSData *data=;
return data;
}
}
#pragma mark 加载图片
-(void)loadImage{
//请求数据
NSData *data= ;
/*将数据显示到UI控件,注意只能在主线程中更新UI,
另外performSelectorOnMainThread方法是NSObject的分类方法,每个NSObject对象都有此方法,
它调用的selector方法是当前调用控件的方法,例如使用UIImageView调用的时候selector就是UIImageView的方法
Object:代表调用方法的参数,不过只能传递一个参数(如果有多个参数请使用对象进行封装)
waitUntilDone:是否线程任务完成执行
*/
;
}
#pragma mark 多线程下载图片
-(void)loadImageWithMultiThread{
//方法1:使用对象方法
//创建一个线程,之一个参数是请求的操作,第二个参数是操作方法的参数
// NSThread *thread=initWithTarget:self selector:@selector(loadImage) object:nil>;
// //启动一个线程,注意启动一个线程并非就一定立即执行,而是处于就绪状态,当系统调度时才真正执行
// ;
//方法2:使用类方法
;
}
@end
运行效果:
程序比较简单,但是需要注意执行步骤:当点击了“加载图片”按钮后启动一个新的线程,这个线程在演示中大概用了5s左右,在这5s内UI线程是不会阻塞的,用户可以进行其他操作,大约5s之后图片下载完成,此时调用UI线程将图片显示到界面中(这个过程瞬间完成)。另外前面也提到过,更新UI的时候使用UI线程,这里调用了NSObject的分类扩展方法,调用UI线程完成更新。
多个线程并发
上面这个演示并没有演示多个子线程操作之间的关系,现在不妨在界面中多加载几张图片,每个图片都来自远程请求。
大家应该注意到不管是使用+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument、- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(id)argument 方法还是使用- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait方法都只能传一个参数,由于更新图片需要传递UIImageView的索引和图片数据,因此这里不妨定义一个类保存图片索引和图片数据以供后面使用。
KCImageData.h
//
// KCImageData.h
// MultiThread
//
// Created by Kenshin Cui on.
// Copyright (c) 2023年 Kenshin Cui. All rights reserved.
//
#import
@interface KCImageData : NSObject
#pragma mark 索引
@property (nonatomic,assign) int index;
#pragma mark 图片数据
@property (nonatomic,strong) NSData *data;
@end
接下来将创建多个UIImageView并创建多个线程用于往UIImageView中填充图片。
KCMainViewController.m
//
// NSThread实现多线程
// MultiThread
//
// Created by Kenshin Cui on.
// Copyright (c) 2023年 Kenshin Cui. All rights reserved.
//
#import “KCMainViewController.h”
#import “KCImageData.h”
#define ROW_COUNT 5
#define COLUMN_COUNT 3
#define ROW_HEIGHT 100
#define ROW_WIDTH ROW_HEIGHT
#define CELL_SPACING 10
@interface KCMainViewController (){
NutableArray *_imageViews;
}
@end
@implementation KCMainViewController
– (void)viewDidLoad {
;
;
}
#pragma mark 界面布局
-(void)layoutUI{
//创建多个图片控件用于显示图片
_imageViews=;
for (int r=0; r
for (int c=0; c
UIImageView *imageView=initWithFrame:CGRectMake(c*ROW_WIDTH+(c*CELL_SPACING), r*ROW_HEIGHT+(r*CELL_SPACING), ROW_WIDTH, ROW_HEIGHT)>;
imageView.contentMode=UIViewContentModeScaleAspectFit;
//imageView.backgroundColor=;
;
;
}
}
UIButton *button=;
button.frame=CGRectMake(50, 500, 220, 25);
//添加方法
;
}
#pragma mark 将图片显示到界面
-(void)updateImage:(KCImageData *)imageData{
UIImage *image=;
UIImageView *imageView= _imageViews;
imageView.image=image;
}
#pragma mark 请求图片数据
-(NSData *)requestData:(int )index{
//对于多线程操作建议把线程操作放到@autoreleasepool中
@autoreleasepool {
NSURL *url=
“>;
NSData *data=;
return data;
}
}
#pragma mark 加载图片
-(void)loadImage:(NSNumber *)index{
// NSLog(@”%i”,i);
//currentThread方法可以取得当前操作线程
NSLog(@”current thread:%@”,);
int i=;
// NSLog(@”%i”,i);//未必按顺序输出
NSData *data= ;
KCImageData *imageData=init>;
imageData.index=i;
imageData.data=data;
;
}
#pragma mark 多线程下载图片
-(void)loadImageWithMultiThread{
//创建多个线程用于填充图片
for (int i=0; i
//>;
NSThread *thread=initWithTarget:self selector:@selector(loadImage:) object:>;
thread.name=;//设置线程名称
;
}
}
想从事游戏开发,现在需要学什么?
现在游戏开发最火的就是unity了,可以去优就业学习
根据游戏类型的不同,所学的软件也不一样。
中小型游戏大致可分为网页游戏,flash游戏,小游戏等,基本上都是一些休闲类的傻呆萌的情节和操作。
这类游戏开发相对比较简单,会Javascript、HTML、flashcs、Java就可以进行开发了,语言类主要有C/C++,汇编语言,着色器语言,脚本语言,高效的开发语言C#或Java。
现在的游戏主要分为三种:
1、PC类端游(就是电睁碰脑上面运行的游戏)
这类游戏在线人数多,游戏中要处理的数据庞大。所以对服务器性能要求非常高,一般都是采用C++做为开发语言,C++可以直接操作内存数据,与操作系统直接交互悉链谈,减少数据之唤侍间的复制,它运行效率高,处理速度快,是很适合这里游戏开发语言。
学习这种游戏的开发,学习的有C++编程,Linux网络编程、TCP/IP通讯协议、多线程编程再加数据库。
PC类端游戏开发周期较长。大概需要三年左右的时间。
2、网页游戏(比如现在经常说的1刀999级)
因为是网页游戏,游戏的界面展示依赖于网络传输,所在在画面和特效上会次于客户端游戏很多。和端游类是差不多是一样的,有些公司之前是做端游的,他们就直接把端游的服务器架构拿来就可以使用,以完成快速开发。
需要学习内容和端游差不多。
3、手机游戏(主要区分为安卓和IOS)
关于ios 子线程解析数据库的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。