?? book.m
字號:
/* 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
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -