一、代码下载

代码下载地址

二、实例效果展示



imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="效果图二.png" title="">

imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="效果图三.png" title="">

三、实例项目简单介绍

这个实例主要是封装sqlite3数据库工具,并用这个工具实现记录、删除、改动、查询学生信息的功能。另外一个附属的功能就是依据学生的姓名首字母分组并加入索引。

对于数据库。我做了两层封装。

第一层是对数据库的基本操作进行抽象。第二层是对学生数据的操作进行抽象。这样的分层的思想不仅能使处于底层的功能得到多次重用,调高效率。并且这样做逻辑清晰,易于定位问题,故意于维护与跟新。

四、实例项目分析

1、首先抽象出数据库基本操作工具类,而数据库的基本操作有:打开、关闭、建表、增、删、改、查……基于这些操作,我封装出例如以下五个方法来提供数据库的操作:

  1. * 打开数据库
  2. *
  3. * @param path 数据库路径
  4. * @param success 打开成功的回调
  5. * @param falure 打开失败的回调
  6. */
  7. + (void)openDBPath:(NSString *)path succesefulBlock:(void (^)(sqlite3 *db))success andFailureBlock:(void (^)(NSString *msg))failure
  8. {
  9. sqlite3 *database = NULL;
  10. int result = sqlite3_open(path.UTF8String, &database);
  11. if (result == SQLITE_OK) {
  12. if (success) {
  13. success(database);
  14. }
  15. }
  16. else
  17. {
  18. if (failure) {
  19. const char *msg = sqlite3_errmsg(database);
  20. failure([NSString stringWithUTF8String:msg]);
  21. }
  22. if (database) {
  23. [self closeDB:database succesefulBlock:nil andFailureBlock:nil];
  24. }
  25. }
  26. }
  27. /**
  28. * 关闭数据库
  29. *
  30. * @param database 数据库链接
  31. * @param succese 成功的回调
  32. * @param failure 失败的回调
  33. */
  34. + (void)closeDB:(sqlite3 *)database succesefulBlock:(void (^)())succese andFailureBlock:(void (^)(NSString *msg))failure
  35. {
  36. int result = sqlite3_close(database);
  37. if (result == SQLITE_OK) {
  38. if (succese) {
  39. succese();
  40. }
  41. }
  42. else
  43. {
  44. if (failure) {
  45. failure([NSString stringWithUTF8String:sqlite3_errmsg(database)]);
  46. }
  47. }
  48. }
  49. /**
  50. * 运行SQL语句(不适应查询语句和blob(NSData)二进制数据类型的操作)
  51. *
  52. * @param sqStr SQL语句
  53. * @param database 数据库链接
  54. * @param succese 成功的回调
  55. * @param failure 失败的回调
  56. */
  57. + (void)executeSql:(NSString *)sqStr toDatabase:(sqlite3 *)database succesefulBlock:(void (^)())succese andFailureBlock:(void (^)(NSString *msg))failure
  58. {
  59. DebugLog(@"%@", sqStr);
  60. char *msg = NULL;
  61. int result = sqlite3_exec(database, sqStr.UTF8String, NULL, NULL, &msg);
  62. if (result == SQLITE_OK) {
  63. if (succese) {
  64. succese();
  65. }
  66. }
  67. else
  68. {
  69. if (failure) {
  70. failure([NSString stringWithUTF8String:msg]);
  71. }
  72. }
  73. }
  74. /**
  75. * 准备须要sqlite3_stmt结果集的SQL语句
  76. *
  77. * @param sqStr SQL语句
  78. * @param database 数据库连接
  79. * @param succese 成功的回调
  80. * @param failure 失败的回调
  81. */
  82. + (void)prepareSql:(NSString *)sqStr fromDatabase:(sqlite3 *)database succesefulBlock:(void (^)(sqlite3_stmt *stmt))succese andFailureBlock:(void (^)(NSString *msg))failure
  83. {
  84. DebugLog(@"%@", sqStr);
  85. sqlite3_stmt *stmt = NULL;
  86. int result = sqlite3_prepare_v2(database, sqStr.UTF8String, -1, &stmt, NULL);
  87. if (result == SQLITE_OK) {
  88. if (succese) {
  89. succese(stmt);
  90. }
  91. }
  92. else
  93. {
  94. if (failure) {
  95. failure(@"SQL语句是非法的。");
  96. }
  97. }
  98. }

