How to observe ABPersonView changes for ABPerson - macos

I know that ABPersonView is not KVO complaint. My issue is that despite declared property of ABPersonView being retained every time I access the property I get different object. Am I doing something wrong or is this correct that every time there was a change in ABPersonView I have to update model with new ABPerson object? Using El Capitan GM.
ABPersonView:
#property (readwrite, retain) ABPerson *person;
// An ABPerson record for display.
// Raises if person originates from ABAddressBook's +sharedAddressBook.
// Person must be exist in an ABAddressBook created and manipulated on the main thread only.
// When person is nil, displays an empty selection state.
Code:
#import "AppDelegate.h"
#import AddressBook;
static void * ABPersonVCContext = &ABPersonVCContext;
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#property (strong) ABPerson *person;
#property (strong) ABPersonView *personView;
#property (strong) ABAddressBook *book;
#property (assign, getter=isEditing) BOOL editing;
#property NSTimer *timer;
#end
#implementation AppDelegate
- (instancetype)init {
self = [super init];
if (self) {
_book = [[ABAddressBook alloc] init];
NSString *vCardRepresentation = #"BEGIN:VCARD\r\nVERSION:3.0\r\nN:AA;BB;;;\r\nFN:\r\nEND:VCARD\r\n";
NSData *vCardData = [vCardRepresentation dataUsingEncoding:NSUTF8StringEncoding];
_person = [[ABPerson alloc] initWithVCardRepresentation:vCardData];
[_book addRecord:_person];
[self addObserver:self forKeyPath:#"editing"
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
context:ABPersonVCContext];
#ifdef DEBUG
NSLog(#"%s %d %s", __FILE__, __LINE__, __PRETTY_FUNCTION__);
NSLog(#"%#",_person);
#endif
}
return self;
}
- (void)awakeFromNib
{
self.personView = [[ABPersonView alloc] initWithFrame:self.window.contentView.frame];
self.personView.person = self.person;
[self.window.contentView addSubview:self.personView];
self.timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(reverseEditing) userInfo:NULL repeats:YES];
[self.timer fire];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (context == ABPersonVCContext) {
if ([keyPath isEqualTo:#"editing"]) {
#ifdef DEBUG
NSLog(#"%s %d %s", __FILE__, __LINE__, __PRETTY_FUNCTION__);
NSLog(#"%#",self.personView.person);
#endif
}
} else {
#try {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
#catch (NSException *exception) {
;
}
#finally {
;
}
}
}
- (void)reverseEditing
{
self.editing = !self.editing;
}
#end
EDIT:
The new object comes from different addressBook instance:
(lldb) po [newPerson addressBook]
<ABAddressBook: 0x6080000d50e0>
(lldb) po self.book
<ABAddressBook: 0x6080000c4130>
(lldb) po [self.person addressBook]
<ABAddressBook: 0x6080000c4130>
EDIT2:
Even registering for notifications does not help because different object is being modified.
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:#selector(changeOccured:) name:kABDatabaseChangedNotification object:nil];
[nc addObserver:self selector:#selector(changeOccured:) name:kABDatabaseChangedExternallyNotification object:nil];

Unfortunately every call to person property of personView triggers ABPersonViewAPIAdapter that converts CNContact to ABPerson. So if one doesn't want to use CNContact on El Capitan he has to propagate edited ABPerson back to the model object.
One can try following code (hope this will save some time to someone)
NSLog(#"%#",[self.personView performSelector:#selector(addressBook) withObject:nil]);
NSLog(#"%#",[self.personView performSelector:#selector(_APIAdapter) withObject:nil]);
NSLog(#"%#",[self.personView performSelector:#selector(_contact) withObject:nil]);

Related

showsUserLocation action attached to button not working

I am having problems with a map view in my app. I have created a button that when clicked should show users location on the map, but nothing happens (no error messages occur).
I believe the issue may lie in the way I've written the delegates. The code from the relevant .h and .m files is below:
mapViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>
#interface mapViewController : UIViewController {
MKMapView *mapview;
}
#property (nonatomic, retain) IBOutlet MKMapView *mapview;
-(IBAction)setMap:(id)sender;
-(IBAction)getCurrentLocation:(id)sender;
#property (nonatomic, retain) IBOutlet CLLocationManager *locationManager;
#end
mapViewController.m
#import "mapViewController.h"
#interface mapViewController ()
#end
#implementation mapViewController {
CLLocationManager *locationManager;
}
#synthesize mapview;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.locationManager.delegate=self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.requestWhenInUseAuthorization;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(IBAction)setMap:(id)sender {
switch (((UISegmentedControl *) sender).selectedSegmentIndex) {
case 0:
mapview.mapType = MKMapTypeStandard;
break;
case 1:
mapview.mapType = MKMapTypeSatellite;
break;
case 2:
mapview.mapType = MKMapTypeHybrid;
break;
default:
break;
}
}
-(IBAction)getCurrentLocation:(id)sender {
mapview.showsUserLocation = YES;
}
#end
Any help would be greatly appreciated, thanks
You have to implement the MapKit delegates. (Make sure you add the delegate signature in .h of your view controller)
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
MKCoordinateRegion region;
MKCoordinateSpan span;
span.latitudeDelta = 0.005;
span.longitudeDelta = 0.005;
CLLocationCoordinate2D location;
location.latitude = userLocation.coordinate.latitude;
location.longitude = userLocation.coordinate.longitude;
region.span = span;
region.center = location;
[mapView setRegion:region animated:YES];
}
Extra: To handle App is on foreground or not:
We add 2 event observers to observe the App is entering background / returning active:
- (void)viewDidLoad{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appToBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appReturnsActive) name:UIApplicationDidBecomeActiveNotification object:nil];
}
- (void)appToBackground{
[mapview setShowsUserLocation:NO];
}
- (void)appReturnsActive{
[mapview setShowsUserLocation:YES];
}

