开源 Objective-C IOS 应用开发(十)数据持久化--文件

  文章的目的为了记录使用Objective-C 进行IOS app 开发学习的经历。本职为嵌入式软件开发,公司安排开发app,临时学习,完成app的开发。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。

 相关链接:

开源 Objective-C IOS 应用开发(一)macOS 的使用

开源 Objective-C IOS 应用开发(二)Xcode安装

开源 Objective-C IOS 应用开发(三)第一个iPhone的APP

开源 Objective-C IOS 应用开发(四)Xcode工程文件结构

开源 Objective-C IOS 应用开发(五)iOS操作(action)和输出口(Outlet)

开源 Objective-C IOS 应用开发(六)Objective-C 和 C语言

开源 Objective-C IOS 应用开发(七)Objective-C核心代码示例

开源 Objective-C IOS 应用开发(八)常见控件UI

开源 Objective-C IOS 应用开发(九)复杂控件-tableview

开源 Objective-C IOS 应用开发(十)数据持久化--文件

开源 Objective-C IOS 应用开发(十一)数据持久化--sqlite

开源 Objective-C IOS 应用开发(十二)通讯--ble

开源 Objective-C IOS 应用开发(十三)通讯--Http访问

开源 Objective-C IOS 应用开发(十四)传感器--陀螺仪和gps

开源 Objective-C IOS 应用开发(十五)通讯--蓝牙ble扫描

开源 Objective-C IOS 应用开发(十六)Storyboard模式下的纯代码界面

开源 Objective-C IOS 应用开发(十七)CAF音频的录制

开源 Objective-C IOS 应用开发(十八)音频的播放

开源 Objective-C IOS 应用开发(十九)视频的播放

开源 Objective-C IOS 应用开发(二十)多线程处理

开源 Objective-C IOS 应用开发(二十一)自定义控件--示波器

开源 Objective-C IOS 应用开发(二十二)自定义控件--车速仪表盘

 推荐链接:

开源 Arkts 鸿蒙应用 开发(一)工程文件分析-优快云博客

开源 Arkts 鸿蒙应用 开发(二)封装库.har制作和应用-优快云博客

开源 Arkts 鸿蒙应用 开发(三)Arkts的介绍-优快云博客

开源 Arkts 鸿蒙应用 开发(四)布局和常用控件-优快云博客

开源 Arkts 鸿蒙应用 开发(五)控件组成和复杂控件-优快云博客

开源 Arkts 鸿蒙应用 开发(六)数据持久--文件和首选项存储-优快云博客

开源 Arkts 鸿蒙应用 开发(七)数据持久--sqlite关系数据库-优快云博客

开源 Arkts 鸿蒙应用 开发(八)多媒体--相册和相机-优快云博客

开源 Arkts 鸿蒙应用 开发(九)通讯--tcp客户端-优快云博客

开源 Arkts 鸿蒙应用 开发(十)通讯--Http-优快云博客

开源 Arkts 鸿蒙应用 开发(十一)证书和包名修改-优快云博客

开源 Arkts 鸿蒙应用 开发(十二)传感器的使用-优快云博客

开源 Arkts 鸿蒙应用 开发(十三)音频--MP3播放_arkts avplayer播放音频 mp3-优快云博客

开源 Arkts 鸿蒙应用 开发(十四)线程--任务池(taskpool)-优快云博客

开源 Arkts 鸿蒙应用 开发(十五)自定义绘图控件--仪表盘-优快云博客

开源 Arkts 鸿蒙应用 开发(十六)自定义绘图控件--波形图-优快云博客

开源 Arkts 鸿蒙应用 开发(十七)通讯--http多文件下载-优快云博客

开源 Arkts 鸿蒙应用 开发(十八)通讯--Ble低功耗蓝牙服务器-优快云博客

推荐链接:

开源 java android app 开发(一)开发环境的搭建-优快云博客

开源 java android app 开发(二)工程文件结构-优快云博客