2、接着就是抽象学生数据功能类了。依据须要我抽象出了例如以下五个方法

  1. /**
  2. * 获取全部学生
  3. *
  4. * @param callBack 回调
  5. */
  6. + (void)getAllStudents:(void (^)(NSArray *students, NSString *msg))callBack;
  7. /**
  8. * 加入学生
  9. *
  10. * @param studentModel 学生模型
  11. * @param succese 加入成功回调
  12. * @param failure 加入失败回调
  13. */
  14. + (void)addStudent:(StudentModel *)studentModel succesefulBlock:(void (^)(StudentModel *studentModel))succese andFailureBlock:(void (^)(NSString *msg))failure;
  15. /**
  16. * 更新学生
  17. *
  18. * @param studentModel 学生模型
  19. * @param succese 更新成功回调
  20. * @param failure 更新失败回调
  21. */
  22. + (void)updateStudent:(StudentModel *)studentModel succesefulBlock:(void (^)(StudentModel *studentModel))succese andFailureBlock:(void (^)(NSString *msg))failure;
  23. /**
  24. * 按条件搜索学生
  25. *
  26. * @param condition 条件
  27. * @param callBack 搜索回调
  28. */
  29. + (void)searchStudents:(NSString *)condition andCallBack:(void (^)(NSArray *students, NSString *msg))callBack;
  30. /**
  31. * 删除学生
  32. *
  33. * @param studentModel 学生模型
  34. * @param succese 删除成功回调
  35. * @param failure 删除失败回调
  36. */
  37. + (void)deleteStudent:(StudentModel *)studentModel succesefulBlock:(void (^)(StudentModel *studentModel))succese andFailureBlock:(void (^)(NSString *msg))failure;

细节分析

- 因为该功能类都是使用类方法,所以不能以实例变量来存储数据,因此声明全局变量static sqlite3 *database来存储数据库的链接。并以static关键字修饰来避免其它源文件对其訪问。

