Book.m
上传用户:hechengdz
上传日期:2020-05-13
资源大小:1591k
文件大小:12k
- /*
- File: Book.m
- Abstract:
- The Book class manages the in-memory representation of information about a single book.
- Version: 1.9
-
- Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
- Inc. ("Apple") in consideration of your agreement to the following
- terms, and your use, installation, modification or redistribution of
- this Apple software constitutes acceptance of these terms. If you do
- not agree with these terms, please do not use, install, modify or
- redistribute this Apple software.
-
- In consideration of your agreement to abide by the following terms, and
- subject to these terms, Apple grants you a personal, non-exclusive
- license, under Apple's copyrights in this original Apple software (the
- "Apple Software"), to use, reproduce, modify and redistribute the Apple
- Software, with or without modifications, in source and/or binary forms;
- provided that if you redistribute the Apple Software in its entirety and
- without modifications, you must retain this notice and the following
- text and disclaimers in all such redistributions of the Apple Software.
- Neither the name, trademarks, service marks or logos of Apple Inc. may
- be used to endorse or promote products derived from the Apple Software
- without specific prior written permission from Apple. Except as
- expressly stated in this notice, no other rights or licenses, express or
- implied, are granted by Apple herein, including but not limited to any
- patent rights that may be infringed by your derivative works or by other
- works in which the Apple Software may be incorporated.
-
- The Apple Software is provided by Apple on an "AS IS" basis. APPLE
- MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
- THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
- FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
- OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
-
- IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
- MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
- AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
- STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
-
- Copyright (C) 2008 Apple Inc. All Rights Reserved.
-
- */
- #import "Book.h"
- // Static variables for compiled SQL queries. This implementation choice is to be able to share a one time
- // compilation of each query across all instances of the class. Each time a query is used, variables may be bound
- // to it, it will be "stepped", and then reset for the next usage. When the application begins to terminate,
- // a class method will be invoked to "finalize" (delete) the compiled queries - this must happen before the database
- // can be closed.
- static sqlite3_stmt *insert_statement = nil;
- static sqlite3_stmt *init_statement = nil;
- static sqlite3_stmt *delete_statement = nil;
- static sqlite3_stmt *hydrate_statement = nil;
- static sqlite3_stmt *dehydrate_statement = nil;
- @implementation Book
- // Finalize (delete) all of the SQLite compiled queries.
- + (void)finalizeStatements {
- if (insert_statement) {
- sqlite3_finalize(insert_statement);
- insert_statement = nil;
- }
- if (init_statement) {
- sqlite3_finalize(init_statement);
- init_statement = nil;
- }
- if (delete_statement) {
- sqlite3_finalize(delete_statement);
- delete_statement = nil;
- }
- if (hydrate_statement) {
- sqlite3_finalize(hydrate_statement);
- hydrate_statement = nil;
- }
- if (dehydrate_statement) {
- sqlite3_finalize(dehydrate_statement);
- dehydrate_statement = nil;
- }
- }
- // Creates the object with primary key and title is brought into memory.
- - (id)initWithPrimaryKey:(NSInteger)pk database:(sqlite3 *)db {
- if (self = [super init]) {
- primaryKey = pk;
- database = db;
- // Compile the query for retrieving book data. See insertNewBookIntoDatabase: for more detail.
- if (init_statement == nil) {
- // Note the '?' at the end of the query. This is a parameter which can be replaced by a bound variable.
- // This is a great way to optimize because frequently used queries can be compiled once, then with each
- // use new variable values can be bound to placeholders.
- const char *sql = "SELECT title FROM book WHERE pk=?";
- if (sqlite3_prepare_v2(database, sql, -1, &init_statement, NULL) != SQLITE_OK) {
- NSAssert1(0, @"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
- }
- }
- // For this query, we bind the primary key to the first (and only) placeholder in the statement.
- // Note that the parameters are numbered from 1, not from 0.
- sqlite3_bind_int(init_statement, 1, primaryKey);
- if (sqlite3_step(init_statement) == SQLITE_ROW) {
- self.title = [NSString stringWithUTF8String:(char *)sqlite3_column_text(init_statement, 0)];
- } else {
- self.title = @"No title";
- }
- // Reset the statement for future reuse.
- sqlite3_reset(init_statement);
- dirty = NO;
- }
- return self;
- }
- - (void)insertIntoDatabase:(sqlite3 *)db {
- database = db;
- // This query may be performed many times during the run of the application. As an optimization, a static
- // variable is used to store the SQLite compiled byte-code for the query, which is generated one time - the first
- // time the method is executed by any Book object.
- if (insert_statement == nil) {
- static char *sql = "INSERT INTO book (title) VALUES(?)";
- if (sqlite3_prepare_v2(database, sql, -1, &insert_statement, NULL) != SQLITE_OK) {
- NSAssert1(0, @"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
- }
- }
- sqlite3_bind_text(insert_statement, 1, [title UTF8String], -1, SQLITE_TRANSIENT);
- int success = sqlite3_step(insert_statement);
- // Because we want to reuse the statement, we "reset" it instead of "finalizing" it.
- sqlite3_reset(insert_statement);
- if (success == SQLITE_ERROR) {
- NSAssert1(0, @"Error: failed to insert into the database with message '%s'.", sqlite3_errmsg(database));
- } else {
- // SQLite provides a method which retrieves the value of the most recently auto-generated primary key sequence
- // in the database. To access this functionality, the table should have a column declared of type
- // "INTEGER PRIMARY KEY"
- primaryKey = sqlite3_last_insert_rowid(database);
- }
- // All data for the book is already in memory, but has not be written to the database
- // Mark as hydrated to prevent empty/default values from overwriting what is in memory
- hydrated = YES;
- }
- - (void)dealloc {
- [title release];
- [author release];
- [copyright release];
- [super dealloc];
- }
- - (void)deleteFromDatabase {
- // Compile the delete statement if needed.
- if (delete_statement == nil) {
- const char *sql = "DELETE FROM book WHERE pk=?";
- if (sqlite3_prepare_v2(database, sql, -1, &delete_statement, NULL) != SQLITE_OK) {
- NSAssert1(0, @"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
- }
- }
- // Bind the primary key variable.
- sqlite3_bind_int(delete_statement, 1, primaryKey);
- // Execute the query.
- int success = sqlite3_step(delete_statement);
- // Reset the statement for future use.
- sqlite3_reset(delete_statement);
- // Handle errors.
- if (success != SQLITE_DONE) {
- NSAssert1(0, @"Error: failed to delete from database with message '%s'.", sqlite3_errmsg(database));
- }
- }
- // Brings the rest of the object data into memory. If already in memory, no action is taken (harmless no-op).
- - (void)hydrate {
- // Check if action is necessary.
- if (hydrated) return;
- // Compile the hydration statement, if needed.
- if (hydrate_statement == nil) {
- const char *sql = "SELECT author, copyright FROM book WHERE pk=?";
- if (sqlite3_prepare_v2(database, sql, -1, &hydrate_statement, NULL) != SQLITE_OK) {
- NSAssert1(0, @"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
- }
- }
- // Bind the primary key variable.
- sqlite3_bind_int(hydrate_statement, 1, primaryKey);
- // Execute the query.
- int success =sqlite3_step(hydrate_statement);
- if (success == SQLITE_ROW) {
- char *str = (char *)sqlite3_column_text(hydrate_statement, 0);
- self.author = (str) ? [NSString stringWithUTF8String:str] : @"";
- self.copyright = [NSDate dateWithTimeIntervalSince1970:sqlite3_column_double(hydrate_statement, 1)];
- } else {
- // The query did not return
- self.author = @"Unknown";
- self.copyright = [NSDate date];
- }
- // Reset the query for the next use.
- sqlite3_reset(hydrate_statement);
- // Update object state with respect to hydration.
- hydrated = YES;
- }
- // Flushes all but the primary key and title out to the database.
- - (void)dehydrate {
- if (dirty) {
- // Write any changes to the database.
- // First, if needed, compile the dehydrate query.
- if (dehydrate_statement == nil) {
- const char *sql = "UPDATE book SET title=?, author=?, copyright=? WHERE pk=?";
- if (sqlite3_prepare_v2(database, sql, -1, &dehydrate_statement, NULL) != SQLITE_OK) {
- NSAssert1(0, @"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
- }
- }
- // Bind the query variables.
- sqlite3_bind_text(dehydrate_statement, 1, [title UTF8String], -1, SQLITE_TRANSIENT);
- sqlite3_bind_text(dehydrate_statement, 2, [author UTF8String], -1, SQLITE_TRANSIENT);
- sqlite3_bind_double(dehydrate_statement, 3, [copyright timeIntervalSince1970]);
- sqlite3_bind_int(dehydrate_statement, 4, primaryKey);
- // Execute the query.
- int success = sqlite3_step(dehydrate_statement);
- // Reset the query for the next use.
- sqlite3_reset(dehydrate_statement);
- // Handle errors.
- if (success != SQLITE_DONE) {
- NSAssert1(0, @"Error: failed to dehydrate with message '%s'.", sqlite3_errmsg(database));
- }
- // Update the object state with respect to unwritten changes.
- dirty = NO;
- }
- // Release member variables to reclaim memory. Set to nil to avoid over-releasing them
- // if dehydrate is called multiple times.
- [author release];
- author = nil;
- [copyright release];
- copyright = nil;
- [data release];
- data = nil;
- // Update the object state with respect to hydration.
- hydrated = NO;
- }
- #pragma mark Properties
- // Accessors implemented below. All the "get" accessors simply return the value directly, with no additional
- // logic or steps for synchronization. The "set" accessors attempt to verify that the new value is definitely
- // different from the old value, to minimize the amount of work done. Any "set" which actually results in changing
- // data will mark the object as "dirty" - i.e., possessing data that has not been written to the database.
- // All the "set" accessors copy data, rather than retain it. This is common for value objects - strings, numbers,
- // dates, data buffers, etc. This ensures that subsequent changes to either the original or the copy don't violate
- // the encapsulation of the owning object.
- - (NSInteger)primaryKey {
- return primaryKey;
- }
- - (NSString *)title {
- return title;
- }
- - (void)setTitle:(NSString *)aString {
- if ((!title && !aString) || (title && aString && [title isEqualToString:aString])) return;
- dirty = YES;
- [title release];
- title = [aString copy];
- }
- - (NSString *)author {
- return author;
- }
- - (void)setAuthor:(NSString *)aString {
- if ((!author && !aString) || (author && aString && [author isEqualToString:aString])) return;
- dirty = YES;
- [author release];
- author = [aString copy];
- }
- - (NSDate *)copyright {
- return copyright;
- }
- - (void)setCopyright:(NSDate *)aDate {
- if ((!copyright && !aDate) || (copyright && aDate && [copyright isEqualToDate:aDate])) return;
- dirty = YES;
- [copyright release];
- copyright = [aDate copy];
- }
- @end