Book.m
上传用户:hechengdz
上传日期:2020-05-13
资源大小:1591k
文件大小:12k
源码类别:

iPhone

开发平台:

Objective-C

  1. /*
  2.      File: Book.m
  3.  Abstract: 
  4. The Book class manages the in-memory representation of information about a single book.  
  5.   Version: 1.9
  6.  
  7.  Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple
  8.  Inc. ("Apple") in consideration of your agreement to the following
  9.  terms, and your use, installation, modification or redistribution of
  10.  this Apple software constitutes acceptance of these terms.  If you do
  11.  not agree with these terms, please do not use, install, modify or
  12.  redistribute this Apple software.
  13.  
  14.  In consideration of your agreement to abide by the following terms, and
  15.  subject to these terms, Apple grants you a personal, non-exclusive
  16.  license, under Apple's copyrights in this original Apple software (the
  17.  "Apple Software"), to use, reproduce, modify and redistribute the Apple
  18.  Software, with or without modifications, in source and/or binary forms;
  19.  provided that if you redistribute the Apple Software in its entirety and
  20.  without modifications, you must retain this notice and the following
  21.  text and disclaimers in all such redistributions of the Apple Software.
  22.  Neither the name, trademarks, service marks or logos of Apple Inc. may
  23.  be used to endorse or promote products derived from the Apple Software
  24.  without specific prior written permission from Apple.  Except as
  25.  expressly stated in this notice, no other rights or licenses, express or
  26.  implied, are granted by Apple herein, including but not limited to any
  27.  patent rights that may be infringed by your derivative works or by other
  28.  works in which the Apple Software may be incorporated.
  29.  
  30.  The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
  31.  MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
  32.  THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
  33.  FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
  34.  OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
  35.  
  36.  IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
  37.  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  38.  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  39.  INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
  40.  MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
  41.  AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
  42.  STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
  43.  POSSIBILITY OF SUCH DAMAGE.
  44.  
  45.  Copyright (C) 2008 Apple Inc. All Rights Reserved.
  46.  
  47.  */
  48. #import "Book.h"
  49. // Static variables for compiled SQL queries. This implementation choice is to be able to share a one time
  50. // compilation of each query across all instances of the class. Each time a query is used, variables may be bound
  51. // to it, it will be "stepped", and then reset for the next usage. When the application begins to terminate,
  52. // a class method will be invoked to "finalize" (delete) the compiled queries - this must happen before the database
  53. // can be closed.
  54. static sqlite3_stmt *insert_statement = nil;
  55. static sqlite3_stmt *init_statement = nil;
  56. static sqlite3_stmt *delete_statement = nil;
  57. static sqlite3_stmt *hydrate_statement = nil;
  58. static sqlite3_stmt *dehydrate_statement = nil;
  59. @implementation Book
  60. // Finalize (delete) all of the SQLite compiled queries.
  61. + (void)finalizeStatements {
  62.     if (insert_statement) {
  63.         sqlite3_finalize(insert_statement);
  64.         insert_statement = nil;
  65.     }
  66.     if (init_statement) {
  67.         sqlite3_finalize(init_statement);
  68.         init_statement = nil;
  69.     }
  70.     if (delete_statement) {
  71.         sqlite3_finalize(delete_statement);
  72.         delete_statement = nil;
  73.     }
  74.     if (hydrate_statement) {
  75.         sqlite3_finalize(hydrate_statement);
  76.         hydrate_statement = nil;
  77.     }
  78.     if (dehydrate_statement) {
  79.         sqlite3_finalize(dehydrate_statement);
  80.         dehydrate_statement = nil;
  81.     }
  82. }
  83. // Creates the object with primary key and title is brought into memory.
  84. - (id)initWithPrimaryKey:(NSInteger)pk database:(sqlite3 *)db {
  85.     if (self = [super init]) {
  86.         primaryKey = pk;
  87.         database = db;
  88.         // Compile the query for retrieving book data. See insertNewBookIntoDatabase: for more detail.
  89.         if (init_statement == nil) {
  90.             // Note the '?' at the end of the query. This is a parameter which can be replaced by a bound variable.
  91.             // This is a great way to optimize because frequently used queries can be compiled once, then with each
  92.             // use new variable values can be bound to placeholders.
  93.             const char *sql = "SELECT title FROM book WHERE pk=?";
  94.             if (sqlite3_prepare_v2(database, sql, -1, &init_statement, NULL) != SQLITE_OK) {
  95.                 NSAssert1(0, @"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
  96.             }
  97.         }
  98.         // For this query, we bind the primary key to the first (and only) placeholder in the statement.
  99.         // Note that the parameters are numbered from 1, not from 0.
  100.         sqlite3_bind_int(init_statement, 1, primaryKey);
  101.         if (sqlite3_step(init_statement) == SQLITE_ROW) {
  102.             self.title = [NSString stringWithUTF8String:(char *)sqlite3_column_text(init_statement, 0)];
  103.         } else {
  104.             self.title = @"No title";
  105.         }
  106.         // Reset the statement for future reuse.
  107.         sqlite3_reset(init_statement);
  108.         dirty = NO;
  109.     }
  110.     return self;
  111. }
  112. - (void)insertIntoDatabase:(sqlite3 *)db {
  113.     database = db;
  114.     // This query may be performed many times during the run of the application. As an optimization, a static
  115.     // variable is used to store the SQLite compiled byte-code for the query, which is generated one time - the first
  116.     // time the method is executed by any Book object.
  117.     if (insert_statement == nil) {
  118.         static char *sql = "INSERT INTO book (title) VALUES(?)";
  119.         if (sqlite3_prepare_v2(database, sql, -1, &insert_statement, NULL) != SQLITE_OK) {
  120.             NSAssert1(0, @"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
  121.         }
  122.     }
  123.     sqlite3_bind_text(insert_statement, 1, [title UTF8String], -1, SQLITE_TRANSIENT);
  124.     int success = sqlite3_step(insert_statement);
  125.     // Because we want to reuse the statement, we "reset" it instead of "finalizing" it.
  126.     sqlite3_reset(insert_statement);
  127.     if (success == SQLITE_ERROR) {
  128.         NSAssert1(0, @"Error: failed to insert into the database with message '%s'.", sqlite3_errmsg(database));
  129.     } else {
  130.         // SQLite provides a method which retrieves the value of the most recently auto-generated primary key sequence
  131.         // in the database. To access this functionality, the table should have a column declared of type 
  132.         // "INTEGER PRIMARY KEY"
  133.         primaryKey = sqlite3_last_insert_rowid(database);
  134.     }
  135.     // All data for the book is already in memory, but has not be written to the database
  136.     // Mark as hydrated to prevent empty/default values from overwriting what is in memory
  137.     hydrated = YES;
  138. }
  139. - (void)dealloc {
  140.     [title release];
  141.     [author release];
  142.     [copyright release];
  143.     [super dealloc];
  144. }
  145. - (void)deleteFromDatabase {
  146.     // Compile the delete statement if needed.
  147.     if (delete_statement == nil) {
  148.         const char *sql = "DELETE FROM book WHERE pk=?";
  149.         if (sqlite3_prepare_v2(database, sql, -1, &delete_statement, NULL) != SQLITE_OK) {
  150.             NSAssert1(0, @"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
  151.         }
  152.     }
  153.     // Bind the primary key variable.
  154.     sqlite3_bind_int(delete_statement, 1, primaryKey);
  155.     // Execute the query.
  156.     int success = sqlite3_step(delete_statement);
  157.     // Reset the statement for future use.
  158.     sqlite3_reset(delete_statement);
  159.     // Handle errors.
  160.     if (success != SQLITE_DONE) {
  161.         NSAssert1(0, @"Error: failed to delete from database with message '%s'.", sqlite3_errmsg(database));
  162.     }
  163. }
  164. // Brings the rest of the object data into memory. If already in memory, no action is taken (harmless no-op).
  165. - (void)hydrate {
  166.     // Check if action is necessary.
  167.     if (hydrated) return;
  168.     // Compile the hydration statement, if needed.
  169.     if (hydrate_statement == nil) {
  170.         const char *sql = "SELECT author, copyright FROM book WHERE pk=?";
  171.         if (sqlite3_prepare_v2(database, sql, -1, &hydrate_statement, NULL) != SQLITE_OK) {
  172.             NSAssert1(0, @"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
  173.         }
  174.     }
  175.     // Bind the primary key variable.
  176.     sqlite3_bind_int(hydrate_statement, 1, primaryKey);
  177.     // Execute the query.
  178.     int success =sqlite3_step(hydrate_statement);
  179.     if (success == SQLITE_ROW) {
  180.         char *str = (char *)sqlite3_column_text(hydrate_statement, 0);
  181.         self.author = (str) ? [NSString stringWithUTF8String:str] : @"";
  182.         self.copyright = [NSDate dateWithTimeIntervalSince1970:sqlite3_column_double(hydrate_statement, 1)];
  183.     } else {
  184.         // The query did not return 
  185.         self.author = @"Unknown";
  186.         self.copyright = [NSDate date];
  187.     }
  188.     // Reset the query for the next use.
  189.     sqlite3_reset(hydrate_statement);
  190.     // Update object state with respect to hydration.
  191.     hydrated = YES;
  192. }
  193. // Flushes all but the primary key and title out to the database.
  194. - (void)dehydrate {
  195.     if (dirty) {
  196.         // Write any changes to the database.
  197.         // First, if needed, compile the dehydrate query.
  198.         if (dehydrate_statement == nil) {
  199.             const char *sql = "UPDATE book SET title=?, author=?, copyright=? WHERE pk=?";
  200.             if (sqlite3_prepare_v2(database, sql, -1, &dehydrate_statement, NULL) != SQLITE_OK) {
  201.                 NSAssert1(0, @"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
  202.             }
  203.         }
  204.         // Bind the query variables.
  205.         sqlite3_bind_text(dehydrate_statement, 1, [title UTF8String], -1, SQLITE_TRANSIENT);
  206.         sqlite3_bind_text(dehydrate_statement, 2, [author UTF8String], -1, SQLITE_TRANSIENT);
  207.         sqlite3_bind_double(dehydrate_statement, 3, [copyright timeIntervalSince1970]);
  208.         sqlite3_bind_int(dehydrate_statement, 4, primaryKey);
  209.         // Execute the query.
  210.         int success = sqlite3_step(dehydrate_statement);
  211.         // Reset the query for the next use.
  212.         sqlite3_reset(dehydrate_statement);
  213.         // Handle errors.
  214.         if (success != SQLITE_DONE) {
  215.             NSAssert1(0, @"Error: failed to dehydrate with message '%s'.", sqlite3_errmsg(database));
  216.         }
  217.         // Update the object state with respect to unwritten changes.
  218.         dirty = NO;
  219.     }
  220.     // Release member variables to reclaim memory. Set to nil to avoid over-releasing them 
  221.     // if dehydrate is called multiple times.
  222.     [author release];
  223.     author = nil;
  224.     [copyright release];
  225.     copyright = nil;
  226.     [data release];
  227.     data = nil;
  228.     // Update the object state with respect to hydration.
  229.     hydrated = NO;
  230. }
  231. #pragma mark Properties
  232. // Accessors implemented below. All the "get" accessors simply return the value directly, with no additional
  233. // logic or steps for synchronization. The "set" accessors attempt to verify that the new value is definitely
  234. // different from the old value, to minimize the amount of work done. Any "set" which actually results in changing
  235. // data will mark the object as "dirty" - i.e., possessing data that has not been written to the database.
  236. // All the "set" accessors copy data, rather than retain it. This is common for value objects - strings, numbers, 
  237. // dates, data buffers, etc. This ensures that subsequent changes to either the original or the copy don't violate 
  238. // the encapsulation of the owning object.
  239. - (NSInteger)primaryKey {
  240.     return primaryKey;
  241. }
  242. - (NSString *)title {
  243.     return title;
  244. }
  245. - (void)setTitle:(NSString *)aString {
  246.     if ((!title && !aString) || (title && aString && [title isEqualToString:aString])) return;
  247.     dirty = YES;
  248.     [title release];
  249.     title = [aString copy];
  250. }
  251. - (NSString *)author {
  252.     return author;
  253. }
  254. - (void)setAuthor:(NSString *)aString {
  255.     if ((!author && !aString) || (author && aString && [author isEqualToString:aString])) return;
  256.     dirty = YES;
  257.     [author release];
  258.     author = [aString copy];
  259. }
  260. - (NSDate *)copyright {
  261.     return copyright;
  262. }
  263. - (void)setCopyright:(NSDate *)aDate {
  264.     if ((!copyright && !aDate) || (copyright && aDate && [copyright isEqualToDate:aDate])) return;
  265.     dirty = YES;
  266.     [copyright release];
  267.     copyright = [aDate copy];
  268. }
  269. @end