1

Im pretty new in programming and im currently looking at blocks which im having some issues with. Im having a hard time reaching an object which is defined within my block. Actually I cannot really explain this issue in an accurate way, so ill give you some code and pictures, and hope that it makes sense and that you excuse my dodgy knowledge and try to help me understand the problem.

So Im starting off by running this block

- (void)fetchSite
{
    //Initiate the request...
    [[FeedStore sharedStore] fetchSites:^(RSSChannel *objSite, NSError *err) {
        //When the request completes, this block will be called
        if (!err) {
            //If everything went ok, grab the object
            channelSite = objSite;

            //Now we need to extract the first siteId and give it to sharedstore
            RSSItem *site = [[channelSite sites] objectAtIndex:0];
            NSString *currentSiteId = [NSString stringWithFormat:@"%@", [site siteNumber]];
            NSLog(@"DEBUG: LVC: fetchSite: currentSiteId: %@", currentSiteId);

            [[FeedStore sharedStore] setCurrentSiteId:currentSiteId];

            //Now we can call fetchEntries
            [self fetchEntries];
        } else {
            //If things went bad, show an alart view
            UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"Fel" message:@"Kunde inte bedömma din position relaterat till närmaste hållplats, försök gärna igen" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
            [av show];

            //Stop UIRefreshControl
            [refreshControl endRefreshing];
        }
    }];
}

Then fetchEntries is called:

- (void)fetchEntries
{   
    void (^completionBlock)(RSSChannel *obj, NSError *err) = ^(RSSChannel *obj, NSError *err) {
        if (!err) {
            //If everything went ok, grab the object, and reload the table and end refreshControl
            channel = obj;

            //Post notification to refreshTableView method which will reload tableViews data.
            [[NSNotificationCenter defaultCenter] postNotificationName:@"RefreshTableView" object:nil];

            //Stop UIRefreshControl
            [refreshControl endRefreshing];
        } else {
            //If things went bad, show an alart view
            UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"Fel" message:@"Kunde inte hämta avgångar för din hållplats, försök gärna igen" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
            [av show];

            //Stop UIRefreshControl
            [refreshControl endRefreshing];
        }
    };
    //Initiate the request...
    if (fetchType == ListViewControllerFetchBus) {
        [[FeedStore sharedStore] fetchDepartures:completionBlock];
        selectedSegment = 0;
    } else {
        [[FeedStore sharedStore] fetchDeparturesMetro:completionBlock];
        selectedSegment = 1;
    }
}

Reloading the table:

- (void)refreshTableView:(NSNotification *)notif
{
    [[self tableView] reloadData];
}

Heres where I populate my custom table cell:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    //Get received data from fetchEntries block
    RSSItem *item = [[channel items] objectAtIndex:[indexPath row]];

    //Get the new or recycled cell
    ItemCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ItemCell"];

    //Set the apperance
    [[cell lineNumberLabel] setTextColor:[UIColor whiteColor]];
    [[cell lineNumberLabel] minimumScaleFactor];
    [[cell lineNumberLabel] setFont:[UIFont boldSystemFontOfSize:22]];
    [[cell lineNumberLabel] adjustsFontSizeToFitWidth];
    [[cell lineNumberLabel] setTextAlignment:NSTextAlignmentCenter];

    [[cell destinationLabel] setTextColor:[UIColor whiteColor]];

    [[cell displayTimeLabel] setTextColor:[UIColor whiteColor]];
    [[cell displayTimeLabel] setTextAlignment:NSTextAlignmentLeft];

    //Configure the cell with the item
    if ([item lineNumber]) [[cell lineNumberLabel] setText:[item lineNumber]];
    if ([item destination]) [[cell destinationLabel] setText:[item destination]];
    if ([item displayTime]) [[cell displayTimeLabel] setText:[item displayTime]];
    if ([item displayRow1]) [[cell destinationLabel] setText:[item displayRow1]];

    return cell;
}

So when im running through these blocks in normal state everything works like a charm.