- 类方法initialize是在手动调用累中不论什么方法前调用一次,所以在这种方法中打开数据库并创建学生表是很合适的。

  1. /**
  2. * 在手动调用类里的不论什么方法前自己主动调用一次
  3. */
  4. + (void)initialize
  5. {
  6. [SqliteDBAccess openDBPath:SqlitePathStr succesefulBlock:^(sqlite3 *db) {
  7. DebugLog(@"数据库打开成功。");
  8. [SqliteDBAccess executeSql:[NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@ (identifier integer PRIMARY KEY AUTOINCREMENT, name text NOT NULL, studentNumber text NOT NULL UNIQUE, photo blob, age integer NOT NULL, address text, describe text);", StudentTableName] toDatabase:db succesefulBlock:^{
  9. database = db;
  10. DebugLog(@"学生表创建成功!");
  11. } andFailureBlock:^(NSString *msg) {
  12. DebugLog(@"学生表创建失败。%@", msg);
  13. }];
  14. } andFailureBlock:^(NSString *msg) {
  15. DebugLog(@"数据库打开失败,%@", msg);
  16. }];
  17. }
  • 在学生数据功能类的内部能够封装一个不公开的方法来从sqlite3_stmt结果集中获取学生模型对象
  1. /**
  2. * 从sqlite3_stmt中获取学生数据
  3. *
  4. * @param stmt sqlite3_stmt结果集
  5. *
  6. * @return 学生数组
  7. */
  8. + (NSArray *)getStudentsFromStatement:(sqlite3_stmt *)stmt
  9. {
  10. NSMutableArray *students = [NSMutableArray arrayWithCapacity:1];
  11. StudentModel *studentModel;
  12. while (sqlite3_step(stmt) == SQLITE_ROW) {
  13. studentModel = [[StudentModel alloc] init];
  14. studentModel.identifier = sqlite3_column_int(stmt, 0);
  15. char *name = (char *)sqlite3_column_text(stmt, 1);
  16. char *studentNumber = (char *)sqlite3_column_text(stmt, 2);
  17. void *photo = (void *)sqlite3_column_blob(stmt, 3);
  18. studentModel.name = name ? [NSString stringWithUTF8String:name] : nil;
  19. studentModel.studentNumber = studentNumber ?
  20. [NSString stringWithUTF8String:studentNumber] : nil;
  21. studentModel.photo = photo ? [UIImage imageWithData:[NSData dataWithBytes:photo length:sqlite3_column_bytes(stmt, 3)]] : nil;
  22. studentModel.age = sqlite3_column_int(stmt, 4);
  23. char *address = (char *)sqlite3_column_text(stmt, 5);
  24. char *describe = (char *)sqlite3_column_text(stmt, 6);
  25. studentModel.address = address ?
  26. [NSString stringWithUTF8String:address] : nil;
  27. studentModel.describe = describe ? [NSString stringWithUTF8String:describe] : nil;
  28. [students addObject:studentModel];
  29. }
  30. return students;
  31. }
  • 因为表中存在二进制数据,所以插入跟新二进制数据时使用sqlite3_exec函数运行SQL语句是有问题的,须要使用sqlite3_step函数来运行SQL语句。

  1. /**
  2. * 更新学生
  3. *
  4. * @param studentModel 学生模型
  5. * @param succese 更新成功回调
  6. * @param failure 更新失败回调
  7. */
  8. + (void)updateStudent:(StudentModel *)studentModel succesefulBlock:(void (^)(StudentModel *studentModel))succese andFailureBlock:(void (^)(NSString *msg))failure
  9. {
  10. [SqliteDBAccess prepareSql:[NSString stringWithFormat:@"UPDATE %@ SET name = ?, studentNumber = ?, photo = ?, age = ?, address = ?, describe = ? WHERE identifier = ?;", StudentTableName] fromDatabase:database succesefulBlock:^(sqlite3_stmt *stmt) {
  11. NSData *data = UIImagePNGRepresentation(studentModel.photo);
  12. sqlite3_bind_text(stmt, 1, studentModel.name.UTF8String, -1, NULL);
  13. sqlite3_bind_text(stmt, 2, studentModel.studentNumber.UTF8String, -1, NULL);
  14. sqlite3_bind_blob(stmt, 3, [data bytes], (int)[data length], NULL);
  15. sqlite3_bind_int(stmt, 4, studentModel.age);
  16. sqlite3_bind_text(stmt, 5, studentModel.address.UTF8String, -1, NULL);
  17. sqlite3_bind_text(stmt, 6, studentModel.describe.UTF8String, -1, NULL);
  18. sqlite3_bind_int(stmt, 7, studentModel.identifier);
  19. //运行完毕
  20. if (sqlite3_step(stmt) == SQLITE_DONE) {
  21. if (succese) {
  22. succese(studentModel);
  23. }
  24. }
  25. else
  26. {
  27. if (failure) {
  28. failure([NSString stringWithFormat:@"更新学生失败。%@", [NSString stringWithUTF8String:sqlite3_errmsg(database)]]);
  29. }
  30. }
  31. //在遍历完结果集后,调用sqlite3_finalize以释放和预编译的语句相关的资源。
  32. sqlite3_finalize(stmt);
  33. } andFailureBlock:^(NSString *msg) {
  34. if (failure) {
  35. failure([NSString stringWithFormat:@"更新学生失败。%@", msg]);
  36. }
  37. }];
  38. }

五、问题与补充

1.sqlite3事务:是并发控制的基本单位。所谓的事务。它是一个操作序列,这些操作要么都运行。要么都不运行,它是一个不可切割的工作单位。比如,银行转账工作:从一个账号扣款并使还有一个账号增款,这两个操作要么都运行,要么都不运行。所以,应该把它们看成一个事务。事务是数据库维护数据一致性的单位,在每一个事务结束时,都能保持数据一致性。 针对上面的描写叙述能够看出,事务的提出主要是为了解决并发情况下保持数据一致性的问题。

事务的语句:

- 開始事物:BEGIN TRANSACTION

- 提交事物:COMMIT TRANSACTION

