Objects, Allocation, Theory

Background: An NSDocument text editing based app. Each page of the document has its own textView for the main document and a textView for the header and the footer for a total of three textViews (and associated objects such as textContainers) for each page.


Currently I have it optimized to the point of being able to load a 336 page, 102K word test document in 2.83 seconds and being ready to edit text. This means that with the test document, over 1000 textViews are being allocated, initialized, and added to a view in that time span. Not bad but I can never leave well enough alone, and, as a personal challenge, I would like to get this down to less than two seconds. The ultimate goal would be to have everything loaded and ready to edit before the dock icon finishes its first bounce.


I mostly know enough about objective C to be dangerous. I am thinking one possible avenue of tinkering could be the way I allocated and use other objects such as attributed strings, arrays, dictionaries, etc.


Here is where I would like some insight.


Possible scenarios:

// scenario 1

// in a parent class

NSMutableAttributedString *aMainStringOfText;

-(void)setThingsUpForString:(NSMutableAttributedString *)theString {
  // assume aMainStringOfText has been allocated/initialized and
  // is being passed in
  [helperClass fiddleWithAttributes:theString];
}

// in a helperClass
-(void)fiddleWithAttributes:(NSMutableAttributedString *)stringToFiddleWith {
  // do some stuff with the attString
}

// scenario 2

// in a parent class

NSMutableAttributedString *aMainStringOfText;

-(void)setThingsUpForString:(NSMutableAttributedString *)theString {
  // assume aMainStringOfText has been allocated/initialized and
  // is being passed in
  theString = [helperClass fiddleWithAttributes:theString];
}

// in a helperClass
-(NSMutableAttributedString *)fiddleWithAttributes:(NSMutableAttributedString *)stringToFiddleWith {
  // do some stuff with the stringToFiddleWith
  return stringToFiddleWith;
}

// scenario 3

// in a parent class

NSMutableAttributedString *aMainStringOfText;

-(void)setThingsUpForString:(NSAttributedString *)theString {

  aMainStringOfText =  [[NSMutableAttributedString alloc] initWithAttributedString:[helperClass fiddleWithAttributes:theString]];
}

// in a helperClass
-(NSAttributedString *)fiddleWithAttributes:(NSAttributedString *)stringToFiddleWith {
  NSMutableAttributedString *tempString = [[NSMutableAttributedString alloc] initWithAttributedString:stringToFiddleWith];
  // do some stuff with the tempString
  return tempString;
}

Scenario 1 seems like the best use of resources and the most efficient. If I understand Objective C well enough, I get from this that one string object is created (aMainStringOfText) and all further fiddling happens on that string.


Scenario 2 is where my understanding gets a bit murky. What is being returned from the helper class? Is it a pointer to the original string that was passed in? And since the string that was passed in was not the actual string, but a pointer to it (is this assumption correct?), then is the helper returning a pointer to the pointer?


Scenario 3 seems like a big waste of resources. An attributed string is passed in, a mutable string is created so it can be changed, and then it is returned as an attributedString which is then allocated again as a mutable string. Wasteful? What happens to all these strings? Which one really winds up as the mainStringOfText?


So what about this:


// Scenario A

// in the main class I need to unarchive some data
  aMainStringOfText = [[NSMutableAttributedString alloc] initWithAttributedString:[helperClass stringFromData:someData]];

// in helper class
-(NSAttibutedString *)stringFromData:(NSData *)someData {
  NSAttributedString *aStringFromData = [unarchiveSomeData:someData];
  return aStringFromData;
}

// Scenario B

  aMainStringOfText = [helperClass stringFromData:someData]];

// in helper class
-(NSMutableAttributedString *)stringFromData:(NSData *)someData {
  NSMutableAttributedString *aStringFromData = [[NSMutableAttributedString alloc] initWithAttributedString:[unarchiveSomeData:someData]];
  return aStringFromData;
}

In Scenario B, who owns the mutableAttributedString? Since it is created in the helper class, isn't the helper the owner? And if the helper gets deallocated, does the string go away? From a coding clarity standpoint, Scenario B is cleaner since the helper does all the allocating and the classes that call this helper class (which can be a lot of them) don't have to do all the allocating in their code. But is this really a good choice.


These same examples could also apply to arrays and dictionaries, right? Or really any object that uses a *pointer?


1. Which of these is a better choice from a resource management standpoint?

2. Which is better from an speed standpoint?

3. Which is better from a "best practices" standpoint?

4. Other?

Replies

TMI.


But ".....over 1000 textViews are being allocated, initialized, and added to a view".


This seems oh so wrong. A UITextView takes up lots of memory. You only need 3 UITextViews; one for the header, footer and page. Store the attributed text for each page as an NSAttributedString object and place it into an NSMutableArray (and another array for footers and another headers). When you want to see page 126 then load the NSAttributedString from those arrays for page 126 (e.g. objectAtIndex:126) into those 3 UITextViews.

This is in macOS.


Anyway, with your suggestion, then when scrolling the document, I have to keep track of where it is scrolled and load/unload strings. And what if it is scrolled so that portions of two pages are visible? And what if I edit text on page 12 (add or delete)--then I have to change the strings in the array for all the pages after page 12.

Then 6 UITextFields or, at most, 9 (i.e. the page before and the page after). And as you scroll up, or down, you swap the UITextFields (and .text) so that the middle textView is always showing in the center-most position of the screen and the page before and after is ready to be scrolled into view.


You will find that with a little bit of computing you will solve your memory and timing problems. And you will have no need of downloading anything but, at most, a few pages forwarded and back - so that will solve a downloading problem.

38 meg of memory with a one page document. 66 meg of memory with the 336 page document. I think that aspect stays as is--it works quite well. I don't have memory or timing problems. I am looking to optimize an app that already performs well and want to take it to an even higher level of performance.

For what it's worth, I agree with PBK. The question you originally asked is about stuff that's almost certainly neglible. It's certainly not going to get you a 29% decrease in startup time. Fundamental design issues like reusing views instead of creating all of the (usually invisible) views for a long document is far more important. In any case, the way to figure out performance questions is to measure using Instruments or similar.


Also, by the way, the code in your scenario 2 isn't feasible. The assignment to the parameter variable doesn't affect the caller's variable. You're assigning to a copy of the caller's pointer variable, not the caller's pointer variable.

Take a look at CATiledLayer. Usually people think of this for images, but it is same concept. You only want to render what is on screen, plus a little overlap to have smooth scrolling.