But the issues starts when Im calling fetchSite method from an UIActionSheet, or actually from an UISegmentedControl which is a subview of UIActionSheet, this takes place here:

- (void)displayPickerView
{
    //First im creating an instance to PickerViewController which will handle the UIPickerView which is a subview of UIActionSheet. pickerArrayFromChannel holds entries for UIPickerView to show in its ViewController.
    pickerViewController = [[PickerViewController alloc] init];
    [pickerViewController initWithItems: pickerArrayFromChannel];
    pickerViewController.delegate = self;

    //initiate UIActionSheet which needs no delegate or appearance more than its style
    actionSheet = [[UIActionSheet alloc] initWithTitle:nil
                                              delegate:nil
                                     cancelButtonTitle:nil
                                destructiveButtonTitle:nil
                                     otherButtonTitles:nil];
    [actionSheet setActionSheetStyle:UIActionSheetStyleBlackTranslucent];

    //Create frame and initiate a UIPickerView with this. Set its delegate to the PickerViewController and set it to start at row 0
    CGRect pickerFrame = CGRectMake(0, 40, 0, 0);
    UIPickerView *pickerView = [[UIPickerView alloc] initWithFrame:pickerFrame];
    pickerView.showsSelectionIndicator = YES;
    pickerView.delegate = pickerViewController;
    pickerView.dataSource = pickerViewController;
    [pickerView selectRow:0 inComponent:0 animated:YES];

    //Add this pickerView as subview to actionSheet
    [actionSheet addSubview:pickerView];

    //Now, create two buttons, closeButton and okButton. Which really are UISegmentedControls. Action to closeButton is (for now) dismissActionSheet which holds only following code " [actionSheet dismissWithClickedButtonIndex:0 animated:YES]; " and works fine
    UISegmentedControl *closeButton = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObject:@"Stäng"]];
    closeButton.momentary = YES;
    closeButton.frame = CGRectMake(260, 7.0f, 50.0f, 30.0f);
    closeButton.segmentedControlStyle = UISegmentedControlStyleBar;
    closeButton.tintColor = [UIColor blackColor];
    [closeButton addTarget:self action:@selector(dismissActionSheet) forControlEvents:UIControlEventValueChanged];
    [actionSheet addSubview:closeButton];

    //OK button
    UISegmentedControl *okButton = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObject:@"Visa avgångar"]];
    okButton.momentary = YES;
    okButton.frame = CGRectMake(10, 7.0f, 100.0f, 30.0f);
    okButton.segmentedControlStyle = UISegmentedControlStyleBar;
    okButton.tintColor = [UIColor blueColor];
    [okButton addTarget:self action:@selector(updateDeparturesFromActionSheet) forControlEvents:UIControlEventValueChanged];
    [actionSheet addSubview:okButton];

    //Now show actionSheet
    [actionSheet showInView:[[UIApplication sharedApplication] keyWindow]];
    [actionSheet setBounds:CGRectMake(0, 0, 320, 485)];
}

Now lets look at updateDeparturesFromActionSheet method and we're getting close to where my issues begin (if your still reading, thank you for that

- (void)updateDeparturesFromActionSheet
{
    //Dismiss the actionSheet
    [actionSheet dismissWithClickedButtonIndex:0 animated:YES];

    //Send the selected entry from UIPickerView to currentAddress property in FeedStore, fetchSite method will eventually use this
    [[FeedStore sharedStore] setCurrentAddress:[pickerViewController addressFromPickerView]];

    //Call fetchSite method
    [self fetchSite];
}

fetchSite then eventually calls fetchEntries method, which eventually populate my table. I store received obj (from block) in an property called channel

