iOS中CoreData的增刪改查相關使用技巧

image.png

  1. 創建項目時選擇
    image.png
  2. 創建實體(選擇項目中的.xcdatamodeld後綴文件)
    image.png
  3. 創建實體類
    image.png
  4. 將AppDelegate中的coreData相關剪切到自己新建的類中(CoreDataStack)裏面有增刪改查的方法
//
//  CoreDataStack.m
//  MyCoreDataDemo
//
//  Created by 陳帆 on 2019/11/6.
//  Copyright © 2019 陳帆. All rights reserved.
//

#import "CoreDataStack.h"
#import "User+CoreDataClass.h"

#define tableName @"User"

@interface CoreDataStack()

/// 文件目錄
@property(nonatomic, copy) NSString *documentDir;

/// 上下文
@property(nonatomic, retain) NSManagedObjectContext *context;

@property(nonatomic, retain) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@property(nonatomic, copy) NSManagedObjectModel *managedObjectModel;

@end

@implementation CoreDataStack

#pragma mark - Core Data stack

@synthesize persistentContainer = _persistentContainer;


/// 單例
+ (instancetype)shareInstance {
    static CoreDataStack *instance = nil;
    static dispatch_once_t onceToken;
    
    dispatch_once(&onceToken, ^{
        instance =[[CoreDataStack alloc] init];
    });
    
    return instance;
}



- (NSPersistentCloudKitContainer *)persistentContainer {
    // The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
    @synchronized (self) {
        if (_persistentContainer == nil) {
            _persistentContainer = [[NSPersistentCloudKitContainer alloc] initWithName:@"MyCoreDataDemo"];
            [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
                if (error != nil) {
                    // Replace this implementation with code to handle the error appropriately.
                    // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                    
                    /*
                     Typical reasons for an error here include:
                     * The parent directory does not exist, cannot be created, or disallows writing.
                     * The persistent store is not accessible, due to permissions or data protection when the device is locked.
                     * The device is out of space.
                     * The store could not be migrated to the current model version.
                     Check the error message to determine what the actual problem was.
                    */
                    NSLog(@"Unresolved error %@, %@", error, error.userInfo);
                    abort();
                }
            }];
        }
    }
    
    return _persistentContainer;
}

#pragma mark - Core Data Saving support

- (void)saveContext {
    NSManagedObjectContext *context = self.persistentContainer.viewContext;
    NSError *error = nil;
    if ([context hasChanges] && ![context save:&error]) {
        // Replace this implementation with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        NSLog(@"Unresolved error %@, %@", error, error.userInfo);
        abort();
    }
}

- (NSString *)documentDir {
    if (!_documentDir) {
        _documentDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    }
    
    return _documentDir;
}

- (NSManagedObjectContext *)context {
    if (!_context) {
//        _context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        _context = self.persistentContainer.viewContext;
    }
    
    return _context;
}

- (NSManagedObjectModel *)managedObjectModel {
    if (_managedObjectModel) {
        _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"coreData" withExtension:@"momd"]];
    }
    return _managedObjectModel;
}

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    if (!_persistentStoreCoordinator) {
        _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:_managedObjectModel];
        NSString *sqliteURL = [_documentDir stringByAppendingPathComponent:@"coreData.sqlite"];
        NSDictionary *options = @{
            NSMigratePersistentStoresAutomaticallyOption : @YES,
            NSInferMappingModelAutomaticallyOption : @YES
        };
        NSString *failureReason = @"There was an error creating or loading the application's saved data.";
        @try {
            [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:sqliteURL] options:options error:nil];
        } @catch (NSException *exception) {
            NSMutableDictionary *dict = [NSMutableDictionary dictionary];
            dict[NSLocalizedDescriptionKey] = @"Failed to initialize the application's saved data";
            dict[NSLocalizedFailureReasonErrorKey] = failureReason;
            NSError *wrappedError = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:6666 userInfo:dict];
            NSLog(@"Unresolved error %@, %@", wrappedError, wrappedError.userInfo);
            abort();
        } @finally {
        }
    }
    
    return _persistentStoreCoordinator;
}


- (User *)newUserEntity {
    return [NSEntityDescription insertNewObjectForEntityForName:tableName inManagedObjectContext:self.context];
}

/// 增加用戶實體
/// @param user 用戶對象
- (void)addUser:(User *)user {
    [self saveContext];
}