开源 java android app 开发(三)GUI界面布局和常用组件-优快云博客

开源 java android app 开发(四)GUI界面重要组件-优快云博客

开源 java android app 开发(五)文件和数据库存储-优快云博客

开源 java android app 开发(六)多媒体使用-优快云博客

开源 java android app 开发(七)通讯之Tcp和Http-优快云博客

开源 java android app 开发(八)通讯之Mqtt和Ble-优快云博客

开源 java android app 开发(九)后台之线程和服务-优快云博客

开源 java android app 开发(十)广播机制-优快云博客

开源 java android app 开发(十一)调试、发布-优快云博客

开源 java android app 开发(十二)封库.aar-优快云博客
 

本章内容主要是演示了两种数据存储方式:NSUserDefaults 和文件存储。

目录:

1.手机演示

2.所有源码

3.源码分析

一、手机演示

二、所有源码

AppDelegate.h

//
//  AppDelegate.h
//  file
//
//  Created by Mixic2025 on 2025/11/12.
//

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>
@end

AppDelegate.m

//
//  AppDelegate.m
//  file
//
//  
//

#import "AppDelegate.h"
#import "ViewController.h"

@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    
    // 直接创建并显示窗口,不使用属性
    UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    
    ViewController *mainVC = [[ViewController alloc] init];
    UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:mainVC];
    
    window.rootViewController = navController;
    [window makeKeyAndVisible];
    
    
    return YES;
}


#pragma mark - UISceneSession lifecycle


- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {
    // Called when a new scene session is being created.
    // Use this method to select a configuration to create the new scene with.
    return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role];
}


- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<UISceneSession *> *)sceneSessions {
    // Called when the user discards a scene session.
    // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
    // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}


@end

SceneDelegate.h

//
//  SceneDelegate.h
//  file
//
//  
//

#import <UIKit/UIKit.h>

@interface SceneDelegate : UIResponder <UIWindowSceneDelegate>

@property (strong, nonatomic) UIWindow * window;

@end

SceneDelegate.m

//
//  SceneDelegate.m
//  file
//
// 
//

#import "SceneDelegate.h"

@interface SceneDelegate ()

@end

@implementation SceneDelegate


- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
    // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
    // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
    // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
}


- (void)sceneDidDisconnect:(UIScene *)scene {
    // Called as the scene is being released by the system.
    // This occurs shortly after the scene enters the background, or when its session is discarded.
    // Release any resources associated with this scene that can be re-created the next time the scene connects.
    // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
}


- (void)sceneDidBecomeActive:(UIScene *)scene {
    // Called when the scene has moved from an inactive state to an active state.
    // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}


- (void)sceneWillResignActive:(UIScene *)scene {
    // Called when the scene will move from an active state to an inactive state.
    // This may occur due to temporary interruptions (ex. an incoming phone call).
}


- (void)sceneWillEnterForeground:(UIScene *)scene {
    // Called as the scene transitions from the background to the foreground.
    // Use this method to undo the changes made on entering the background.
}


- (void)sceneDidEnterBackground:(UIScene *)scene {
    // Called as the scene transitions from the foreground to the background.
    // Use this method to save data, release shared resources, and store enough scene-specific state information
    // to restore the scene back to its current state.
}


@end

ViewController.h

//
//  ViewController.h
//  file
//
// 
//

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

// NSUserDefaults 相关的 UI 组件
@property (nonatomic, strong) UITextField *userDefaultsWriteField;
@property (nonatomic, strong) UITextField *userDefaultsReadField;
@property (nonatomic, strong) UIButton *userDefaultsSaveButton;
@property (nonatomic, strong) UIButton *userDefaultsLoadButton;

// 文件存储相关的 UI 组件
@property (nonatomic, strong) UITextField *fileWriteField;
@property (nonatomic, strong) UITextField *fileReadField;
@property (nonatomic, strong) UIButton *fileSaveButton;
@property (nonatomic, strong) UIButton *fileLoadButton;

// 操作方法
- (void)saveToUserDefaults;
- (void)loadFromUserDefaults;
- (void)saveToFile;
- (void)loadFromFile;

@end

ViewController.m

//
//  ViewController.m
//  file
//
// 
//

/*
 
 
#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}


@end

*/

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor whiteColor];
    self.title = @"数据存储演示";
    
    [self setupUI];
}