void (^completionBlock)(RSSChannel *obj, NSError *err) = ^(RSSChannel *obj, NSError *err) {
if (!err) {
//If everything went ok, grab the object, and reload the table and end refreshControl
            channel = obj;

The channel object looks like this

channel             RSSChannel *        0x07464340
NSObject                NSObject    
currentString           NSMutableString *   0x00000000
lvc                 ListViewController *    0x08c455d0
items               NSMutableArray *    0x08c452d0
sites                   NSMutableArray *    0x08c452f0
parentParserDelegate    id              0x00000000
pickerViewArray     NSMutableArray *    0x08c45320

As you can see when I populate my tables cells, I use the info from items inside of the channel object.

Now if your still with me (and havnt fallen asleep) so far I will explain my actual problem, now when you have (hopefully) all the relevant code.

So when I press okButton in my UIActionSheet, updateDeparturesFromActionSheet method gets called. Which calls fetchSite which eventually calls fetchEntries and so far so good, these blocks performs and I get back information. But when I grab fetchEntries obj and put it in channel, it doesnt seems to "update" this object with the new information grabbed by the blocks obj object. Among others it does not seems that channel object gets a new place in the memory (it keeps 0x07464340). My first thought was to make sure channel object gets released by ARC, by removing all owners of that object, but it seems that even if I do so (and then doublecheck that its null with simple NSLog(@"%@") it keeps getting its old values and memory reference back when I trigger the "update".

After many attempts to release the channel object and doing all sorts of stuff (creating new arrays (outside of block) amongst other things). I keep thinking that some special rule within blocks that I dont understand is messing with me. Any ideas?

Please let me know if anything isnt clear because I've expressed myself bad, its hard to explain an issue that you dont understand yourself. Thanks in advanced.

maroux
  • 3,764
  • 3
  • 23
  • 32
mar-schmidt
  • 394
  • 4
  • 11
  • question has nothing to do with XCode. – maroux May 01 '13 at 06:59
  • Show the property declaration of channel. Also, you should use `self.channel = ...` instead of directly accessing the ivar. Add a breakpoint at `channel = obj` and check if the completion block is being called and that the value of channel is correct. Make sure `refreshTableView` is called *after* the completion block where you update `channel`. – maroux May 01 '13 at 07:01
  • @Mar0ux The property of channel is: `@property (nonatomic, strong) RSSChannel *channel;` **obj** initially memory space is `0x089653d0`. Channel gets `0x089653d0` and tableView starts to fetch and display its _items_. But when `-(void)fetchSite` is triggered from `-(void)updateDeparturesFromActionSheet` and eventually `-(void)fetchEntries` block is received, **obj** gets new memory space and includes correct New data `obj RSSChannel * 0x08960710`. How ever my breakpoint inside of cellForRowAtIndexPath displays my channel as the old one `channel RSSChannel * 0x089653d0` with old data in it. – mar-schmidt May 01 '13 at 11:52
  • Are you sure `channel = obj` is being executed? What's the value of channel right after that line? – maroux May 01 '13 at 11:54
  • You can add a breakpoint on `@property (nonatomic, strong) RSSChannel *channel;` line - it will stop for every access of `self.channel`. If `channel = obj` is being executed and `channel` is updated, check who's overriding it's value. – maroux May 01 '13 at 11:55
  • @Mar0ux actually it seems that my **channel** object always gets updated with new data from blocks **obj** `0x08960710`. But cellForRowAtIndexPath seems to work with the old channel `0x089653d0`. I've tried to clear old channel but tableView seems to hold on to it with iron hands – mar-schmidt May 01 '13 at 12:02
  • You do realize that makes absolutely no sense :) `channel` is class ivar and can only refer to one value at a time. Someone is overriding the value. My last comment should guide you through working this out. Good luck! – maroux May 01 '13 at 12:05
  • @Mar0ux maybe so :) thanks for your help. Next text is for others willing to help. Listen to this, if I set channel to `nil` at the end of `cellForRowAtIndexPath`. Reference to `channel` gets removed and eventually nothing will be displayed in my table. But when I then run the `- (void)updateDeparturesFromActionSheet` method, the blocks are working and `channel` eventually gets populated in `fetchEntries` block, and next step is to reloadData of table, then `cellForRowAtIndexPath` never gets called this time, as wierd as it sound but it seems because reference to old `channel` is gone. Why? :) – mar-schmidt May 01 '13 at 12:15

0 Answers0