more iphone 3 development phần 5 pps

57 194 0
more iphone 3 development phần 5 pps

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

CHAPTER 7: Relationships, Fetched Properties, and Expressions 212 Save the file. We’re actually creating two categories in this one file pair. We could have just as easily added these four methods in a single category, but to make things more organized, we’re separating the methods by the entity that they are used to edit. Switch over to ManagedObjectEditor-SuperDB.m and replace its contents with the following: #import "ManagedObjectEditor-SuperDB.h" @implementation ManagedObjectEditor (HeroEditor) + (id)controllerForHero { id ret = [[[self class] alloc] initHeroEditor]; return [ret autorelease]; } - (id)initHeroEditor { if (self = [super initWithStyle:UITableViewStyleGrouped]) { sectionNames = [[NSArray alloc] initWithObjects: [NSNull null], NSLocalizedString(@"General", @"General"), NSLocalizedString(@"Powers", @"Powers"), NSLocalizedString(@"Reports", @"Reports"), nil]; rowLabels = [[NSArray alloc] initWithObjects: // Section 1 [NSArray arrayWithObjects:NSLocalizedString(@"Name", @"Name"), nil], // Section 2 [NSArray arrayWithObjects:NSLocalizedString(@"Identity", @"Identity"), NSLocalizedString(@"Birthdate", @"Birthdate"), NSLocalizedString(@"Age", @"Age"), NSLocalizedString(@"Sex", @"Sex"), NSLocalizedString(@"Fav. Color", @"Favorite Color"), nil], // Section 3 [NSArray arrayWithObject:@"name"], // label here is the key on the // other object to use as the label // Section 4 [NSArray arrayWithObjects: NSLocalizedString(@"All Older Heroes", @"All Older Heroes"]), NSLocalizedString(@"All Younger Heroes", @"All Younger Heroes"), NSLocalizedString(@"Same Sex Heroes", @"Same Sex Heroes"), NSLocalizedString(@"Opposite Sex Heroes", @" Opposite Sex Heroes"), nil], // Sentinel nil]; rowKeys = [[NSArray alloc] initWithObjects: CHAPTER 7: Relationships, Fetched Properties, and Expressions 213 // Section 1 [NSArray arrayWithObjects:@"name", nil], // Section 2 [NSArray arrayWithObjects:@"secretIdentity", @"birthdate", @"age", @"sex", @"favoriteColor", nil], // Section 3 [NSArray arrayWithObject:@"powers"], // Section 4 [NSArray arrayWithObjects:@"olderHeroes", @"youngerHeroes", @"sameSexHeroes", @"oppositeSexHeroes", nil], // Sentinel nil]; rowControllers = [[NSArray alloc] initWithObjects: // Section 1 [NSArray arrayWithObject:@"ManagedObjectStringEditor"], // Section 2 [NSArray arrayWithObjects:@"ManagedObjectStringEditor", @"ManagedObjectDateEditor", [NSNull null], @"ManagedObjectSingleSelectionListEditor", @"ManagedObjectColorEditor", nil], // Section 3 [NSArray arrayWithObject:kToManyRelationship], // Section 4 [NSArray arrayWithObjects: @"ManagedObjectFetchedPropertyDisplayer", @"ManagedObjectFetchedPropertyDisplayer", @"ManagedObjectFetchedPropertyDisplayer", @"ManagedObjectFetchedPropertyDisplayer", nil], // Sentinel nil]; rowArguments = [[NSArray alloc] initWithObjects: // Section 1 [NSArray arrayWithObject:[NSNull null]], // Section 2 [NSArray arrayWithObjects:[NSNull null], [NSNull null], [NSNull null], [NSDictionary dictionaryWithObject: [NSArray arrayWithObjects:@"Male", @"Female", nil] forKey:@"list"], [NSNull null], CHAPTER 7: Relationships, Fetched Properties, and Expressions 214 [NSNull null], nil], // Section 3 [NSArray arrayWithObject:[NSDictionary dictionaryWithObjectsAndKeys: @"controllerForPower", kSelectorKey, nil]], //Section 4 [NSArray arrayWithObjects: [NSDictionary dictionaryWithObjectsAndKeys: @"name", @"displayKey", @"controllerForHero", @"controllerFactoryMethod", nil], [NSDictionary dictionaryWithObjectsAndKeys: @"name", @"displayKey", @"controllerForHero", @"controllerFactoryMethod", nil], [NSDictionary dictionaryWithObjectsAndKeys: @"name", @"displayKey", @"controllerForHero", @"controllerFactoryMethod", nil], [NSDictionary dictionaryWithObjectsAndKeys: @"name", @"displayKey", @"controllerForHero", @"controllerFactoryMethod", nil], nil], // Sentinel nil]; } return self; } @end @implementation ManagedObjectEditor (PowerEditor) + (id)controllerForPower { id ret = [[[self class] alloc] initPowerEditor]; return [ret autorelease]; } - (id)initPowerEditor { if (self = [[[self class] alloc] initWithStyle:UITableViewStyleGrouped]) { sectionNames = [[NSArray alloc] initWithObjects:[NSNull null], [NSNull null], nil]; rowLabels = [[NSArray alloc] initWithObjects: [NSArray arrayWithObject:NSLocalizedString(@"Name", @"Name")], [NSArray arrayWithObject:NSLocalizedString(@"Source", @"Source")], nil]; rowKeys = [[NSArray alloc] initWithObjects: [NSArray arrayWithObject:@"name"], [NSArray arrayWithObject:@"source"], nil]; rowControllers = [[NSArray alloc] initWithObjects: [NSArray arrayWithObject:@"ManagedObjectStringEditor"], [NSArray arrayWithObject:@"ManagedObjectStringEditor"], nil]; rowArguments = [[NSArray alloc] initWithObjects: CHAPTER 7: Relationships, Fetched Properties, and Expressions 215 [NSArray arrayWithObject:[NSNull null]], [NSArray arrayWithObject:[NSNull null]], nil]; } return self; } @end The two init methods should look familiar to you. They set up the structure arrays, just as in viewDidLoad. The contents of the Hero arrays have gotten a little more complex, since we’ve added a to-many relationship and four fetched properties, but the basic concept is unchanged from before. You should look these over to make sure you understand what they’re doing. We’ve been working with the nested arrays long enough now that we’re not going to step through them line by line. Deleting the Nib Instance We need to delete the instance of ManagedObjectEditor in MainWindow.xib. If you remember from the earlier chapters, there is an instance of HeroEditController in the nib, and that instance is used to edit all heroes. When we refactored HeroEditController, the instance of the nib became an instance of ManagedObjectEditor. We can no longer instantiate our controller class from the nib file because the nested arrays won’t be set up properly if we leave it like this. We used to create the arrays in viewDidLoad, but that is no longer the case, so we need to create the controller instance in code to make sure that those arrays are created. Double-click MainWindow.xib in the Groups & Files pane to open Interface Builder. Look in the nib’s main window for an icon labeled Managed Object Editor. Single-click it to select it, and then press the Delete key on your keyboard to delete it. Note that if you are in list mode, Managed Object Editor will also have a child Table View. No worries—that child view will disappear when you delete the parent. Save the nib and go back to Xcode. Updating HeroListController Now that we’re not creating an instance of ManagedObjectEditor in MainWindow.xib, we need to take care of that task in code. We will do this in HeroListViewController, which is the navigation controller’s root view controller. Single-click HeroListViewController.m and add the following import statements at the top of the file: #import "HeroListViewController.h" #import "SuperDBAppDelegate.h" #import "ManagedObjectEditor.h" #import "Hero.h" #import "ManagedObjectEditor-SuperDB.h" CHAPTER 7: Relationships, Fetched Properties, and Expressions 216 @implementation HeroListViewController Next, we need to create the controller class in viewDidLoad. Insert the following line of code into viewDidLoad to accomplish that: - (void)viewDidLoad { [super viewDidLoad]; self.detailController = [ManagedObjectEditor controllerForHero]; NSError *error = nil; Because we’re using the factory method controllerForHero, the controller class that is created will have all the arrays populated so that it works correctly and allows the user to edit the Hero entity. Creating the Fetched Property Attribute Controller At this point, the application should run and work mostly okay, with the exception of the fetched properties. We haven’t written the controller to display them yet. Let’s do that now. You’ve written enough of these attribute editing classes, so we won’t walk through this one step by step. Create a new file by single-clicking the Classes folder and selecting N from the File menu. Use the Objective-C class template, subclass NSObject, and name the new file ManagedObjectFetchedPropertyDisplayer.m, making sure to create the header file as well. Once the file is created, single-click ManagedObjectFetchedPropertyDisplayer.h and replace the contents with the following: #import <Foundation/Foundation.h> #import "ManagedObjectAttributeEditor.h" @interface ManagedObjectFetchedPropertyDisplayer : ManagedObjectAttributeEditor { NSString *displayKey; NSString *controllerFactoryMethod; } @property (nonatomic, retain) NSString *displayKey; @end Save the file. Switch over to ManagedObjectFetchedPropertyDisplayer.m and replace its contents with the following: #import "ManagedObjectFetchedPropertyDisplayer.h" #import "NSArray-Set.h" #import "ManagedObjectEditor.h" @implementation ManagedObjectFetchedPropertyDisplayer @synthesize displayKey; - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; CHAPTER 7: Relationships, Fetched Properties, and Expressions 217 self.navigationItem.leftBarButtonItem = nil; self.navigationItem.rightBarButtonItem = nil; } - (void)dealloc { [displayKey release]; [super dealloc]; } #pragma mark - #pragma mark Table View Methods - (NSInteger)tableView:(UITableView *)theTableView numberOfRowsInSection:(NSInteger)section { NSArray *array = [self.managedObject valueForKey:keypath]; return [array count]; } - (UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Fetched Property Display Cell"; UITableViewCell *cell = [theTableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; } NSArray *array = [self.managedObject valueForKey:keypath]; NSManagedObject *oneObject = [array objectAtIndex:[indexPath row]]; cell.textLabel.text = [oneObject valueForKey:displayKey]; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; return cell; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { NSArray *array = [self.managedObject valueForKey:keypath]; NSManagedObject *oneObject = [array objectAtIndex:[indexPath row]]; SEL factorySelector = NSSelectorFromString(controllerFactoryMethod); ManagedObjectEditor *controller = [ManagedObjectEditor performSelector:factorySelector]; controller.managedObject = oneObject; [self.navigationController pushViewController:controller animated:YES]; } @end This attribute editor uses Objective-C’s dynamic dispatching to let the calling object specify a factory method that can be used to edit any of the objects in the fetched relationship. Selecting a hero in one of the lists drills down and lets you edit that hero in a new instance of ManagedObjectEditor. In fact, you can drill down endlessly, even in our simple application—at least until you run out of memory. CHAPTER 7: Relationships, Fetched Properties, and Expressions 218 Build and run the application, and then test it. Try out the four fetched properties, and make sure you see the heroes you expect to see in each one. Try drilling down to edit the heroes from the fetched property. It’s pretty good, and you can extend this application quite a bit without writing any code except new factory methods to populate those arrays. Cleaning Up Deleted Objects There is still one minor problem to address. Select a hero or create a new one, and then hit the plus button to add a new power to the hero. Once the new view comes up, immediately hit the Cancel button. When you get back to the original hero, you’ll see two insert rows, as shown in Figure 7–18. Figure 7–18. Oops! That’s not good. Here’s what’s happening. When we added the new power, the power instance was added to the managed object context in memory. When we pressed the Cancel button, we deleted the object from the context. But instead, the delete rule should have come into play, and the object should have been deleted from the data structure that Core Data uses to represent the relationship in memory. This is a bug—at least as of this writing. We could have ignored this, hoping that the bug was fixed before the book was released, but we didn’t want to leave you hanging. There are a number of ways that we could handle this. CHAPTER 7: Relationships, Fetched Properties, and Expressions 219 We could, for example, give the ManagedObjectEditor class a property that points to its parent controller—the one that created it and pushed it onto the navigation stack. With that information, we could then remove the offending object from the relationship when we delete it. That creates a dependency, however. It operates under the assumption that the parent view controller is the same class, and we know that that’s not always true, because HeroListController is the parent view controller for one instance of this class. How can we fix the problem, then? What we can do is loop through the properties of the managed object looking for instances of NSSet, which we know will represent to-many relationships. When we find one, we can loop through the objects in the relationship, and if we find a deleted one, we can remove it. In order to get access to information about an object’s properties, we need to use the Objective-C runtime, which is a library of C functions that are responsible for Objective- C’s dynamic nature. Single-click ManagedObjectEditor.m. In order to call any of the Objective-C runtime’s functions, we need to import two header files. Insert the following two lines of code near the top of the file: #import "ManagedObjectEditor.h" #import "NSArray-NestedArrays.h" #import "HeroValueDisplay.h" #import "ManagedObjectAttributeEditor.h" #import "NSManagedObject-IsNew.h" #import "NSArray-Set.h" #import <objc/runtime.h> #import <objc/message.h> Now, look for the viewWillAppear: method. At the very beginning of that method, insert the following code: - (void)viewWillAppear:(BOOL)animated { unsigned int outCount; objc_property_t *propList = class_copyPropertyList([self.managedObject class], &outCount); for (int i = 0; i < outCount; i++) { objc_property_t oneProp = propList[i]; NSString *propName = [NSString stringWithUTF8String:property_getName(oneProp)]; NSString *attrs = [NSString stringWithUTF8String: property_getAttributes(oneProp)]; if ([attrs rangeOfString:@"NSSet"].location != NSNotFound) { NSMutableSet *objects = [self.managedObject CHAPTER 7: Relationships, Fetched Properties, and Expressions 220 valueForKey:propName]; NSMutableArray *toDelete = [NSMutableArray array]; for (NSManagedObject *oneObject in objects) { if ([oneObject isDeleted]) [toDelete addObject:oneObject]; } for (NSManagedObject *oneObject in toDelete) { [objects removeObject:oneObject]; NSError *error; if (![self.managedObject.managedObjectContext save:&error]) NSLog(@"Error saving: %@", [error localizedDescription]); } } } free(propList); [self.tableView reloadData]; NOTE: The Objective-C runtime is fairly advanced juju, so if you don’t 100% understand this right now, don’t worry about it. You can read up on the Objective-C runtime in Apple’s documentation: http://developer.apple.com/mac/library/documentation/Cocoa/Referen ce/ObjCRuntimeRef/Reference/reference.html This is the first time we’ve worked with the Objective-C runtime directly. Although for most programming jobs there’s no need to dive down into the runtime, having access to the same functions that are used to implement Objective-C gives us an incredible amount of power. Let’s quickly run through what we’re doing here, but don’t feel like you have to grok this one the first time through. First, we declare an int, which will hold the number of properties that managedObject has. Then we declare a pointer to an objc_property_t, which is a datatype that represents Objective-C 2.0 properties, and use a runtime function called class_copyPropertyList() to retrieve the list of pointers to the managedObject properties. This function also populates outCount with the number of properties. unsigned int outCount; objc_property_t *propList = class_copyPropertyList([self.managedObject class], &outCount); Next, we use a for loop to iterate over the properties: for (int i=0; i < outCount; i++) { We grab a reference to the structure that points to one property in the list, and then get the property’s name as an NSString instance. We also get the property’s attributes, which are contained in a string. The format for the attribute string is documented in CHAPTER 7: Relationships, Fetched Properties, and Expressions 221 Apple’s Objective-C runtime documentation, but for our purposes, all we need to know is that it contains (among other things) the class of the property. objc_property_t oneProp = propList[i]; NSString *propName = [NSString stringWithUTF8String:property_getName(oneProp)]; NSString *attrs = [NSString stringWithUTF8String: property_getAttributes(oneProp)]; We check to see if the attribute string contains @"NSSet": if ([attrs rangeOfString:@"NSSet"].location != NSNotFound) { If it does, we then retrieve the set and create an instance of NSMutableArray to keep track of the objects that need to be deleted. It is not safe to delete objects from a collection while we are iterating over it, so we’ll stick them in an array. Then, when we’re finished iterating, we’ll iterate through the array of objects that need to be deleted and remove them. NSMutableSet *objects = [self.managedObject valueForKey:propName]; NSMutableArray *toDelete = [NSMutableArray array]; for (NSManagedObject *oneObject in objects) { if ([oneObject isDeleted]) [toDelete addObject:oneObject]; } for (NSManagedObject *oneObject in toDelete) { [objects removeObject:oneObject]; NSError *error; if (![self.managedObject.managedObjectContext save:&error]) NSLog(@"Error saving: %@", [error localizedDescription]); } } } And, believe it or not, the application is done. Build and run it, and try it out. See how many times you can drill down. Try creating new powers, deleting existing powers, and canceling when editing both new and existing powers. Now, if you really want to challenge yourself, try adding more entities and relationships and using ManagedObjectEditor instances and its nested arrays to allow editing of those new entities. In short, play. Get used to this application. Expand it. Change it. Break it. And then fix it. That’s the best way to cement your understanding of everything we did in this chapter. Wonderful to the Core This chapter and the previous chapters have given you a solid foundation in the use of Core Data. Along the way, we’ve also tried to give you some information about how to design complex iPhone applications so that they can be maintained and expanded without writing unnecessary code or repeating the same logic in multiple places. We’ve demonstrated just how much benefit you can get from taking the time to write code generically. We’ve showed you how to look for opportunities to refactor your code to [...]... explorations even further Now it’s time to leave our friend Core Data behind and explore some of the other aspects of iPhone SDK 3 2 23 Part II Further Explorations We’ve devoted six chapters to the biggest of the new APIs, but Core Data is not all that iPhone SDK 3 brings to the table for iPhone developers A whole slew of new functionality has been made available, including peer-to-peer connectivity,... dive into a few more advanced topics such as networking and concurrency 224 2 25 Chapter 8 Peer-to-Peer Over Bluetooth Using GameKit One of the coolest new frameworks added to the iPhone 3 SDK is called GameKit GameKit makes it easy to wirelessly connect multiple iPhones or iPod touches using Bluetooth Bluetooth is a wireless networking option built into all but the first-generation iPhone and iPod... folder for the version of the iPhone SDK that you’re using, and then selecting GameKit.framework The Frameworks folder is at the following location: /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOSx.y.z.sdk/System/Library/F rameworks In this path, x, y, and z denote the release number For iPhone SDK 3. 1.2, for example (the current version as of this writing), x is 3, y is 1, and z is 2 In...222 CHAPTER 7: Relationships, Fetched Properties, and Expressions make it smaller, more efficient, easier to maintain, and just generally more pleasant to be around We could go on for several more chapters about Core Data and not exhaust the topic But Core Data is not the only new framework introduced in iPhone SDK 3 At this point, you should have a solid enough understanding of Core Data to be able... unavailable, as well as when a peer connects or disconnects The second argument will tell you which peer’s state changed, and the last argument will tell you its new state 233 234 CHAPTER 8: Peer-to-Peer Over Bluetooth Using GameKit If you find one or more other sessions that are available, you can choose to connect the session to one of the available sessions by calling connectToPeer:withTimeout: Here’s an example... way to build and debug apps on a device attached to your machine is by joining the paid iPhone Developer Program So you’ll need to do that if you want to fully experience this chapter’s chewy goodness In addition, the game we’re building in this chapter requires the use of two second-generation devices (iPhone 3G or 3Gs, or second-generation iPod touch) to run and test As of this writing, you cannot play... roughly 30 feet (about 10 meters) of each other Though the name implies differently, GameKit is useful for nongaming apps, too For example, you might build a social networking app that allows people to easily transfer contact information over Bluetooth CAUTION: The code in this chapter will not run in the simulator because the simulator does not support Bluetooth The only way to build and debug apps on... sent to other peers There are two basic approaches to doing this for use in GameKit The first is to use archiving and unarchiving, just as we did in the archiving section of Chapter 11 of Beginning iPhone 3 Development (Apress, 2009) With the archiving/unarchiving method, you define a class to hold a single packet of data to be sent That class will contain instance variables to hold whatever types of... and unarchiving those objects Although archiving objects is the best approach in many cases, because it is easy to implement and it fits well with the design of Cocoa Touch, there may be some cases 2 35 236 CHAPTER 8: Peer-to-Peer Over Bluetooth Using GameKit where applications need to constantly send a lot of data to their peers, and this overhead might be unacceptable In those situations, a faster... represents a client-server scenario 229 230 CHAPTER 8: Peer-to-Peer Over Bluetooth Using GameKit Figure 8–6 The client-server model features one machine acting as a server with all communications—even communications between clients—going through the server In the context of an iPhone application, a client-server setup is where one phone acts as a server and listens for other iPhones running the same program . aspects of iPhone SDK 3. 2 23 Part Further Explorations We’ve devoted six chapters to the biggest of the new APIs, but Core Data is not all that iPhone SDK 3 brings to the table for iPhone. few more advanced topics such as networking and concurrency. II 224 2 25 2 25 Chapter Peer-to-Peer Over Bluetooth Using GameKit One of the coolest new frameworks added to the iPhone 3. and Expressions 222 make it smaller, more efficient, easier to maintain, and just generally more pleasant to be around. We could go on for several more chapters about Core Data and not exhaust

Ngày đăng: 12/08/2014, 21:20

Từ khóa liên quan

Mục lục

  • Part 1: Core Data

    • Relationships, Fetched Properties, and Expressions

      • Creating the Fetched Property Attribute Controller

      • Cleaning Up Deleted Objects

      • Wonderful to the Core

      • Part 2: Further Explorations

        • Peer-to-Peer Over Bluetooth Using GameKit

          • This Chapter’s Application

          • Network Communication Models

          • The GameKit Session

          • The Peer Picker

          • Creating the Project

            • Setting Up the View Controller Header

            • Designing the Game Board

            • Creating the TicTacToePacket Object

            • Implementing the Tic-Tac-Toe View Controller

            • Trying It Out

Tài liệu cùng người dùng

Tài liệu liên quan