- 回滚事务:ROLLBACK TRANSACTION

项目中加入学生数据的时候,并没有设置identifer主键。可是主键是实用的,在插入成功后须要查出来,因此加入学生就涉及两步操作:插入数据,查询数据。并且这两个操作必须要同一时候成功才干算是学生加入成功。所以在这里,我就开启一个事务。先插入学生数据,再查询此学生数据,仅仅要有一个步骤错误发生就回滚事务,全部成功则提交事务:

  1. /**
  2. * 加入学生
  3. *
  4. * @param studentModel 学生模型
  5. * @param succese 加入成功回调
  6. * @param failure 加入失败回调
  7. */
  8. + (void)addStudent:(StudentModel *)studentModel succesefulBlock:(void (^)(StudentModel *studentModel))succese andFailureBlock:(void (^)(NSString *msg))failure
  9. {
  10. //开启事务
  11. [SqliteDBAccess executeSql:@"BEGIN TRANSACTION" toDatabase:database succesefulBlock:^{
  12. DebugLog(@"事务启动成功。");
  13. [SqliteDBAccess prepareSql:[NSString stringWithFormat:@"INSERT INTO %@ (name, studentNumber, photo, age, address, describe) VALUES(?, ?
  14. , ?
  15. , ?, ?, ?);", StudentTableName] fromDatabase:database succesefulBlock:^(sqlite3_stmt *stmt) {
  16. NSData *data = UIImagePNGRepresentation(studentModel.photo);
  17. sqlite3_bind_text(stmt, 1, studentModel.name.UTF8String, -1, NULL);
  18. sqlite3_bind_text(stmt, 2, studentModel.studentNumber.UTF8String, -1, NULL);
  19. sqlite3_bind_blob(stmt, 3, [data bytes], (int)[data length], NULL);
  20. sqlite3_bind_int(stmt, 4, studentModel.age);
  21. sqlite3_bind_text(stmt, 5, studentModel.address.UTF8String, -1, NULL);
  22. sqlite3_bind_text(stmt, 6, studentModel.describe.UTF8String, -1, NULL);
  23. if (sqlite3_step(stmt) == SQLITE_DONE) {
  24. [SqliteDBAccess prepareSql:[NSString stringWithFormat:@"SELECT * FROM %@ WHERE studentNumber = ?
  25. ;", StudentTableName] fromDatabase:database succesefulBlock:^(sqlite3_stmt *stmt) {
  26. sqlite3_bind_text(stmt, 1, studentModel.studentNumber.UTF8String, -1, NULL);
  27. StudentModel *model = [[self getStudentsFromStatement:stmt] firstObject];
  28. if (studentModel) {
  29. studentModel.identifier = model.identifier;
  30. if (succese) {
  31. succese(studentModel);
  32. }
  33. //提交事务
  34. [SqliteDBAccess executeSql:@"COMMIT TRANSACTION" toDatabase:database succesefulBlock:^{
  35. DebugLog(@"提交事务成功!");
  36. } andFailureBlock:^(NSString *msg) {
  37. DebugLog(@"提交事务失败:%@", msg);
  38. }];
  39. }
  40. else
  41. {
  42. //回滚事务
  43. [SqliteDBAccess executeSql:@"ROLLBACK TRANSACTION" toDatabase:database succesefulBlock:^{
  44. DebugLog(@"回滚成功。");
  45. } andFailureBlock:^(NSString *msg) {
  46. DebugLog(@"回滚失败:%@", msg);
  47. }];
  48. }
  49. //在遍历完结果集后,调用sqlite3_finalize以释放和预编译的语句相关的资源。
  50. sqlite3_finalize(stmt);
  51. } andFailureBlock:^(NSString *msg) {
  52. if (failure) {
  53. failure(msg);
  54. //回滚事务
  55. [SqliteDBAccess executeSql:@"ROLLBACK TRANSACTION" toDatabase:database succesefulBlock:^{
  56. DebugLog(@"回滚成功。");
  57. } andFailureBlock:^(NSString *msg) {
  58. DebugLog(@"回滚失败:%@", msg);
  59. }];
  60. }
  61. }];
  62. }
  63. else
  64. {
  65. if (failure) {
  66. failure([NSString stringWithFormat:@"加入学生失败,%@", [NSString stringWithUTF8String:sqlite3_errmsg(database)]]);
  67. }
  68. }
  69. //在遍历完结果集后,调用sqlite3_finalize以释放和预编译的语句相关的资源。
  70. sqlite3_finalize(stmt);
  71. } andFailureBlock:^(NSString *msg) {
  72. if (failure) {
  73. failure([NSString stringWithFormat:@"加入学生失败。%@", msg]);
  74. }
  75. //回滚事务
  76. [SqliteDBAccess executeSql:@"ROLLBACK TRANSACTION" toDatabase:database succesefulBlock:^{
  77. DebugLog(@"回滚成功。");
  78. } andFailureBlock:^(NSString *msg) {
  79. DebugLog(@"回滚失败:%@", msg);
  80. }];
  81. }];
  82. } andFailureBlock:^(NSString *msg) {
  83. DebugLog(@"事务启动失败:%@", msg);
  84. }];
  85. }