ProgressView/ ProgressBar Xcode

I was wondering how to set the progress bar equal to max user input in xcode.
#property (weak, nonatomic) IBOutlet UIProgressView *progressBar;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_inputAmount.keyboardType = UIKeyboardTypeDecimalPad;
self.amount = [NSMutableArray new];
[self.amount addObject:#"Total Amount of Push-Ups:"];
[self.myList setDataSource:self];
_inputAmount = UIProgressViewStyleBar;
}
This is just a snipet of my code, the inputamount = progress bar is what I want to do, but I'm not really sure how to do it. I want the greatest input amount the be equal to the progressviewbar as well. So i would have to compare all the #s that were added to the array. Any ideas? Thanks!
//
// ViewController.m
// Push Up Tracker
//
// Created by Paul Lesny on 10/26/14.
// Copyright (c) 2014 Paul Lesny. All rights reserved.
//
#import "ViewController.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UITextField *inputAmount;
#property (weak, nonatomic) IBOutlet UIButton *addButton;
#property NSMutableArray *amount;
#property (weak, nonatomic) IBOutlet UITableView *myList;
#property (weak, nonatomic) IBOutlet UIProgressView *progressBar;
#end
#implementation ViewController
//#synthesize progressBar, progressValue;
///NSInteger stringToInt(NSString *string) {
//return [string integerValue];
//}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_inputAmount.keyboardType = UIKeyboardTypeDecimalPad;
self.amount = [NSMutableArray new];
[self.myList setDataSource:self];
[self readDataFromFile:#"lalala1"];
if (_amount.count!=0)
{
NSMutableDictionary *max=[_amount objectAtIndex:0];
NSString *m = max[#"max"];
NSLog (#"%#",m);
NSInteger num=[m intValue];
NSLog(#"amount is not empty");
_progressBar.progress = (float) [m intValue]/50;
}
else
{
_progressBar.progress=0;
}
//[self.amount addObject:#"0"];
//[self.myList reloadData];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return[self.amount count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString * cellId = #"pancake";//identifier for the cells
// get a cell from the cache
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:cellId];
// if the cell is not cached then
if(cell ==nil)
{
// create a new cell
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
}
NSMutableDictionary *newEntry=_amount[indexPath.row];
[cell.textLabel setText:newEntry[#"pushUpNum"]];
// put the corresponding element of the array as text.
// return the cell.
return cell;
}
-(BOOL) textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
-(void) readDataFromFile:(NSString*)fileName
{
NSData *fileData=[NSData dataWithContentsOfURL:[self urlOfEntries:#"lalala1"]];
if(fileData!=nil)
{
_amount=[NSPropertyListSerialization
propertyListWithData:fileData options:
NSPropertyListMutableContainers format:nil error:nil];
}
else
{
_amount=[NSMutableArray new];
}
}
-(NSURL *) urlOfEntries:(NSString*)name
{
NSURL *docDirectory=[[[NSFileManager defaultManager]
URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]lastObject];
NSURL* fullPath=[docDirectory URLByAppendingPathComponent:name];
return fullPath;
}
-(void) savePushUpTotal:(NSMutableArray *)myEntries
{
[myEntries writeToURL:[self urlOfEntries:#"lalala1"]atomically:YES];
}
- (IBAction)sender:(id)addButton
{
NSMutableDictionary *newRecord = [[NSMutableDictionary alloc]init];
[newRecord setObject:self.inputAmount.text forKey:#"pushUpNum"];
if (_amount.count==0)
{
[newRecord setObject:#"0" forKey:#"max"];
}
[self.amount addObject:newRecord];
[self savePushUpTotal:_amount];
[self.myList reloadData];
[self.inputAmount resignFirstResponder];
[self savePushUpTotal:_amount];
NSMutableDictionary *max=_amount[0];
NSString *m = max[#"max"];
NSInteger num=[m intValue];
NSMutableDictionary *newDictionary=_amount[_amount.count-1];
NSString *blah = newDictionary[#"pushUpNum"];
NSInteger number=[blah intValue];
NSLog(#"dadala%#",blah);
if (_amount.count!=1)
{
if (number>num)
{
_progressBar.progress= (float)number/50;
NSString *pushUp = max[#"pushUpNum"];
NSMutableDictionary *maxPush = [[NSMutableDictionary alloc]init];
[maxPush setObject:pushUp forKey:#"pushUpNum"];
[maxPush setObject:blah forKey:#"max"];
[_amount removeObjectAtIndex:(NSUInteger)0];
[_amount insertObject:maxPush atIndex:0];
[self savePushUpTotal:_amount];
}
}
else
{
_progressBar.progress = (float)number/50;
}
}
- (IBAction)clear:(UIButton *)sender
{
_progressBar.progress=0;
[_amount removeAllObjects];
[[NSUserDefaults standardUserDefaults] setObject:_amount forKey:0];
[_myList reloadData];
}
#end

How are dependent keys registered? (Key-Value Observing)

I have created a simple Cocoa Application. In MainMenu.xib I have added an NSDatePicker and an NSTextField. Both of these objects have Value bindings to properties of the App Delegate. I expect that when the user changes the date in the NSDatePicker, the NSTextField will be updated. This isn't happening. Here is the App Delegate:
// AppDelegate.h
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (assign) IBOutlet NSWindow *window;
#property (nonatomic, strong) NSDate *dateFromPicker;
#property (nonatomic, readonly) NSString *dateString;
#end
// AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
}
- (NSString *)dateString
{
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
return [formatter stringFromDate:self.dateFromPicker];
}
+ (NSSet *)keyPathsForValuesAffectingDateString
{
return [NSSet setWithObject:#"dateFromPicker"];
}
#end
Updated code with an observer for dateFromPicker and some NSLog statements:
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[self addObserver:self forKeyPath:#"dateFromPicker" options:0 context:NULL];
self.dateFromPicker = [NSDate dateWithNaturalLanguageString:#"12/12/12"];
}
- (NSString *)dateString
{
NSLog(#"dateString was called.");
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
return [formatter stringFromDate:self.dateFromPicker];
}
+ (NSSet *)keyPathsForValuesAffectingDateString
{
NSLog(#"keyPathsForValuesAffectingDateString was called.");
return [NSSet setWithObject:#"dateFromPicker"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change: (NSDictionary *)change context:(void *)context
{
NSLog(#"dateFromPicker changed.");
}
#end
Here is the log:
2012-08-10 15:37:15.086 ... keyPathsForValuesAffectingDateString was called.
2012-08-10 15:37:15.087 ... dateString was called.
2012-08-10 15:37:15.116 ... dateFromPicker changed.
2012-08-10 15:37:15.117 ... dateString was called.
2012-08-10 15:37:19.831 ... dateFromPicker changed.
2012-08-10 15:37:19.831 ... dateString was called.
I know this is not exactly what you were asking, but ...
Why don't you bind the NSTextField to the NSDate property and add a formatter to it in Interface Builder?

xCode Console Errors when recording Audio

I get the following console errors when recording a sound.. Any ideas what I'm doing wrong? The recordings work, except that the outputs are REALLY soft.
TIA
2011-04-17 12:51:25.707 FlashCards[18561:1210f] Cannot find executable for CFBundle/CFPlugIn 0x5a64780 </Library/Audio/Plug-Ins/HAL/DVCPROHDAudio.plugin> (not loaded)
2011-04-17 12:51:25.708 FlashCards[18561:1210f] Cannot find function pointer NewPlugIn for factory C5A4CE5B-0BB8-11D8-9D75-0003939615B6 in CFBundle/CFPlugIn 0x5a64780 </Library/Audio/Plug-Ins/HAL/DVCPROHDAudio.plugin> (not loaded)
2011-04-17 12:51:25.712 FlashCards[18561:1210f] Cannot find executable for CFBundle/CFPlugIn 0x5c69e90 </Library/Audio/Plug-Ins/HAL/iSightAudio.plugin> (not loaded)
2011-04-17 12:51:25.713 FlashCards[18561:1210f] Cannot find function pointer iSightAudioNewPlugIn for factory 9BE7661E-8AEF-11D7-8692-000A959F49B0 in CFBundle/CFPlugIn 0x5c69e90 </Library/Audio/Plug-Ins/HAL/iSightAudio.plugin> (not loaded)
2011-04-17 12:51:25.729 FlashCards[18561:c503] start recording
As requested, I am adding code:
.h file snippet:
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <CoreAudio/CoreAudioTypes.h>
#import <AudioToolbox/AudioToolbox.h>
#protocol BackViewControllerDelegate;
#interface BackViewController : UIViewController <UITextViewDelegate, AVAudioRecorderDelegate, AVAudioPlayerDelegate, UIAlertViewDelegate>
{
AVAudioRecorder *audioRecorder;
AVAudioPlayer *audioPlayer;
IBOutlet UIButton *playButton;
IBOutlet UIButton *recordButton;
IBOutlet UIActivityIndicatorView *autoCog;
BOOL toggle;
}
#property (nonatomic, retain) IBOutlet UIButton *playButton;
#property (nonatomic, retain) IBOutlet UIButton *recordButton;
#property (nonatomic, retain) IBOutlet UIActivityIndicatorView *autoCog;
-(IBAction) recordAudio;
-(IBAction) playAudio;
.m snippet
#synthesize playButton;
#synthesize recordButton;
#synthesize autoCog;
- (void)viewWillAppear:(BOOL)animated {
NSLog(#"%s", __FUNCTION__);
[super viewWillAppear:animated];
//Start the toggle in false mode. PREMISE: WHEN WE GET HERE FIRST, WE ARE NOT RECORDING
toggle = NO;
NSError *error = nil;
//Instantiate an instance of the AVAudioSession object.
AVAudioSession * audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error: &error];
//Activate the session
[audioSession setActive:YES error: &error];
}
-(void) playAudio
{
NSLog(#"%s", __FUNCTION__);
if (audioPlayer.playing) {
[audioPlayer stop];
}
if (toggle == NO)
{
recordButton.enabled = NO;
if (audioPlayer)
[audioPlayer release];
NSError *error;
// GET THE APPROPRIATE SOUND FILE NAME
.....
//CHECK FOR EXISTING SOUNDFILE
if (![[NSFileManager defaultManager] fileExistsAtPath:soundFilePath])
{
UIAlertView *someError = [[UIAlertView alloc] initWithTitle: #"Oops!" message: #"There is not a sound for this word. Press REC to record one. Press cancel to stop" delegate: self
cancelButtonTitle: #"REC" otherButtonTitles:#"CANCEL", nil];
[someError show];
[someError release];
}
NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];
AudioSessionSetProperty (
kAudioSessionProperty_OverrideCategoryDefaultToSpeaker,
sizeof (doChangeDefaultRoute),
&doChangeDefaultRoute
);
audioPlayer = [[AVAudioPlayer alloc]
initWithContentsOfURL:soundFileURL
error:&error];
audioPlayer.volume = 1.0;
audioPlayer.delegate = self;
if (error){
NSLog(#"Error: %#",
[error localizedDescription]);
}
else
{
[audioPlayer play];
}
}
recordButton.enabled = YES;
//NSLog(#"end of playAudio");
}
I've seen these warnings before, what happens is you copied another older project? if so this is what i did, I simply created a new blank project with the newest version of Xcode, and then started copying the old files into it.
this seemed to clear out those errors.

objectAtIndex - Message sent to deallocated instance

I am having a real problem finding where my problem is in my search controller. This is a table view with search bar and search display controller. It used to work fine, but all the sudden it stopped working. I turned on NSZombieEnabled and it shows that my NSArray called searchDataSource is the zombie.
When you type a search term the "shouldReloadTableForSearchTerm" executes the handleSearchForTerm function. The handleSearchForTerm" function accesses my ProductInfo class that query a SQLite database and returns the query results. Those results are then placed in my searchDataSource Array. Everything appears to work fine there. However, once I get to the "cellForRowAtIndexPath" function and I try to load the cells from the searchDataSource, that is when I run in to the problem of the Array having been deallocated.
Here is my code for the search controller:
//
// SearchViewController.h
// Priority Wire
//
// Created by Keith Yohn on 2/2/11.
// Copyright 2011 Priority Wire & Cable. All rights reserved.
//
#import <UIKit/UIKit.h>
#interface FourthViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UISearchDisplayDelegate, UISearchBarDelegate> {
UITableView *mainTableView;
NSArray *searchDataSource;
NSMutableArray *contentsList;
NSMutableArray *searchResults;
NSString *savedSearchTerm;
NSString *webURL;
}
#property (nonatomic, retain) IBOutlet UITableView *mainTableView;
#property (nonatomic, retain) IBOutlet NSArray *searchDataSource;
#property (nonatomic, retain) NSMutableArray *contentsList;
#property (nonatomic, retain) NSMutableArray *searchResults;
#property (nonatomic, copy) NSString *savedSearchTerm;
#property (nonatomic, retain) NSString *webURL;
- (void)handleSearchForTerm:(NSString *)searchTerm;
#end
SearchViewController.m
//
// SearchViewController.m
// Priority Wire
//
// Created by Keith Yohn on 2/2/11.
// Copyright 2011 Priority Wire & Cable. All rights reserved.
//
#import "FourthViewController.h"
#import "ProductsDatabase.h"
#import "ProductInfo.h"
#import "WebViewController.h"
#implementation FourthViewController
#synthesize mainTableView;
#synthesize searchDataSource;
#synthesize contentsList;
#synthesize searchResults;
#synthesize savedSearchTerm;
#synthesize webURL;
- (void)viewDidLoad {
[super viewDidLoad];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.searchDataSource count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
// Set up the cell...
ProductInfo *info = [searchDataSource objectAtIndex:indexPath.row]; //This is where I get the 'message sent to deallocated instance' message.
[cell.textLabel setText:info.sName];
[cell.detailTextLabel setText:info.sType];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
ProductInfo *info = [searchDataSource objectAtIndex:indexPath.row];
webURL = [NSString stringWithFormat:#"http://www.prioritywire.com/specs/%#", info.sFile];
WebViewController *wvController = [[WebViewController alloc] initWithNibName:#"WebViewController" bundle:[NSBundle mainBundle]];
wvController.URL = webURL;
wvController.navTitle = #"Spec Sheet";
[self.navigationController pushViewController:wvController animated:YES];
[wvController release];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Relinquish ownership any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Save the state of the search UI so that it can be restored if the view is re-created.
[self setSavedSearchTerm:[[[self searchDisplayController] searchBar] text]];
[self setSearchResults:nil];
}
- (void)dealloc {
[searchDataSource release], searchDataSource = nil;
[mainTableView release];
[contentsList release];
[searchResults release];
[savedSearchTerm release];
[super dealloc];
}
- (void)handleSearchForTerm:(NSString *)searchTerm
{
[self setSavedSearchTerm:searchTerm];
if ([self searchResults] == nil)
{
NSMutableArray *array = [[NSMutableArray alloc] init];
[self setSearchResults:array];
[array release], array = nil;
} else {
NSArray *productInfo = [[ProductsDatabase database] searchListing:searchTerm];
self.searchDataSource = productInfo;
[self.mainTableView reloadData];
[productInfo release];
}
[[self searchResults] removeAllObjects];
if ([[self savedSearchTerm] length] != 0)
{
for (NSString *currentString in [self contentsList])
{
if ([currentString rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound)
{
[[self searchResults] addObject:currentString];
}
}
}
}
#pragma mark -
#pragma mark UISearchDisplayController Delegate Methods
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self handleSearchForTerm:searchString];
// Return YES to cause the search result table view to be reloaded.
return YES;
}
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
[self setSavedSearchTerm:nil];
self.searchDataSource = nil;
[self.mainTableView reloadData];
}
#end
I am quite new to objective-C and can't understand what I did wrong. I have spent days trying to figure this out and have had no luck. I would appreciate any help anyone can offer.
Keith
This bit of code seems to be the only place searchDataSource gets set:
NSArray *productInfo = [[ProductsDatabase database] searchListing:searchTerm];
self.searchDataSource = productInfo;
[self.mainTableView reloadData];
[productInfo release];
If ProductsDatabase follows the rules, you don't own the returned array (i.e. it is already autoreleased) so the release on the fourth line is incorrect.
Don't you mean to use your searchResults-array instead of your searchDataSource, because in handleSearchForTerm: you are adding the results to it. Why do you even have the searchResult ivar? It's only used in handleSearchForTerm:, maybe some mixup there?

Resources