#pragma mark - UI 设置
- (void)setupUI {
    // NSUserDefaults 部分标题
    UILabel *userDefaultsLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 100, 200, 30)];
    userDefaultsLabel.text = @"NSUserDefaults 存储";
    userDefaultsLabel.font = [UIFont boldSystemFontOfSize:16];
    userDefaultsLabel.textColor = [UIColor darkTextColor];
    [self.view addSubview:userDefaultsLabel];
    
    // NSUserDefaults 写入文本框
    self.userDefaultsWriteField = [[UITextField alloc] initWithFrame:CGRectMake(20, 140, 200, 40)];
    self.userDefaultsWriteField.placeholder = @"输入要保存到UserDefaults的数据";
    self.userDefaultsWriteField.borderStyle = UITextBorderStyleRoundedRect;
    self.userDefaultsWriteField.backgroundColor = [UIColor systemGray6Color];
    [self.view addSubview:self.userDefaultsWriteField];
    
    // NSUserDefaults 保存按钮
    self.userDefaultsSaveButton = [UIButton buttonWithType:UIButtonTypeSystem];
    self.userDefaultsSaveButton.frame = CGRectMake(230, 140, 80, 40);
    [self.userDefaultsSaveButton setTitle:@"保存" forState:UIControlStateNormal];
    [self.userDefaultsSaveButton addTarget:self action:@selector(saveToUserDefaults) forControlEvents:UIControlEventTouchUpInside];
    self.userDefaultsSaveButton.backgroundColor = [UIColor systemBlueColor];
    self.userDefaultsSaveButton.tintColor = [UIColor whiteColor];
    self.userDefaultsSaveButton.layer.cornerRadius = 8;
    [self.view addSubview:self.userDefaultsSaveButton];
    
    // NSUserDefaults 读取文本框
    self.userDefaultsReadField = [[UITextField alloc] initWithFrame:CGRectMake(20, 190, 200, 40)];
    self.userDefaultsReadField.placeholder = @"显示从UserDefaults读取的数据";
    self.userDefaultsReadField.borderStyle = UITextBorderStyleRoundedRect;
    self.userDefaultsReadField.backgroundColor = [UIColor systemGray6Color];
    self.userDefaultsReadField.enabled = NO; // 只读
    [self.view addSubview:self.userDefaultsReadField];
    
    // NSUserDefaults 读取按钮
    self.userDefaultsLoadButton = [UIButton buttonWithType:UIButtonTypeSystem];
    self.userDefaultsLoadButton.frame = CGRectMake(230, 190, 80, 40);
    [self.userDefaultsLoadButton setTitle:@"读取" forState:UIControlStateNormal];
    [self.userDefaultsLoadButton addTarget:self action:@selector(loadFromUserDefaults) forControlEvents:UIControlEventTouchUpInside];
    self.userDefaultsLoadButton.backgroundColor = [UIColor systemGreenColor];
    self.userDefaultsLoadButton.tintColor = [UIColor whiteColor];
    self.userDefaultsLoadButton.layer.cornerRadius = 8;
    [self.view addSubview:self.userDefaultsLoadButton];
    
    // 文件存储部分标题
    UILabel *fileLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 260, 200, 30)];
    fileLabel.text = @"文件存储";
    fileLabel.font = [UIFont boldSystemFontOfSize:16];
    fileLabel.textColor = [UIColor darkTextColor];
    [self.view addSubview:fileLabel];
    
    // 文件写入文本框
    self.fileWriteField = [[UITextField alloc] initWithFrame:CGRectMake(20, 300, 200, 40)];
    self.fileWriteField.placeholder = @"输入要保存到文件的数据";
    self.fileWriteField.borderStyle = UITextBorderStyleRoundedRect;
    self.fileWriteField.backgroundColor = [UIColor systemGray6Color];
    [self.view addSubview:self.fileWriteField];
    
    // 文件保存按钮
    self.fileSaveButton = [UIButton buttonWithType:UIButtonTypeSystem];
    self.fileSaveButton.frame = CGRectMake(230, 300, 80, 40);
    [self.fileSaveButton setTitle:@"保存" forState:UIControlStateNormal];
    [self.fileSaveButton addTarget:self action:@selector(saveToFile) forControlEvents:UIControlEventTouchUpInside];
    self.fileSaveButton.backgroundColor = [UIColor systemOrangeColor];
    self.fileSaveButton.tintColor = [UIColor whiteColor];
    self.fileSaveButton.layer.cornerRadius = 8;
    [self.view addSubview:self.fileSaveButton];
    
    // 文件读取文本框
    self.fileReadField = [[UITextField alloc] initWithFrame:CGRectMake(20, 350, 200, 40)];
    self.fileReadField.placeholder = @"显示从文件读取的数据";
    self.fileReadField.borderStyle = UITextBorderStyleRoundedRect;
    self.fileReadField.backgroundColor = [UIColor systemGray6Color];
    self.fileReadField.enabled = NO; // 只读
    [self.view addSubview:self.fileReadField];
    
    // 文件读取按钮
    self.fileLoadButton = [UIButton buttonWithType:UIButtonTypeSystem];
    self.fileLoadButton.frame = CGRectMake(230, 350, 80, 40);
    [self.fileLoadButton setTitle:@"读取" forState:UIControlStateNormal];
    [self.fileLoadButton addTarget:self action:@selector(loadFromFile) forControlEvents:UIControlEventTouchUpInside];
    self.fileLoadButton.backgroundColor = [UIColor systemPurpleColor];
    self.fileLoadButton.tintColor = [UIColor whiteColor];
    self.fileLoadButton.layer.cornerRadius = 8;
    [self.view addSubview:self.fileLoadButton];
    
    // 添加手势隐藏键盘
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hideKeyboard)];
    [self.view addGestureRecognizer:tapGesture];
}