/// 獲取用戶列表
- (NSArray<User *> *)getUserList {
    @try {
        NSArray *userList = [self.context executeFetchRequest:[User fetchRequest] error:nil];
        return userList;
    } @catch (NSException *exception) {
        NSLog(@"getUserList error:%@", exception.description);
    }
}

/// 刪除指定索引用戶
/// @param index 索引值
- (void)deleteUser:(NSUInteger)index {
    @try {
        NSArray *userList = [self.context executeFetchRequest:[User fetchRequest] error:nil];
        if (userList.count > 0 && index < userList.count) {
            [self.context deleteObject:userList[0]];
            [[CoreDataStack shareInstance] saveContext];
        } else {
             NSLog(@"deleteUser failed. reason:data is null or index larger array count");
        }
    } @catch (NSException *exception) {
        NSLog(@"deleteUser error. %@", exception.description);
    }
}

/// 刪除所有用戶
- (void)deleteAllUser {
    @try {
        NSArray *userList = [self.context executeFetchRequest:[User fetchRequest] error:nil];
        if (userList.count > 0) {
            [self.context deletedObjects];
            [[CoreDataStack shareInstance] saveContext];
        } else {
             NSLog(@"deleteUser failed. reason:data is null");
        }
    } @catch (NSException *exception) {
        NSLog(@"deleteUser error. %@", exception.description);
    }
}

/// 更新(修改)用戶
/// @param user 用戶對象
- (void)updateUser:(User *)user {
    @try {
        NSArray *userList = [self.context executeFetchRequest:[User fetchRequest] error:nil];
        for (User *tempUser in userList) {
            if ([tempUser.userId isEqual:user.userId]) {
                tempUser.username = user.username;
                tempUser.age = user.age;
                tempUser.isMan = user.isMan;
                break;
            }
        }
        [[CoreDataStack shareInstance] saveContext];
    } @catch (NSException *exception) {
        NSLog(@"deleteUser error. %@", exception.description);
    }
}

@end

  1. 調用相關的增刪改查方法
//
//  ViewController.m
//  MyCoreDataDemo
//
//  Created by 陳帆 on 2019/11/6.
//  Copyright © 2019 陳帆. All rights reserved.
//

#import "ViewController.h"

#import "User+CoreDataClass.h"
#import "CoreDataStack.h"


@interface ViewController ()<UITableViewDelegate, UITableViewDataSource>

@property(nonatomic, strong) NSMutableArray *dataSource;
@property(nonatomic, strong) UITableView *tableView;
@property(nonatomic, weak) UIButton *addBtn;
@property(nonatomic, weak) UIButton *deleteBtn;
@property(nonatomic, weak) UIButton *modifyBtn;
@property(nonatomic, weak) UIButton *queryBtn;

@property(nonatomic, weak) UILabel *noDataTipLabel;

@end

@implementation ViewController

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