2.sqlite3注入漏洞:

举例:登录功能

1.用户输入登录名和密码:userName = ‘zhangsan or 1 = 1 or ” = ”’ password = ‘123456’

select * from t_user where name = ‘zhangsan or 1 = 1 or ” = ”’ and password = ‘123456’;

如此条件永远成立

解决方式:參数化查询

用?替换掉须要查询的条件

select * from t_user where name = ?

and passsword = ?

;

检查查询语句合法后。再绑定參数

sqlite3_bind_text(stmt, 1, “张三”, NULL);

3.sqlite3模糊查询:确定给定的字符串是否与指定的模式匹配。模式能够包括常规字符和通配符字符。

模式匹配过程中,常规字符必须与字符串中指定的字符全然匹配。然而,可 使用字符串的随意片段匹配通配符。与使用 = 和 != 字符串比較运算符相比,使用通配符可使 LIKE 运算符更加灵活。

- %:包括零个或很多其它字符的随意字符串。WHERE title LIKE ‘%computer%’ 将查找处于书名任何位置的包括单词 computer 的全部书名。

- _(下划线):不论什么单个字符。

WHERE au_fname LIKE ‘_ean’ 将查找以 ean 结尾的全部 4 个字母的名字(Dean、Sean 等)。

- [ ]:指定范围 ([a-f]) 或集合 ([abcdef]) 中的不论什么单个字符。WHERE au_lname LIKE ‘[C-P]arsen’ 将查找以arsen 结尾且以介于 C 与 P 之间的不论什么单个字符開始的作者姓氏,比如,Carsen、Larsen、Karsen 等。