#pragma mark - NSUserDefaults 操作
- (void)saveToUserDefaults {
    NSString *textToSave = self.userDefaultsWriteField.text;
    
    if (textToSave.length == 0) {
        [self showAlertWithTitle:@"提示" message:@"请输入要保存的数据"];
        return;
    }
    
    // 使用 NSUserDefaults 保存数据
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:textToSave forKey:@"savedText"];
    [defaults synchronize]; // 立即保存
    
    NSLog(@"数据已保存到 NSUserDefaults: %@", textToSave);
    [self showAlertWithTitle:@"成功" message:@"数据已保存到 NSUserDefaults"];
    
    // 清空输入框
    self.userDefaultsWriteField.text = @"";
    [self hideKeyboard];
}

- (void)loadFromUserDefaults {
    // 从 NSUserDefaults 读取数据
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSString *savedText = [defaults objectForKey:@"savedText"];
    
    if (savedText) {
        self.userDefaultsReadField.text = savedText;
        NSLog(@"从 NSUserDefaults 读取数据: %@", savedText);
    } else {
        self.userDefaultsReadField.text = @"暂无数据";
        [self showAlertWithTitle:@"提示" message:@"没有找到保存的数据"];
    }
}

#pragma mark - 文件操作
- (void)saveToFile {
    NSString *textToSave = self.fileWriteField.text;
    
    if (textToSave.length == 0) {
        [self showAlertWithTitle:@"提示" message:@"请输入要保存的数据"];
        return;
    }
    
    // 获取 Documents 目录路径
    NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
    NSString *filePath = [documentsPath stringByAppendingPathComponent:@"savedData.txt"];
    
    NSError *error = nil;
    BOOL success = [textToSave writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
    
    if (success) {
        NSLog(@"数据已保存到文件: %@", filePath);
        [self showAlertWithTitle:@"成功" message:@"数据已保存到文件"];
        
        // 清空输入框
        self.fileWriteField.text = @"";
        [self hideKeyboard];
    } else {
        NSLog(@"保存文件失败: %@", error);
        [self showAlertWithTitle:@"错误" message:@"保存文件失败"];
    }
}

- (void)loadFromFile {
    // 获取 Documents 目录路径
    NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
    NSString *filePath = [documentsPath stringByAppendingPathComponent:@"savedData.txt"];
    
    NSError *error = nil;
    NSString *savedText = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];
    
    if (savedText) {
        self.fileReadField.text = savedText;
        NSLog(@"从文件读取数据: %@", savedText);
    } else {
        self.fileReadField.text = @"暂无数据";
        [self showAlertWithTitle:@"提示" message:@"没有找到保存的文件或文件为空"];
    }
}