- (void)setViewUI {
    // 設置增加按鈕
    UIButton *addBtn = [[UIButton alloc] init];
    self.addBtn = addBtn;
    [addBtn setTitle:@"增加" forState:UIControlStateNormal];
    addBtn.backgroundColor = [UIColor blueColor];
    [addBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    addBtn.titleLabel.font = [UIFont systemFontOfSize:14.0];
    [addBtn addTarget:self action:@selector(addBtnClick:) forControlEvents:UIControlEventTouchUpInside];
    
    // 設置刪除按鈕
    UIButton *deleteBtn = [[UIButton alloc] init];
    self.deleteBtn = deleteBtn;
    [deleteBtn setTitle:@"刪除" forState:UIControlStateNormal];
    deleteBtn.backgroundColor = [UIColor blueColor];
    [deleteBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    deleteBtn.titleLabel.font = [UIFont systemFontOfSize:14.0];
    [deleteBtn addTarget:self action:@selector(deleteBtnClick:) forControlEvents:UIControlEventTouchUpInside];
    
    // 設置修改按鈕
    UIButton *modifyBtn = [[UIButton alloc] init];
    self.modifyBtn = modifyBtn;
    [modifyBtn setTitle:@"修改" forState:UIControlStateNormal];
    modifyBtn.backgroundColor = [UIColor blueColor];
    [modifyBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    modifyBtn.titleLabel.font = [UIFont systemFontOfSize:14.0];
    [modifyBtn addTarget:self action:@selector(modifyBtnClick:) forControlEvents:UIControlEventTouchUpInside];
    
    // 設置查詢按鈕
    UIButton *queryBtn = [[UIButton alloc] init];
    self.queryBtn = queryBtn;
    [queryBtn setTitle:@"查詢" forState:UIControlStateNormal];
    queryBtn.backgroundColor = [UIColor blueColor];
    [queryBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    queryBtn.titleLabel.font = [UIFont systemFontOfSize:14.0];
    [queryBtn addTarget:self action:@selector(queryBtnClick:) forControlEvents:UIControlEventTouchUpInside];
    
    // 沒有數據提醒
    UILabel *noDataTipLabel = [[UILabel alloc] init];
    self.noDataTipLabel = noDataTipLabel;
    noDataTipLabel.text = @"沒有數據啦";
    noDataTipLabel.textColor = [UIColor lightGrayColor];
    noDataTipLabel.textAlignment = NSTextAlignmentCenter;
    noDataTipLabel.font = [UIFont systemFontOfSize:14.0];
    noDataTipLabel.numberOfLines = 0;
//    noDataTipLabel.hidden = YES;
    
    
    self.tableView.layer.masksToBounds = true;
    self.tableView.layer.cornerRadius = 10;
    self.tableView.backgroundColor = [UIColor groupTableViewBackgroundColor];
    self.tableView.tableFooterView = [UIView new];
    self.tableView.delegate = self;
    self.tableView.dataSource = self;
    
    [self.view addSubview:addBtn];
    [self.view addSubview:modifyBtn];
    [self.view addSubview:deleteBtn];
    [self.view addSubview:queryBtn];
    self.tableView.backgroundView = self.noDataTipLabel;
    [self.view addSubview:self.tableView];
    self.view.backgroundColor = [UIColor darkGrayColor];
}

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    
    // 設置佈局適配
    int btnWidth = 70, btnHeight = 30, gapXY = 20;
    [self.addBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).with.offset(gapXY*2);
        make.right.equalTo(self.view.mas_centerX).with.offset(-gapXY/2);
        make.width.mas_equalTo(btnWidth);
        make.height.mas_equalTo(btnHeight);
    }];
    
    [self.deleteBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).with.offset(gapXY*2);
        make.left.equalTo(self.view.mas_centerX).with.offset(gapXY/2);
        make.width.mas_equalTo(btnWidth);
        make.height.mas_equalTo(btnHeight);
    }];
    
    [self.modifyBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.addBtn.mas_bottom).with.offset(gapXY);
        make.right.equalTo(self.view.mas_centerX).with.offset(-gapXY/2);
        make.width.mas_equalTo(btnWidth);
        make.height.mas_equalTo(btnHeight);
    }];
    
    [self.queryBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.deleteBtn.mas_bottom).with.offset(gapXY);
        make.left.equalTo(self.view.mas_centerX).with.offset(gapXY/2);
        make.width.mas_equalTo(btnWidth);
        make.height.mas_equalTo(btnHeight);
    }];
    
    [self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.modifyBtn.mas_bottom).with.offset(gapXY);
        make.left.mas_equalTo(gapXY);
        make.right.mas_equalTo(-gapXY);
        make.bottom.equalTo(self.view.mas_bottom).with.offset(-gapXY);
    }];
    
    [self.noDataTipLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.mas_equalTo(self.tableView.mas_centerX);
        make.centerY.mas_equalTo(self.tableView.mas_centerY);
        make.width.mas_equalTo(self.tableView.mas_width);
        make.height.mas_equalTo(btnHeight);
    }];
}

// MARK: - selector action funcation
// MARK: 增加按鈕響應
- (void)addBtnClick:(UIButton *)sender {
    if (@available(iOS 13.0, *)) {
        User *user = [[CoreDataStack shareInstance] newUserEntity];
        user.userId = [NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]];
        user.username = [NSString stringWithFormat:@"張三%ld", random()];
        user.age = 18;
        user.createTime = [NSDate date];
        user.income = [[NSDecimalNumber alloc] initWithFloat:1233.04338];
        user.isMan = YES;
        user.userIcon = @"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1573042496310&di=d6b4363fe0bb7455d97672b4d5d0e316&imgtype=jpg&src=http%3A%2F%2Fimg1.imgtn.bdimg.com%2Fit%2Fu%3D4232323981%2C903032036%26fm%3D214%26gp%3D0.jpg";
        
        [[CoreDataStack shareInstance] addUser:user];
        
        // 查詢
        [self queryBtnClick:nil];
    } else {
        // Fallback on earlier versions
    }
}

