iOS Dev 其实iOS开发很简单之《归属地查询》软件的抛砖引玉

学习iOS开发有一段时间了,可能很多人也会有一样的想法,究竟自己现在能写出点什么看上去能算是应用的程序,我在左思右想之后,用了8个小时,写了这个归属地查询软件,我想说的是,这个软件从代码上看其实很简单,学了iOS开发一段时间的人都应该能写出来,无非就是从一个textfield接收一个电话号码,之后对电话号码稍加转换,之后是用数据库查询。

在这里呢,我们输入的号码,无非就是几种(可能是,10XXXX;可能是固话:0XXxxxxxxxx,和0XXXxxxxxxxx,这里我们允许用户只输入区号(0XX,0XXX);可能是移动电话:1XXxxxxxxxx,861XXxxxxxxxx,+861XXxxxxxxxx,这里我们同样允许用户输入电话号码的前7位,即(1XXxxxx,861XXxxxx,+861XXxxxx)),其余输入我们一律视为格式不正确输入,显示内容为:输入的手机号码,归属地,运营商,城市区号;

我们需要一个数据库,如果想做做练习,大家可以自己随便写一个数据库,

这里我会为大家展示一些核心的代码:

第一段代码:拷贝数据库

-(void)DoCopyDatabase{
    NSArray *paths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory,NSUserDomainMask, YES); 
    NSString *documentsDirectory = [paths objectAtIndex:0];     
    NSString *documentLibraryFolderPath = [documentsDirectory stringByAppendingPathComponent:@"location_Numbercity_citynumber.db"]; 
    if ([[NSFileManager defaultManager] fileExistsAtPath:documentLibraryFolderPath]) { 
    }else { 
        NSString *resourceSampleImagesFolderPath =[[NSBundle mainBundle] 
                                                   pathForResource:@"location_Numbercity_citynumber" 
                                                   ofType:@"db"]; 
        NSData *mainBundleFile = [NSData dataWithContentsOfFile:resourceSampleImagesFolderPath]; 
        [[NSFileManager defaultManager] createFileAtPath:documentLibraryFolderPath 
                                                contents:mainBundleFile 
                                              attributes:nil];
    }
}
这段代码,在Documents文件夹下没有我们的数据库的时候将数据库拷贝到该位置。

第二段代码:数据的整理操作