- [^]:不属于指定范围 ([a-f]) 或集合 ([abcdef]) 的不论什么单个字符。WHERE au_lname LIKE ‘de[^l]%’ 将查找以 de 開始且其后的字母不为 l 的全部作者的姓氏。

  1. /**
  2. * 按条件搜索学生
  3. *
  4. * @param condition 条件
  5. * @param callBack 搜索回调
  6. */
  7. + (void)searchStudents:(NSString *)condition andCallBack:(void (^)(NSArray *students, NSString *msg))callBack
  8. {
  9. int age = [condition intValue];
  10. NSString *selectStr;
  11. if (age != 0 || [condition isEqualToString:@"0"]) {
  12. selectStr = [NSString stringWithFormat:@"SELECT * FROM %@ WHERE name LIKE ?
  13. or studentNumber LIKE ? or age LIKE ?
  14. ;", StudentTableName];
  15. }
  16. else
  17. {
  18. selectStr = [NSString stringWithFormat:@"SELECT * FROM %@ WHERE name LIKE ?
  19. or studentNumber LIKE ?;", StudentTableName];
  20. }
  21. [SqliteDBAccess prepareSql:selectStr fromDatabase:database succesefulBlock:^(sqlite3_stmt *stmt) {
  22. sqlite3_bind_text(stmt, 1, [NSString stringWithFormat:@"%%%@%%", condition].UTF8String, -1, NULL);
  23. sqlite3_bind_text(stmt, 2, [NSString stringWithFormat:@"%%%@%%", condition].UTF8String, -1, NULL);
  24. if (age != 0 || [condition isEqualToString:@"0"]) {
  25. sqlite3_bind_int(stmt, 3, age);
  26. }
  27. if (callBack) {
  28. callBack([self getStudentsFromStatement:stmt], @"搜索成功!");
  29. }
  30. //在遍历完结果集后。调用sqlite3_finalize以释放和预编译的语句相关的资源。
  31. sqlite3_finalize(stmt);
  32. } andFailureBlock:^(NSString *msg) {
  33. if (callBack) {
  34. callBack(nil, msg);
  35. }
  36. }];
  37. }

3.UITableView索引:

- 对数据进行分组排序

  1. /**
  2. * 按首字母分组排序
  3. *
  4. * @param sourceArr 目标数组
  5. *
  6. * @return 分好组的数组
  7. */
  8. - (NSArray *)groupedByLetter:(NSArray *)sourceArr
  9. {
  10. UILocalizedIndexedCollation *collation = [UILocalizedIndexedCollation currentCollation];
  11. //依照索引创建相应的数组
  12. NSMutableArray *sectionsArr = [NSMutableArray arrayWithCapacity:collation.sectionTitles.count];
  13. for (int index = 0; index < collation.sectionTitles.count; index++) {
  14. NSMutableArray *arr = [NSMutableArray arrayWithCapacity:1];
  15. [sectionsArr addObject:arr];
  16. }
  17. //设置StudentModel模型的组号。并装进相应数组中
  18. for (StudentModel *studentModel in sourceArr) {
  19. NSInteger section = [collation sectionForObject:studentModel collationStringSelector:@selector(name)];
  20. studentModel.section = section;
  21. [sectionsArr[section] addObject:studentModel];
  22. }
  23. //对每组数据进行按名称首字母排序
  24. NSMutableArray *resultArr = [NSMutableArray arrayWithCapacity:1];
  25. for (NSArray *arr in sectionsArr) {
  26. [resultArr addObject:[NSMutableArray arrayWithArray:[collation sortedArrayFromArray:arr collationStringSelector:@selector(name)]]];
  27. }
  28. return resultArr;
  29. }
  • 在UITableView的代理方法中设置组的标题和索引数组

  1. - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
  2. {
  3. if (tableView == self.tableView && [self.studentsArr[section] count] > 0) {
  4. return [UILocalizedIndexedCollation currentCollation].sectionTitles[section];
  5. }
  6. return nil;
  7. }
  8. - (NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView
  9. {
  10. if (tableView == self.tableView) {
  11. NSMutableArray *resultArr = [NSMutableArray arrayWithCapacity:1];
  12. for (NSArray *arr in self.studentsArr) {
  13. if (arr.count > 0) {
  14. NSInteger index = [self.studentsArr indexOfObject:arr];
  15. [resultArr addObject:[UILocalizedIndexedCollation currentCollation].sectionTitles[index]];
  16. }
  17. }
  18. [resultArr insertObject:UITableViewIndexSearch atIndex:0];
  19. return resultArr;
  20. }
  21. else
  22. {
  23. return nil;
  24. }
  25. }
  • 在UITableView的代理方法中将索引与cell的组联系起来
  1. - (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
  2. {
  3. NSInteger result = -1;
  4. if (tableView == self.tableView) {
  5. if (index == 0) {
  6. [tableView scrollRectToVisible:tableView.tableHeaderView.frame animated:NO];
  7. }
  8. else
  9. {
  10. result = [[UILocalizedIndexedCollation currentCollation].sectionTitles indexOfObject:title];
  11. }
  12. }
  13. return result;
  14. }

iOS之Sqlite3封装的更多相关文章

  1. ios在SQLite3基本操作

    iOS关于sqlite3操作 iPhone中支持通过sqlite3来訪问iPhone本地的数据库. 详细用法例如以下 1:加入开发包libsqlite3.0.dylib 首先是设置项目文件.在项目中加 ...

  2. 使用iOS原生sqlite3框架对sqlite数据库进行操作

    摘要: iOS中sqlite3框架可以很好的对sqlite数据库进行支持,通过面向对象的封装,可以更易于开发者使用. 使用iOS原生sqlite3框架对sqlite数据库进行操作 一.引言 sqlit ...

  3. ios对SQLite3的使用

    ios对SQLite3的使用 一.在Firefox中打开sqlite3(如果没有,选择工具->附加组件,添加即可)新建sqlite3数据库,Contacts, 建立一个members表,字段 i ...

  4. iOS蓝牙原生封装,助力智能硬件开发

    代码地址如下:http://www.demodashi.com/demo/12010.html 人工智能自1956年提出以来,一直默默无闻,近年来人工智能的发展得到重视逐渐发展起步,智能硬件.智能手环 ...

  5. iOS 瀑布流封装

    代码地址如下:http://www.demodashi.com/demo/12284.html 一.效果预览 功能描述:WSLWaterFlowLayout 是在继承于UICollectionView ...

  6. android 仿ios 对话框已封装成工具类

    对话框 在android中是一种非经常见的交互提示用户的方式,可是非常多产品狗都叫我们这些做android的仿ios,搞的我们android程序猿非常苦逼,凭什么效果老是仿ios,有没有一点情怀,只是 ...

  7. iOS关于sqlite3操作

    原文:http://hi.baidu.com/clickto/blog/item/0c6904f787c34125720eec87.html iPhone中支持通过sqlite3来访问iPhone本地 ...

  8. iOS使用sqlite3原生语法进行增删改查以及FMDB的使用

    首先要导入libsqlite3.dylib并且加入头文件#import <sqlite3.h>,在进行增删改查之前还要先把数据库搞进去. 一种方法是从外面拷贝到程序里:http://www ...

  9. iOS:转载sqlite3

     SQLITE3 使用总结 2012-08-21 13:48:28 分类: SQLite/嵌入式数据库 SQLITE3 使用总结 2009-09-16 07:36 2624人阅读 评论(10) 收藏  ...

随机推荐

  1. SQL:1999基本语法(学习笔记)

    SQL:1999基本语法 SELECT [DISTINCT] * | 列名称 [AS]别名,........ FROM 表名称1 [别名1][CROSS JOIN表名称2 别名2]| [NATURAL ...

  2. 从零开始学JavaScript四(数据类型)

    一.分类 基本数据类型:undefined.null.string.Boolean.number 复杂数据类型:object object的属性以无序的名称和值对的形式 (name : value) ...

  3. margin 负边距应用

    margin-right:负值,在没有设置DOM元素宽度的前提下,DOM元素宽度变宽. <!DOCTYPE html> <html lang="zh-CN"> ...

  4. oracle 多字段去重查询

      oracle 多字段去重查询 CreationTime--2018年6月29日15点11分 Author:Marydon 1.情景展示 需要对表BASE_MRI_DEVICE的COMPNAME.F ...

  5. 简单四步開始树莓派上的Docker之旅

    大概这篇博文发表之后,应该算是我个人的第一篇翻译作品了,翻译的可能不是非常到位,望各位看官大刀砍过来. 原文链接:http://resin.io/blog/docker-on-raspberry-pi ...

  6. spring mvc自定义数据转换

    @InitBinder   在controller中注册一个customer protperty editor以解析request中的参数并通过date bind机制与handler method中的 ...

  7. 【Java虚拟机】运行时数据区

    Java在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途.创建和销毁的时间,有一些是随虚拟机的启动而创建,随虚拟机的退出而销毁,有些则是与线程一一对应,随 ...

  8. 理解并使用.NET 4.5中的HttpClient(转)

    原文地址:http://www.cnblogs.com/wywnet/p/httpclient.html HttpClient介绍HttpClient是.NET4.5引入的一个HTTP客户端库,其命名 ...

  9. spring.net aop 讲解

    spring.net aop几个术语: 切面:针对类 切点:针对方法 object.xml <?xml version="1.0" encoding="utf-8& ...

  10. jQuery正则:电话、身份证、邮箱简单校验

    if (!(/^1[3,5,6,7,8,9]\d{9}$/).test(e.detail.value.data_phone)) { wx.showToast({ title: '请输入有效11位手机号 ...