#pragma mark - 辅助方法
- (void)showAlertWithTitle:(NSString *)title message:(NSString *)message {
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:title
                                                                   message:message
                                                            preferredStyle:UIAlertControllerStyleAlert];
    
    UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil];
    [alert addAction:okAction];
    
    [self presentViewController:alert animated:YES completion:nil];
}

- (void)hideKeyboard {
    [self.view endEditing:YES];
}

@end

三、源码分析

1. AppDelegate.m - 应用入口

objc

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 手动创建窗口(不使用Storyboard)
    UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];

    ViewController *mainVC = [[ViewController alloc] init];
    UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:mainVC];

    window.rootViewController = navController;
    [window makeKeyAndVisible];

    return YES;
}

关键点:

  • 纯代码创建 UI,不使用 Storyboard

  • 创建了 UINavigationController 作为根控制器

  • makeKeyAndVisible 使窗口可见并成为关键窗口

2. SceneDelegate.m - 场景管理(iOS 13+)

这个文件包含了场景生命周期的回调方法,但在这个应用中实际没有使用,因为窗口是在 AppDelegate 中手动创建的。

3. ViewController.h - 界面定义

定义了两种存储方式对应的 UI 组件:

  • NSUserDefaults 组件:写入字段、读取字段、保存按钮、加载按钮

  • 文件存储组件:类似的四个组件

  • 操作方法:四种对应的操作方怯

4. ViewController.m - 主要实现

UI 设置 (setupUI 方法)

创建了两组对称的 UI 组件:

  • 标签:说明各部分功能

  • 文本框:用于输入和显示数据

  • 按钮:触发保存和读取操作

布局特点:

  • 使用绝对坐标布局(CGRectMake)

  • 为按钮设置了不同的背景色和圆角

  • 添加了手势识别器用于隐藏键盘

NSUserDefaults 操作

保存数据 (saveToUserDefaults):

objc

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:textToSave forKey:@"savedText"];
[defaults synchronize]; // 立即保存

读取数据 (loadFromUserDefaults):

objc

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *savedText = [defaults objectForKey:@"savedText"];

NSUserDefaults 特点:

  • 适合存储小量数据(偏好设置、配置信息)

  • 数据存储在 plist 文件中

  • 读写速度快

  • 应用删除时数据也会被清除

文件存储操作

保存数据 (saveToFile):

objc

NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *filePath = [documentsPath stringByAppendingPathComponent:@"savedData.txt"];
[textToSave writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];

读取数据 (loadFromFile):

objc

NSString *savedText = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];

文件存储特点:

  • 适合存储大量数据或复杂数据结构

  • 数据存储在应用的 Documents 目录

  • 应用删除时数据会被清除

  • 可以存储各种格式的数据

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值