iOS 数据持久化方案-Realm的使用(下)

简介: iOS 数据持久化方案-Realm的使用(下)

(5)、查询方式四链式查询 (在查询结果的基础上, 进行二次查询)

RLMResults<Student *> *students1 = [Student allObjects];
NSLog(@"第 1 次查询的结果:%@",students1);
RLMResults<Student *> *students2 = [Student objectsWhere:@"studentName = '小王'"];
NSLog(@"第 2 次查询的结果:%@",students2);


image.png

(6)、查询方式五:分页 查询,先看一个基础知识,更多的可以看我的 数据库查询的第五部分


image.png


image.png

我们在这里采用先拿到相应条件下的数据,再根据分页(利用for循环)来展示数据
RLMResults<Student *> *students = [Student allObjects];
for (NSInteger i = 0; i < 2; i++) {
      Student *student = students[I];
      NSLog(@"结果是:%@",student);
}
  • (7)、Filtering(过滤):NSPredicate,以下内容将通过调用[RLMObject objectsWhere:]从默认Realm中检索名称以“小”开头的所有学生来扩展我们之前的示例:


// Query using a predicate string:使用谓词字符串进行查询
RLMResults<Student *> *students1 = [Student objectsWhere:@"studentName BEGINSWITH '小'"];
NSLog(@"students1=%@",students1);
// Query using an NSPredicate:使用NSPredicate查询
NSPredicate *pred = [NSPredicate predicateWithFormat:@"studentName BEGINSWITH %@",@"小"];
RLMResults<Student *> *student2 = [Student objectsWithPredicate:pred];
NSLog(@"students2=%@",student2);


image.png