- (IBAction)SearchButton:(id)sender {
    [textfieldyourphonenumber resignFirstResponder];
    mylabelmobile.text = @"";
    mylabellocation.text = @"";
    mylabelphonenumber.text = @"";
    mylabelzonecode.text = @"";
    
    NSString *findPhonenumber = @"";
    NSString *findPhoneNumberMobile = @"";
    NSString *findPhoneNumberIsACall = @"";
    NSString *findPhoneNumberIsMobile = @"";
    
    NSInteger phonenumberlength = [textfieldyourphonenumber.text length];
    if (phonenumberlength == 3 ||
        phonenumberlength == 4 ||
        phonenumberlength == 5 ||
        phonenumberlength == 7 ||
        phonenumberlength == 11|| 
        phonenumberlength == 12||
        phonenumberlength == 13||
        phonenumberlength == 14) 
    {
        NSString *tempstring = textfieldyourphonenumber.text;
        if ((phonenumberlength == 14) && ([tempstring characterAtIndex:0] == '+') &&([tempstring characterAtIndex:1] == '8')&&([tempstring characterAtIndex:2] == '6')&&([tempstring characterAtIndex:3] == '1')) 
        {
            NSMutableString *tempstring02 = [NSMutableString stringWithString:tempstring];
            NSRange range;
            range.location = 0;
            range.length = 3;
            [tempstring02 deleteCharactersInRange:range];
            NSString *tempstring03 = [tempstring02 stringByPaddingToLength:7 withString:nil startingAtIndex:0];
            NSString *findPhonenumberFull = [tempstring02 stringByPaddingToLength:11 withString:nil startingAtIndex:0];
            mylabelphonenumber.text = findPhonenumberFull;
            findPhoneNumberMobile = [tempstring02 stringByPaddingToLength:3 withString:nil startingAtIndex:0];
            findPhonenumber = tempstring03;
        }else if
.
.
.
}else if (((phonenumberlength == 12) && ([tempstring characterAtIndex:0] == '0'))||((phonenumberlength == 4) && ([tempstring characterAtIndex:0] == '0'))) {
            NSMutableString *tempstring02 = [NSMutableString stringWithString:tempstring];
            mylabelphonenumber.text = tempstring02;
            NSMutableString *tempstring03 = [[NSMutableString alloc] initWithCapacity:1];
            [tempstring03 appendString:[tempstring02 stringByPaddingToLength:4 withString:nil startingAtIndex:0]];
			mylabelzonecode.text = tempstring03;
			NSRange range;
            range.location = 0;
            range.length = 1;
            [tempstring03 deleteCharactersInRange:range];
            findPhoneNumberIsACall = tempstring03;
        }else if
.
.
.
}else if ((phonenumberlength == 5) &&([tempstring characterAtIndex:0] == '1')) {
            mylabelphonenumber.text = tempstring;
            findPhoneNumberIsMobile = tempstring;
        }else {
            [self PhoneNumberError];
        }
    }else {
        [self PhoneNumberError];
    }
    if ([findPhonenumber length] ==7 && [findPhoneNumberMobile length] ==3) 
	{
        [self SelectInfoByPhone:findPhonenumber WithMobile:findPhoneNumberMobile];
    }else if ([findPhoneNumberIsACall length] == 3||[findPhoneNumberIsACall length] == 4)
	{
        [self SelectInfoByCall:findPhoneNumberIsACall];

    }else if ([findPhoneNumberIsMobile length] == 5)
	{
        NSInteger findPhoneNumberIsMobileInt = [findPhoneNumberIsMobile intValue];
		[self SelectInfoByPhoneNumberIsMobile:findPhoneNumberIsMobileInt];
    }
    textfieldyourphonenumber.text = @"";
}
这里我为大家展示了部分对数据操作代码,省略的代码与其他大同小异,所以我们就提供一个特殊的5位,和移动电话,和固话,各一段操作。

第三段代码:查询数据库

-(void)SelectInfoByPhone:(NSString *)phonenumber WithMobile:(NSString *)phonemobile
{
    NSString *SelectWhatMobile = @"SELECT mobile FROM numbermobile where uid=";
    NSString *SelectWhatMobileFull = [SelectWhatMobile stringByAppendingFormat:phonemobile];
    sqlite3 *database;
    if (sqlite3_open([[self FindDatabase] UTF8String], &database)
        != SQLITE_OK) {
        sqlite3_close(database);
        NSAssert(0, @"Failed to open database");
    }
    sqlite3_stmt *stmt;
    if (sqlite3_prepare_v2(database, [SelectWhatMobileFull UTF8String], -1, &stmt, nil) == SQLITE_OK) {
        while (sqlite3_step(stmt) == SQLITE_ROW) {
            int mobilenumber = sqlite3_column_int(stmt, 0);
            if (mobilenumber) {
                NSString *mobileNumberString = [NSString stringWithFormat:@"%d",mobilenumber];
                NSString *SelectWhatMobileName = @"SELECT mobile FROM mobilenumber WHERE uid=";
                NSString *SelectWhatMobileNameFull = [SelectWhatMobileName stringByAppendingFormat:mobileNumberString];
                sqlite3_stmt *stmt2;
                if (sqlite3_prepare_v2(database, [SelectWhatMobileNameFull UTF8String], -1, &stmt2, nil) == SQLITE_OK) {
                    while (sqlite3_step(stmt2) == SQLITE_ROW) {
                        char *mobilename = (char *)sqlite3_column_text(stmt2, 0);
                        NSString *mobilenamestring = [[NSString alloc] initWithUTF8String:mobilename];
                        if (mobilenamestring!= NULL) {
                            mylabelmobile.text = mobilenamestring;
                        }
                    }
                }sqlite3_finalize(stmt2);
                
            }
        }
        sqlite3_finalize(stmt);
    }
    sqlite3_stmt *stmt3;
    NSString *SelectCityNumberByPhoneNumber = @"SELECT city FROM phonenumberwithcity WHERE uid=";
    NSString *SelectCityNumberByPhoneNumberFull = [SelectCityNumberByPhoneNumber stringByAppendingFormat:phonenumber];
    if (sqlite3_prepare_v2(database, [SelectCityNumberByPhoneNumberFull UTF8String], -1, &stmt3, nil) == SQLITE_OK) {
        if (sqlite3_step(stmt3) == SQLITE_ROW) {
            int citynumber = sqlite3_column_int(stmt3, 0);
            NSString *citynumberNSString = [NSString stringWithFormat:@"%d",citynumber]; 
            if (citynumberNSString != nil) {
                NSString *SelectCityNameAndCtiyZoneByCityBumber = @"SELECT city,zone FROM citywithnumber WHERE uid=";
                NSString *SelectCityNameAndCtiyZoneByCityBumberFull = [SelectCityNameAndCtiyZoneByCityBumber stringByAppendingFormat:citynumberNSString];
                sqlite3_stmt *stmt4;
                if (sqlite3_prepare_v2(database, [SelectCityNameAndCtiyZoneByCityBumberFull UTF8String], -1, &stmt4, nil) == SQLITE_OK) {
                    if (sqlite3_step(stmt4) == SQLITE_ROW) {
                        char *cityname = (char *)sqlite3_column_text(stmt4, 0);
                        int cityzonecode = sqlite3_column_int(stmt4, 1);
                        NSString *cityNameNSString = [[NSString alloc] initWithUTF8String:cityname];
                        NSString *cityzonecodeNnumber = [@"0" stringByAppendingFormat:@"%d",cityzonecode];
                        if (cityNameNSString != nil && cityzonecodeNnumber != nil) {
                            mylabellocation.text = cityNameNSString;
                            mylabelzonecode.text = cityzonecodeNnumber;
                        }
                    }else {
                        [self PhoneNumberError];
                    }
                    sqlite3_finalize(stmt4);
                }
            }
        }else {
            [self PhoneNumberError];
        }
        sqlite3_finalize(stmt3);
    }
    
    sqlite3_close(database);
    
	
	
}
上面代码,对移动电话的查询方法