// MARK: 刪除按鈕響應
- (void)deleteBtnClick:(UIButton *)sender {
    if (@available(iOS 13.0, *)) {
        [[CoreDataStack shareInstance] deleteUser:0];
        
        [self queryBtnClick:nil];
    } else {
        // Fallback on earlier versions
    }
    
    NSLog(@"刪除成功");
}

// MARK: 需改按鈕響應
- (void)modifyBtnClick:(UIButton *)sender {
    if (self.dataSource.count > 0) {
        [self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] animated:true scrollPosition:UITableViewScrollPositionTop];
        [self tableView:self.tableView didSelectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
    }
}

// MARK: 查詢按鈕響應
- (void)queryBtnClick:(UIButton *)sender {
    [self.dataSource removeAllObjects];
    if (@available(iOS 13.0, *)) {
        NSArray *userList = [[CoreDataStack shareInstance] getUserList];
        if (userList.count > 0) {
            [self.dataSource addObjectsFromArray:userList];
        }
    } else {
        // Fallback on earlier versions
    }
    
    [self.tableView reloadData];
}


// MARK: - UITableView Delegate
// MARK: section Count
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

// MARK: row count in section
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    self.noDataTipLabel.hidden = self.dataSource.count > 0;
    return self.dataSource.count;
}

// MARK: cell content
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"];
        cell.detailTextLabel.numberOfLines = 0;
    }
    
    // 填充數據
    User *user = self.dataSource[indexPath.row];
    cell.textLabel.text = user.username;
    cell.detailTextLabel.text = [NSString stringWithFormat:@"age:%d\ncreateTime:%@\nincome:%@\nisMan:%@\nuserId:%@", user.age, user.createTime, user.income, user.isMan ? @"YES":@"NO", user.userId];
    [cell.imageView sd_setImageWithURL:[NSURL URLWithString:user.userIcon] completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
        if (image) {
            // 調整圖片大小
            CGSize imageSize = CGSizeMake(70, 70);
            UIGraphicsBeginImageContextWithOptions(imageSize, NO, UIScreen.mainScreen.scale);
            [image drawInRect:CGRectMake(0, 0, imageSize.width, imageSize.height)];
            cell.imageView.image = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
        }
    }];
    
    return cell;
}

// MARK: cell height
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 140;
}

// MARK: did selected
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
     UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:@"請輸入個人信息" preferredStyle:UIAlertControllerStyleAlert];
    
    // selected user
    User *user = self.dataSource[indexPath.row];
    
     //增加確定按鈕;
     [alertController addAction:[UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
         //獲取第1個輸入框;
         UITextField *userNameTextField = alertController.textFields.firstObject;
         if (![userNameTextField.text isEqual:@""] && ![userNameTextField.text isEqual:user.username]) {
             user.username = userNameTextField.text;
         }
         
         //獲取第2個輸入框;
         UITextField *ageTextField = [alertController.textFields objectAtIndex:1];
         if (![ageTextField.text isEqual:@""] && ![ageTextField.text isEqual:[NSString stringWithFormat:@"%d", user.age]]) {
             user.age = [ageTextField.text intValue];
         }
         
         //獲取第3個輸入框;
         UITextField *sexField = alertController.textFields.lastObject;
         if (![sexField.text isEqual:@""]) {
             user.isMan = ![sexField.text isEqual:@"0"];
         }
         if (@available(iOS 13.0, *)) {
             [[CoreDataStack shareInstance] updateUser:user];
         } else {
             // Fallback on earlier versions
         }
         
         [self.tableView reloadData];
     }]];
    // 增加取消按鈕
    [alertController addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        NSLog(@"取消");
        [self.tableView deselectRowAtIndexPath:indexPath animated:true];
    }]];
    
    //定義第一個輸入框;
    [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
      textField.placeholder = @"請輸入用戶名";
    }];
    //定義第二個輸入框;
    [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
      textField.placeholder = @"請輸入年齡";
    }];
    //定義第三個輸入框;
    [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
      textField.placeholder = @"性別";
    }];
    [self presentViewController:alertController animated:true completion:nil];
    
}

// MARK: - get / set 方法

/// get dataSource
- (NSMutableArray *)dataSource {
    if (!_dataSource) {
        _dataSource = [[NSMutableArray alloc] init];
    }
    return _dataSource;
}

/// get tableview
- (UITableView *)tableView {
    if (!_tableView) {
        _tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
    }
    
    return _tableView;
}

@end

附件(GitHub地址):https://github.com/MacleChen/MyCoreDataDemo.git

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章