提示:有关构建谓词和使用我们的NSPredicate Cheatsheet的更多信息,请参阅Apple的Predicates编程指南。Realm支持许多常见谓词:


  • 比较操作数可以是属性名称或常量。至少有一个操作数必须是属性名称。
  • 比较操作符==<= <> =>!=,和BETWEEN都支持intlonglong longfloatdouble,和NSDate属性类型,例如age == 45
  • 身份比较==!=,例如[Employee objectsWhere:@"company == %@", company]
  • 布尔属性支持比较运算符==!=
  • 对于NSStringNSData属性,支持==!=BEGINSWITHCONTAINSENDSWITH运算符,例如name CONTAINS 'Ja'
  • 对于NSString属性,LIKE运算符可用于将左手属性与右手表达式进行比较:?并且*允许作为通配符,其中?匹配1个字符并*匹配0个或更多个字符。示例:value LIKE '?bc*'匹配“abcde”和“cbc”等字符串。
  • 字符串的不区分大小写的比较,例如name CONTAINS[c] 'Ja'。请注意,只有字符“AZ”和“az”才会被忽略。在[c] modifier can be combined with the[d]`改性剂。
  • 字符串的变音符号不敏感比较,例如name BEGINSWITH[d] 'e'匹配étoile。此修饰符可与[c]修饰符组合使用。(此修饰符只能应用于Realm支持的字符串子集:请参阅详细信息的限制。)
  • Realm支持以下复合运算符:“AND”“OR”“NOT”,例如name BEGINSWITH 'J' AND age >= 32
  • 收容操作数IN,例如name IN {'Lisa', 'Spike', 'Hachi'}
  • 无比较==!=,例如[Company objectsWhere:@"ceo == nil"]。请注意,Realm将其nil视为特殊值而不是缺少值; 与SQL不同,nil等于自己。
  • 任何比较,例如ANY student.age < 21
  • 支持和属性的聚合表达式@ count@ min@ max@ sum@avg,例如,查找所有员工人数超过五人的公司。RLMArray``RLMResults``[Company objectsWhere:@"employees.@count > 5"]
  • 子查询受以下限制支持:
  • @count是唯一可以应用于SUBQUERY表达式的运算符。
  • SUBQUERY(…).@count表达式必须以恒定的相比较。
  • 尚不支持相关的子查询。


  • 3.5、Realm 支持的数据类型
  • (1)、支持的数据类型:BOOL、bool、int、NSInteger、long、long long、float、double、NSString、NSDate、NSData、NSNumber


不支持集合类型,比如:NSSarray 不能直接存储


  • (2)、不支持集合类型的解决方案
  • 序列化成 NSData 进行存储
  • 转换成 RLMArray<RLMObject> 进行存储
  • (3)、比如:存储 image图片,我们不能直接存储 UIImage,我们可以存储 NSData,如下定义


@property NSData *imageData;


提示:@property(readonly) UIImage *image; 其中  readonly 在Realm 模型里面是自动忽略的意思,当然你也可以把要忽略的字段放到下面数组里面

+ (NSArray *)ignoredProperties
{
   return @[@"image"];
}
  • 在学生 Student 里面定义狗数组,如下
  • 3.6、Realm 关系
  • (1)、对一关系:多对一或一对一关系
    比如一个学生有一只狗,那么我们就可以在学生里面定义一个狗的属性,如下


@property Dog *dog;


  • 在使用的时候和其他的属性一样,正常的赋值即可


image.png


Student *student = [[Student alloc]init];
student.studentId = 1;
student.studentName = @"小明";
Dog *dog = [[Dog alloc]init];
dog.dogId = 1;
dog.dogName = @"旺财";
student.dog = dog;
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
    [realm addObject:student];
}];


  • (2)、对多关系:
    举例:一个学生有多只狗,那么我们需要在学生里面定义一个狗的数组,但是这个数组格式有一定要求,在狗的类.h里面遵守 RLM_ARRAY_TYPE(Dog),一般在安装了插件后在创建model的时候就会自动遵守这个协议


image.png

在学生里面定义狗数组,如下

@property RLMArray<Dog *><Dog> *dogs;


在使用的时候,给小明两只狗


image.png


Student *student = [[Student alloc]init];
student.studentId = 1;
student.studentName = @"小明";
Dog *dog1 = [[Dog alloc]init];
dog1.dogId = 1;
dog1.dogName = @"旺财";
Dog *dog2 = [[Dog alloc]init];
dog2.dogId = 2;
dog2.dogName = @"阿福";
[student.dogs addObject:dog1];
[student.dogs addObject:dog2];
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
    [realm addObject:student];
}];


  • (3)、反向关系:关系是单向的。就拿我们的两个类Student,并Dog作为一个例子。如果Student.dogs链接到Dog实例,则可以按照链接从Student a到a Dog,但是无法从a Dog到其Student对象。您可以设置Dog.owner链接到的一对一属性Student,但这些链接彼此独立。添加一个Dog to Student.dogs不会将该狗的Dog.owner属性设置为正确Student。为解决此问题,Realm提供链接对象属性以表示反向关系。


@interface Dog : RLMObject
/** 狗的唯一ID */
@property int dogId;
/** 狗的名字 */
@property NSString *dogName;
@property (readonly) RLMLinkingObjects *owners;
@end
@implementation Dog
/** 设置主键 */
+(NSString *)primaryKey{
   return @"dogId";
}
+ (NSDictionary *)linkingObjectsProperties {
      return @{@"owners":[RLMPropertyDescriptor descriptorWithClass:NSClassFromString(@"Student") propertyName:@"dogs"]};
}
@end


提示:

  • @property (readonly) RLMLinkingObjects *owners; 中的 readonly 是忽略的意思,不会被写入数据库
  • 上面 linkingObjectsProperties 方法里面第一个参数是类对象,第二个是相应类里面的属性,通过链接对象属性,您可以从特定属性获取链接到给定对象的所有对象。一个Dog对象可以有一个名为属性owners包含所有的Student有这个确切的对象Dog在他们的对象dogs属性。创建owners类型的属性,RLMLinkingObjects然后覆盖+[RLMObject linkingObjectsProperties]以指示owners与Student模型对象的关系。


  • 在使用的时候,如下,通过 Dog 查询其主人的信息


image.png


RLMResults *results = [Student allObjects];
Student *student = results.firstObject;
Dog *dog = student.dogs.firstObject;
NSLog(@"owners=%@",dog.owners);
  • 3.7、Realm消息机制:可空属性&默认值&忽略属性
  • (1)、可空属性:默认情况下, 属性值可空, 如果强制要求某个属性非空, 可以使用如下方法,比如,我们要num属性必须有值,可以在像一个的model的.m 如下设置,在保存模型的时候,如果不赋值会报错的


+(NSArray<NSString *> *)requiredProperties{
     return @[@"num"];
}
  • (2)、默认值:我们在给一些属性赋值的时候,如果不赋值,我们想有一个默认的值,可以在相应的model的.m里面的下面方法设置对应属性的值,如下


+ (NSDictionary *)defaultPropertyValues
{
   return @{@"catName":@"小花"};
}
  • (3)、忽略属性
  • 忽略属性一:使用 readonly 来忽略这个属性,也就是不用写入数据库,如下


/** 猫的年龄 */
@property(readonly) NSInteger catAge;
  • 忽略属性二:在对应model 的 .m里面设置,如下


+ (NSArray *)ignoredProperties
{
    return @[@"catAge"];
}


  • 3.8、通知


image.png

  • (1)、通知概念
  • 可以注册侦听器以接收有关Realm或其实体的更改的通知。当Realm作为一个整体被更改时发送领域通知 ; 更改,添加或删除单个对象时会发送收集通知
  • 只要对返回的通知令牌进行引用,就会传递通知。您应该在注册更新的类上保留对此标记的强引用,因为在取消分配通知令牌时会自动取消注册通知。
  • 通知始终在最初注册的线程上提供。该线程必须具有当前运行的运行循环。如果您希望在主线程以外的线程上注册通知,则您负责在该线程上配置和启动运行循环(如果尚不存在)。
  • 在提交每个相关的写事务之后异步调用通知处理程序,无论写事务发生在哪个线程或进程上。
  • 如果在启动写入事务时将Realm提升到最新版本,则可能会同步调用通知处理程序。如果在Realm进入最新版本时,将以触发通知的方式修改或删除正在观察的Realm实体,则会发生这种情况。此类通知将在当前写入事务的上下文中运行,这意味着尝试在通知处理程序中开始写入事务将导致Realm抛出异常。如果您的应用程序的架构设置可能会出现这种情况,您可以使用它-[RLMRealm isInWriteTransaction]来确定您是否已经在写入事务中。
  • 由于使用运行循环传递通知,因此运行循环上的其他活动可能会延迟通知的传递。当无法立即传递通知时,多个写入事务的更改可能会合并为单个通知。


  • (2)、领域通知:通知处理程序可以在整个Realm上注册。每次提交涉及该Realm的写入事务时,无论写入事务发生在哪个线程或进程上,都将触发通知处理程序:


  • 举例:
    创建一个强引用通知对象


@property (nonatomic, strong) RLMNotificationToken *token;
  • 创建通知


RLMRealm *realm = [RLMRealm defaultRealm];
// 创建通知
self.token = [realm addNotificationBlock:^(RLMNotification  _Nonnull notification, RLMRealm * _Nonnull realm) {
     NSLog(@"监听到修改通知");
}];
  • 添加数据测试


NotificationModel *model = [[NotificationModel alloc]init];
model.number = 1;
model.name = @"测试1";
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
      [realm addObject:model];
}];
  • 销毁通知


[self.token invalidate];


  • (3)、领域通知:
  • 收集通知不会收到整个Realm,而是收到细粒度的更改说明。它们包括自上次通知以来已添加,删除或修改的对象索引。收集通知是异步传递的,首先是初始结果,然后是每次写入事务后再次发送,这会改变集合中的任何对象(或添加新对象)。
  • 可以通过RLMCollectionChange传递给通知块的参数访问这些更改。这个对象保存有关受索引信息deletionsinsertionsmodifications
  • 前两个,删除插入,在对象开始和停止成为集合的一部分时记录索引。这会将对象添加到Realm或从Realm中删除它们时考虑在内。为此,RLMResults当您筛选特定值并更改对象以使其现在与查询匹配或不再匹配时也适用。对于基于RLMArrayRLMLinkingObjects包括派生的集合,RLMResults当在关系中添加或删除对象时,这也适用。
  • 只要集合中对象的属性发生更改,您就会收到有关修改的通知。这也发生更改的一对一一对多的关系,虽然通知不会采取反向关系考虑在内。
  • 举例1:
    创建一个强引用通知对象


@property (nonatomic, strong) RLMNotificationToken *token;
  • 创建通知


RLMResults *results = [NotificationModel allObjects];
self.token2 = [results addNotificationBlock:^(RLMResults * _Nullable results, RLMCollectionChange * _Nullable change, NSError * _Nullable error) {
     NSLog(@"%@--%@--%@", results, change, error); 
}];

提示:results并不一定是全部的数据,你可以通过查询去获取一些数据,当这个结果集的数据发生变化就会走这个通知


  • 销毁通知


[self.token invalidate];
  • 添加数据测试


NotificationModel *model = [[NotificationModel alloc]init];
model.number = 2;
model.name = @"测试2";
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
      [realm addObject:model];
}];
[realm transactionWithBlock:^{
     [realm deleteObject:model];
}];
NotificationModel *model2 = [[NotificationModel alloc]init];
model2.number = 3;
model2.name = @"测试3";
[realm transactionWithBlock:^{
     [realm addObject:model2];
}];
  • 举例2:我们可以看如果在tableview上面的数据变化时候的代码


- (void)viewDidLoad {
   [super viewDidLoad];
   // Observe RLMResults Notifications
   __weak typeof(self) weakSelf = self;
   self.notificationToken = [[Person objectsWhere:@"age > 5"] addNotificationBlock:^(RLMResults<Person *> *results, RLMCollectionChange *changes, NSError *error) {
       if (error) {
            NSLog(@"Failed to open Realm on background worker: %@", error);
            return;
       }
       UITableView *tableView = weakSelf.tableView;
       // Initial run of the query will pass nil for the change information
       if (!changes) {
            [tableView reloadData];
            return;
       }
       // Query results have changed, so apply them to the UITableView
       [tableView beginUpdates];
       [tableView deleteRowsAtIndexPaths:[changes deletionsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic];
       [tableView insertRowsAtIndexPaths:[changes insertionsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic];
       [tableView reloadRowsAtIndexPaths:[changes modificationsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic];
       [tableView endUpdates];
    }];
}
- (void)dealloc {
    [self.notificationToken invalidate];
}


  • (4)、对象通知
  • Realm支持对象级通知。您可以在特定Realm对象上注册通知,以便在删除对象时或在对象上的任何托管属性修改其值时收到通知。(这也适用于将其值设置为其现有值的托管属性。)
  • 只有Realm管理的对象可能在其上注册了通知处理程序。
  • 对于在不同线程或不同进程中执行的写入事务,当管理对象的Realm(自动)刷新到包含更改的版本时,将调用该块,而对于本地写入事务,它将在某个时刻被调用。写入事务提交后的未来。
  • 通知处理程序有三个参数。第一个参数deleted是BOOL指示对象是否已删除。如果是这样YES,其他参数将为nil,并且永远不会再次调用该块。
  • 第二个参数,changes是一个NSArray的RLMPropertyChange对象。这些对象中的每一个都包含已更改的属性的名称(作为字符串),前一个值和当前值。
  • 第三个论点是NSError。如果发生涉及对象的错误,NSError则将包含有关发生的事件的信息,changes将为nil,deleted将是NO,并且将永远不再调用该块。


@interface RLMStepCounter : RLMObject
@property NSInteger steps;
@end
@implementation RLMStepCounter
@end
RLMStepCounter *counter = [[RLMStepCounter alloc] init];
counter.steps = 0;
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
[realm addObject:counter];
[realm commitWriteTransaction];
__block RLMNotificationToken *token = [counter addNotificationBlock:^(BOOL deleted,NSArray<RLMPropertyChange *> *changes,NSError *error) {
     if (deleted) {
         NSLog(@"The object was deleted.");
     } else if (error) {
         NSLog(@"An error occurred: %@", error);
     } else {
         for (RLMPropertyChange *property in changes) {
              if ([property.name isEqualToString:@"steps"] && [property.value integerValue] > 1000) {
                    NSLog(@"Congratulations, you've exceeded 1000 steps.");
                    [token invalidate];
                    token = nil;
              }
         }
      }
}];
  • 3.9、Realm数据库操作 (不同的用户, 使用不同的数据库文件)
  • (1)、不同的用户, 创建不同的数据库(用户在登陆后,我们就进行设置相应的数据库,databaseName 是数据库的名字,以 .realm 结尾)


+(void)setDefaultRealmForUserDatabaseName:(NSString *)databaseName{
     RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
     // 使用默认的目录,但是使用用户名来替换默认的文件名
     config.fileURL = [[[config.fileURL URLByDeletingLastPathComponent] URLByAppendingPathComponent: databaseName] URLByAppendingPathExtension:@"realm"];
     // 将这个配置应用到默认的 Realm 数据库当中
     [RLMRealmConfiguration setDefaultConfiguration:config]; 
}


  • 使用举例,我们以 DataBaseModel为例


image.png


//  数据库的配置
[RLMRealmDataBaseTools setDefaultRealmForUserName:@"王二"]; 
// 创建数据model
DataBaseModel *model = [DataBaseModel new];
model.number = 1;
// 获取对应数据库的 RLMRealm
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
    // 写入数据
    [realm addObject:model];
}];
  • (2)、只读数据库的设置


+(void)setDefaultRealmReadOnly{
   RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
   // 以只读模式打开文件,因为应用数据包并不可写
   config.readOnly = YES;
   [RLMRealmConfiguration setDefaultConfiguration:config];
}
  • (3)、删除指定用户的数据库


+(void)deleteDatabaseName:(NSString *)databaseName{
     [self setDefaultRealmForUserDatabaseName:databaseName];
     NSFileManager *manager = [NSFileManager defaultManager];
     RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
     NSArray<NSURL *> *realmFileURLs = @[
                             config.fileURL,
                             [config.fileURL URLByAppendingPathExtension:@"lock"],
                             [config.fileURL URLByAppendingPathExtension:@"management"],
                             ];
     for (NSURL *URL in realmFileURLs) {
          NSError *error = nil;
          [manager removeItemAtURL:URL error:&error];
          if (error) {
              // 处理错误
          }
     }  
}
  • 3.10、Realm 数据库迁移(适用于修改了数据模型的情况)
  • (1)、适用于修改了数据模型的情况:比如在一个模型里面我们删除了一个属性或者添加了一个属性的情况等等,结构发生了变化
  • (2)、数据结构迁移 (增加或者减少属性):在 [AppDelegate didFinishLaunchingWithOptions:] 中进行配置



+(void)databaseMigration:(int)newVersion{
   // 1. 获取默认配置
   RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
   // 2. 叠加版本号(要比上一次的版本号高) 0
   config.schemaVersion = newVersion;
   // 3. 设置闭包,这个闭包将会在打开低于上面所设置版本号的 Realm 数据库的时候被自动调用
   [config setMigrationBlock:^(RLMMigration *migration, uint64_t oldSchemaVersion) {
         // 什么都不要做!Realm 会自行检测新增和需要移除的属性,然后自动更新硬盘上的数据库架构
         if (oldSchemaVersion < newVersion) {
         }
   }];
   // 4. 让配置生效(告诉 Realm 为默认的 Realm 数据库使用这个新的配置对象)
   [RLMRealmConfiguration setDefaultConfiguration:config];
   // 5. 我们已经告诉了 Realm 如何处理架构的变化,打开文件之后将会自动执行迁移,数据结构会发生变化
   [RLMRealm defaultRealm];    
}
  • (3)、数据迁移(比如说想要把某些数据用其他的数据字段表示,我们可以在上面代码的block做操作)


if (oldSchemaVersion < newVersion) {
     NSLog(@"需要做迁移动作");
     //执行更名动作
     // [migration renamePropertyForClass:@"Migration" oldName:@"fullName" newName:@"fullName2"];
     // 无需做任何事情, 就可以完成, 数据结构, 以及数据的迁移
     [migration enumerateObjects:@"模型的名字" block:^(RLMObject * _Nullable oldObject, RLMObject * _Nullable newObject) {
         newObject[@"新的字段名字"] = [NSString stringWithFormat:@"%@", oldObject[@"旧的字段名字"]];
       }];
}

提示:这里可以把多个字段拼接为一个字段,如:newObject[@"新的字段名字"] = [NSString stringWithFormat:@"%@", oldObject[@"旧的字段名字1"], oldObject[@"旧的字段名字2"]];


  • (4)、属性重命名(上面代码的block做操作)


if (oldSchemaVersion < newVersion) {
    NSLog(@"需要做迁移动作");
    //执行更名动作
    [migration renamePropertyForClass:@"模型名字" oldName:@"旧字段名" newName:@"模型里新字段名"];
}


  • (5)、多版本增量式迁移(线性迁移)
  • 假如说,我们的应用有两个用户:JP和Tim,JP经常更新应用,但Tim却经常跳过某些版本。所以JP可能下载过这个应用的每一个版本,并且一步一步地跟着更新构架:第一次下载更新后,数据库架构从v0更新到v1;第二次架构从v1更新到v2 ...以此类推,井然有序。相反,Tim很有可能直接从v0版本直接跳到了v2版本。因此,应该您使用非嵌套的 if (oldSchemaVersion < X)结构来构造您的数据库迁移模块,以确保无论用户在使用哪个版本的架构,都能完成必需的更新。
  • 当你的用户不按套路出牌,跳过有些更新版本的时候,另一种情况也会发生。假如您在v2里删掉了一个“email”属性,然后在v3里又把它重新引进了。假如有个用户从v1直接跳到v3,那Realm不会自动检测到v2的这个删除操作,因为存储的数据架构和代码中的架构吻合。这会导致Tim的人对象有一个v3的电子邮件属性,但里面的内容却是v1的。这个看起来没什么大问题,但是假如两者的内部存储类型不同(比如说:从ISO email标准格式变成了自定义格式),那麻烦就大了。为了避免这种不必要的麻烦,我们推荐您在if (oldSchemaVersion < 3)语句中,清空所有的电子邮件属性。
  • 举例如下:核心代码
    enumerateObjects:block: 遍历了存储在 Realm 文件中的每一个“Student”对象



[migration enumerateObjects:Person.className
           block:^(RLMObject *oldObject, RLMObject *newObject) {
       // 只有当 Realm 数据库的架构版本为 0 的时候,才添加 “studentName” 属性
       if (oldSchemaVersion < 1) {
            newObject[@"studentName"] = @"";
       }
       // 只有当 Realm 数据库的架构版本为 0 或者 1 的时候,才添加“email”属性
       if (oldSchemaVersion < 2) {
             newObject[@"email"] = @"";
      }
      // 只有当 Realm 数据库的架构版本为 2 的时候,去掉“email”属性
       if (oldSchemaVersion < 3) {
             // 在后面执行 [RLMRealm defaultRealm];  后会自动更新模型结构
      }
}];


最后:说一下 Realm 使用的话对项目会植入很深,因为它创建的model 都是继承于RLMObject,相应模型里面的数组也是 RLMArray 类型,我们可以看出如果在项目去掉Realm是一件很麻烦的事情,但是它的功能是很强大的;前面面我们所学的 iOS Sqlite数据库的使用 对项目的植入很小,功能不是很强大,基本上够用;具体用什么自己决定了

目录
相关文章
|
3月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
349 4
|
6月前
|
测试技术 Linux 虚拟化
iOS自动化测试方案(五):保姆级VMware虚拟机安装MacOS
详细的VMware虚拟机安装macOS Big Sur的保姆级教程,包括下载VMware和macOS镜像、图解安装步骤和遇到问题时的解决方案,旨在帮助读者顺利搭建macOS虚拟机环境。
281 3
iOS自动化测试方案(五):保姆级VMware虚拟机安装MacOS
|
6月前
|
测试技术 数据安全/隐私保护 iOS开发
iOS自动化测试方案(四):保姆级搭建iOS自动化开发环境
iOS自动化测试方案的第四部分,涵盖了基础环境准备、iPhone虚拟机设置、MacOS虚拟机与iPhone真机的连接,以及扩展问题和代码示例,确保读者能够顺利完成环境搭建并进行iOS自动化测试。
675 0
iOS自动化测试方案(四):保姆级搭建iOS自动化开发环境
|
6月前
|
测试技术 开发工具 iOS开发
iOS自动化测试方案(三):WDA+iOS自动化测试解决方案
这篇文章是iOS自动化测试方案的第三部分,介绍了在没有MacOS系统条件下,如何使用WDA(WebDriverAgent)结合Python客户端库facebook-wda和tidevice工具,在Windows系统上实现iOS应用的自动化测试,包括环境准备、问题解决和扩展应用的详细步骤。
590 1
iOS自动化测试方案(三):WDA+iOS自动化测试解决方案
|
6月前
|
测试技术 虚拟化 iOS开发
iOS自动化测试方案(二):Xcode开发者工具构建WDA应用到iphone
这篇文章是iOS自动化测试方案的第二部分,详细介绍了在Xcode开发者工具中构建WebDriverAgent(WDA)应用到iPhone的全过程,包括环境准备、解决构建过程中可能遇到的错误,以及最终成功安装WDA到设备的方法。
365 0
iOS自动化测试方案(二):Xcode开发者工具构建WDA应用到iphone
|
6月前
|
测试技术 开发工具 虚拟化
iOS自动化测试方案(一):MacOS虚拟机保姆级安装Xcode教程
这篇文章提供了一份保姆级的教程,指导如何在MacOS虚拟机上安装Xcode,包括环境准备、基础软件安装以及USB扩展插件的使用,以实现iOS自动化测试方案的第一步。
366 0
iOS自动化测试方案(一):MacOS虚拟机保姆级安装Xcode教程
|
6月前
|
iOS开发 开发者
iOS平台RTMP|RTSP播放器如何实时回调YUV数据
我们在做RTMP、RTSP播放器的时候,有开发者需要自己处理拉取到的YUV数据,做二次分析之用,为此,我们做了以下的设计:InitPlayer之后,再调用SmartPlayerStart()接口之前,设置yuv数据回调即可。
|
9月前
|
Java iOS开发
iOS的数据序列化(又称持久化)的两类使用方式
iOS的数据序列化(又称持久化)的两类使用方式
85 0
|
9月前
|
Java 开发工具 Android开发
SLS:使用 OTel 官方 SDK 采集 Android、iOS Trace 数据实践
本文介绍了使用 OTel 官方 SDK 采集 Android、iOS Trace 数据实践。
594 7
SLS:使用 OTel 官方 SDK 采集 Android、iOS Trace 数据实践
|
9月前
|
移动开发 小程序 API
uniapp通过蓝牙传输数据 (ios)
uniapp通过蓝牙传输数据 (ios)
426 1

热门文章

最新文章

  • 1
    Cellebrite UFED 4PC 7.71 (Windows) - Android 和 iOS 移动设备取证软件
    24
  • 2
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    33
  • 3
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    28
  • 4
    【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
    23
  • 5
    uniapp开发ios打包Error code = -5000 Error message: Error: certificate file(p12) import failed!报错问题如何解决
    143
  • 6
    【05】2025年1月首发完整版-篇幅较长-苹果app如何上架到app store完整流程·不借助第三方上架工具的情况下无需花钱但需仔细学习-优雅草央千澈详解关于APP签名以及分发-们最关心的一篇来了-IOS上架app
    233
  • 7
    app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
    90
  • 8
    深入探索iOS开发中的SwiftUI框架
    143
  • 9
    ios样式开关按钮jQuery插件
    58
  • 10
    Android与iOS生态差异深度剖析:技术架构、开发体验与市场影响####
    75