iOS - Not allocating too much memory at once

2019-07-20 17:39发布

问题:

Trying to get around a crash that is happening on some iOS devices, in conjunction with advice from Apple to "not cause allocation spikes". How can I change this code to not happen all at once?

for (Item *item in self.items) {
        ItemView *itemView = [[ItemView alloc] initWithFrame:CGRectMake(xPos, kYItemOffsetIphone, kItemWidthIphone, kItemHeightIphone) ];

        itemView.delegate = self;
        [itemView layoutWithData:item]; //this just adds an imageView and button
        [self.scrollView addSubview:itemView];
        xPos += kXItemSpacingIphone;
    }

There are around 20 objects in the self.items array, which are used to build the 20 ItemViews. Again, is there some way to make this code less "allocation intensive"?

回答1:

I personally do something along the lines of:

  1. Make my view controller the delegate of the scroll view (if you do this in code, you have to modify your view controller's .h to say that it conforms to UIScrollViewDelegate).

  2. Define a scrollViewDidScroll method that (a) determines the frame of the visible portion of the scroll view; (b) determine which of the subviews intersect with that visible portion; (c) load the items that are visible, and unload the ones that aren't.

So, for example, it might look something like the following:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    // Determine the frame of the visible portion of the scrollview.

    CGRect visibleScrollViewFrame = scrollView.bounds;
    visibleScrollViewFrame.origin = scrollView.contentOffset;

    // Now iterate through the various items, remove the ones that are not visible,
    // and show the ones that are.

    for (Item *itemObject in self.itemCollection)
    {
        // Determine the frame within the scrollview that the object does (or 
        // should) occupy.

        CGRect itemObjectFrame = [self getItemObjectFrame:itemObject];

        // see if those two frames intersect

        if (CGRectIntersectsRect(visibleScrollViewFrame, itemObjectFrame))
        {
            // If it's visible, then load it (if it's not already).
            // Personally, I have my object have a boolean property that
            // tells me whether it's loaded or not. You can do this any
            // way you want.

            if (!itemObject.loaded)
                [itemObject loadItem];
        }
        else
        {
            // If not, go ahead and unload it (if it's loaded) to conserve memory.

            if (itemObject.loaded)
                [itemObject unloadItem];
        }
    }
}

That's the basic idea. You can certainly optimize this logic based upon your app's particular design, but this is how I generally do it.