同样我们还有-(void)SelectInfoByCall:(NSString *) callnumber和-(void)SelectInfoByPhoneNumberIsMobile:(NSInteger)PhoneNumberIsMobile

在这里我不做太多的讲解,因为在程序内使用的都是特别基础的语法,我们还可以再继续写下去,比如添加读取最近的10条通话,供用户选择等等等等,程序运行图片我会在周一为大家添加上


DeepSeek 3FS解读与源码分析专栏收录该内容5 篇文章订阅专栏客户端模式3FS 实现了两套客户端模式,FUSE Client和Native Client。前者更方便适配,性能相对较差。后者适合集成性能敏感的应用程序,适配成本较高。接下来做进一步分析。Fuse ClientFuse Client 模式的原理如下图所示。和传统 FUSE 应用类似,在 libfuse 中注册了 FUSE Daemon 实现的 fuse_lowlevel_ops,之后通过 FUSE 的所有的文件操作,都会通过 libfuse 回调到 FUSE Daemon 进行处理。同时在 libfuse 中实现了一个多线程模式来高效读取请求。这种模式对于业务逻辑影响较小,可以做到无感知。但是每次 I/O 单向需要经过两次“用户态-内核态”上下文切换,以及“用户空间-内核空间”之间数据拷贝。Native Client (USRBIO)介绍这个模式之前我们先了解一下 User Space Ring Based IO(简称 USRBIO)[1],它是一组构建在 3FS 上的高速 I/O 函数。用户应用程序能够通过 USRBIO API 直接提交 I/O 请求给 FUSE Daemon 进程中的 3FS I/O queue 来和 FUSE 进程通信,从而做到 kernel bypass。两者之间通过基于共享内存 ior/iov 的机制交换数据,这部分在后面章节介绍。Native Client 模式的原理如下图所示。使用这种模式能有效避免“用户态-内核态”上下文切换,同时做到零数据拷贝,全链路基本无锁化设计,性能上要比 Fuse Client 模式提升很多。(根据我们对 3FS 开源的 fio ioengine hf3fs_usrbio 压测结果看,在不进行参数调优的情况下,USRBIO 比 **Fuse Client **模式顺序写性能提升 20%-40%,其他场景性能还在进一步验证中)3FS 使用 Pybind 定义 Python 扩展模块 hf3fs_py_usrbio,这也方便 Python 能够访问 hf3fs 的功能。以此推测 USRBIO 模式适合在大模型训练和推理等对性能有极致需求的场景中使用。另外,从上面分析我们注意到 Fuse Daemon 在两种客户端模式下都起到核心作用的重要组件。在 Fuse Client 模式中,它通过 fuseMainLoop 创建 FuseClients,注册 fuse 相关 op 的 hook,并根据配置拉起单线程(或多线程) fuse session loop 处理 fuse op。在 USRBIO 模式中,与 I/O 读写链路相关的 USRBIO API 通过共享内存和 Fuse Daemon 通信,部分与 I/O 无关的控制路径请求例如 hardlink,punchhole 等,USRBIO API 则还是通过 ioctl 直接走了内核 FUSE 路径。这可能是一个 tradeoff 的设计,后面会做讨论。基础组件ServerLauncherFuse Daemon 也就是 FuseApplication, 通过 core::ServerLauncher 拉起。同样的还有 MgmtdServer,MetaServer,StorageServer 都是类似的 Daemon。Fuse Daemon 拉起之后就创建一个 FuseClients 进行核心功能操作。FuseClients一个 FuseApplication 包含 一个 FuseClients,一个 FuseClients 和一个挂载点对应。FuseClients 主要包括下图所示组件,其中包含与其他组件(meta,mgmtd,storage)打交道的 "client for client"。FuseClients 在启动时也会初始化 mgmtdClient,创建 StorageClient,metaClient,启动周期性 Sync Runner(用来更新文件长度等元数据),创建 notifyInvalExec 线程池等。同时还为每一个 FuseClients 创建一组 IOV 和 IOR。FuseClients 最重要的部分还是在和 USRBIO 协同设计。下面我们着重分析这部分。USRBIO 的设计和思考3FS USRBIO 设计思想借鉴了 io_uring 以及 SPDK NVMe 协议栈的设计。原生 io_uring [2] 由一组 Submission Queue 和 Completion Queue 组成,每个 queue 是一个 ring buffer。用户进程提交请求到 SQ,内核选择 polling 模式或事件驱动模式处理 SQ 中的请求,完成之后内核向 CQ 队尾 put 完成 entry,应用程序根据 polling 模式或者事件驱动模式处理 CQ 队首的请求。整个过程无锁,共享内存无内存拷贝。在 polling 模式下,io_uring 接近纯用户态 SPDK polling mode 性能,但是 io_uring 需要通过额外的 CPU cost 达到这个效果。3FS USRBIO 的核心设计围绕 ior 和 iov 来开展。 ior 是一个用于用户进程与 FUSE 进程之间通信的小型共享内存环。用户进程将读/写请求入队,而 FUSE 进程从队列中取出这些请求并完成执行。ior 记录的是读写操作的元数据,并不包含用户数据,其对应的用户数据 buffer 指向一个叫做 iov 的共享内存文件。这个 iov 映射到用户进程和 FUSE 进程共享的一段内存,其中 InfiniBand 内存注册由 FUSE 进程管理。在 USRBIO 中,所有的读取数据都会被读取到 iov,而所有的写入数据应由用户先写入 iov。ior 用来管理 op 操作任务,和 io_uring 不同的是这个 queue 中既包括提交 I/O 请求(sqe)又接收完成 I/O 结果(cqe),而且不通过 kernel,纯用户态操作。ior 中包含的 sqeSection 和 cqeSection 的地址范围由创建 ring 的时候计算出来的 entries 个数确定,用来查询 sqe 和 cqe 在 ring 中的 位置。ior 中还包含一个 ringSection,这个 section 用来帮助 sqe 定位 iov id 的索引和位置。如下图所示,sqe 里包含 idx 是 IOArgs* ringSection 这个数组的下标,索引后才是真正的 io 参数。例如:seq -> ringSection[idx] -> IovId -> Iov。USRBIO 中提供了一个 API hf3fs_iorwrap 用来创建和管理 ior,其中 Hf3fsIorHandle 用来管理 ior。之后 hf3fs_iorwrap 会通过 cqeSem 解析 submit-ios 信号量的路径,并通过 sem_open 打开关联信号量,用于 I/O 任务同步。这里的信号量根据优先级被放置在不同目录中。之后在提交 IO 过程中,会 post 信号量通知 cqe section 中 available 的 slots。在 ior 中,通过 IoRingJob 分配工作,任务被拆分成 IoRingJob,每个任务会处理一定数量的 I/O 请求做批处理。和 io_uring 一样,采用 shared memory 减少用户态与内核态切换。1. IoRing 初始化资源2. 提交 I/O 请求 addSqe3. 获取待处理的 I/O 任务 IoRing::jobsToProc4. 处理 I/O 任务 IoRing::process,如上图所示。IoRing::process() -->ioExec.addWrite() --> ioExec.executeWrite()--> ioExec.finishIo()IoRing 中的 ioExec 就是 PioV。PioV::executeWrite() 执行写操作中根据是否需要 truncate chunk,选择将 truncate WriteIO 包到一个 std::vectorstorage::client::WriteIO wios2中,或者直接传输std::vectorstorage::client::WriteIO wios_,最后通过 StorageClient::batchWrite() 将 Write IO 通过发送 RPC 写请求到 Storage 端。其中,写请求 WriteReq 包括 payload,tag,retryCount,userInfo,featureFlags 等字段。FuseClients 中最核心的逻辑之一在 ioRingWorker 中。它负责从 FuseClients 的 ior job queue 中拿到一个 ior,并调用 process 处理它。在处理过程中考虑了取消任务的设计,这里使用了一个 co_withCancellation 来封装,它能够在异步操作中优雅地处理任务取消,避免不必要的计算或资源占用,并且支持嵌套任务的取消感知。有关 co_cancellation 的原理可以参考 [3]:另外,还支持可配置的对任务 job 分优先级,优先级高的 job 优先处理。这些优化都能在复杂的场景下让性能得到极致提升。值得提到的一点是,所有的 iovs 共享内存文件在挂载点 3fs-virt/iovs/ 目录下均建有 symlink,指向 /dev/shm 下的对应文件。USRBIO 代码逻辑错综复杂,偏差之处在所难免,在这里抛砖引玉一些阅读代码的思路和头绪,如有错误也请不吝批评指正。关于USRBIO的思考USRBIO 在共享内存设计上使用了映射到物理内存的一个文件上,而不是使用匿名映射到物理内存。这可能是因为用户进程和 FUSE Daemon 进程不是父子进程关系。实现非派生关系进程间的内存共享,只能使用基于文件的映射或 POSIX 共享内存对象。USRBIO 没有采用直接以 SDK 形式,放弃 Fuse Daemon,直接和元数据服务器与 Chunk Server 来通信的方式设计客户端,而采用了关键 I/O 路径使用纯用户态共享内存,非关键路径上依旧复用 libfuse 这种方式。这可能是简化控制链路设计,追求 FUSE 上的复用性,追求关键路径性能考虑。另外在 IoRing 的设计上并没有使用类似 io_uring 中的可配置的 polling 模式,而是采用信号量进行同步,这里暂时还没有理解背后的原因是什么。USRBIO 使用共享内存还是不可避免会带来一些开销和性能损耗,如此设计的本质原因还是所有核心逻辑都做在了 FUSE Daemon 进程中。如果提供重客户端 SDK,所有逻辑都实现在 SDK 中,以动态连接库形式发布给客户端,可能就不需要进行这样的 IoRing 设计,或者只需要保留 io_uring 这样的无锁设计,不再需要共享内存设计。这样的好处和坏处都很鲜明:好处是 SDK 的实现能避免跨进程的通信开销,性能能达到理想的极限;坏处是如果需要保留 FUSE 功能的话需要实现两套代码,逻辑还很雷同,带来较大的开发和维护成本。而且 SDK 的升级比较重,对客户端造成的影响相对较大。当然从工程角度上可以由 FUSE 抽象出公共函数库让 Native Client 直接调用也可以避免重复开发
最新发布
04-08
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值