2

I have some problem with the code below. When lines gets over 10000 the app will crash basically, or at least not respond or do anything.

The label.text can output the whole text file whithout problem. And there is no problem to store the whole 25000 line file in textArr -array.

But when trying to do the while-loop for a file larger than 10000 lines it just won't respond.

Any suggestions how to solve this? :-)

    let path = NSBundle.mainBundle().pathForResource("thefile", ofType: "log")
    var text = String(contentsOfFile: path!, encoding: NSUTF8StringEncoding, error: nil)!
    var textArr = text.componentsSeparatedByString("\r\n")
    var lines = textArr.count

    var user_event=1
    var user_state=1
    var user_info=1
    var user_devcom=1
    var user_warning=1
    var user_io=1
    var user_useract=1
    var user_error=1

    class ViewController2: UIViewController {

     override func viewDidLoad() {

            label.font = UIFont(name: label.font.fontName, size: 8)

            super.viewDidLoad()

            var n=0

            while n < lines{

                var rad: String = textArr[n]

                if user_event == 1{
                   if rad.lowercaseString.rangeOfString("  event ") != nil{
                        label.text = "\(label.text!)\(rad)"
                        label.text! += "\r\r"
                    }
                }

                if user_state == 1{
                    if rad.lowercaseString.rangeOfString("  state ") != nil{
                        label.text = "\(label.text!)\(rad)"
                        label.text! += "\r\r"
                    }

                }

                if user_info == 1{
                    if rad.lowercaseString.rangeOfString("  info ") != nil{
                        label.text = "\(label.text!)\(rad)"
                        label.text! += "\r\r"

                    }
                }

                if user_devcom == 1{
                    if rad.lowercaseString.rangeOfString("  devcom ") != nil{
                        label.text = "\(label.text!)\(rad)"
                        label.text! += "\r\r"
                    }
                }

                if user_warning == 1{
                   if rad.lowercaseString.rangeOfString("  warning ") != nil{
                        label.text = "\(label.text!)\(rad)"
                        label.text! += "\r\r"
                    }
                }

                if user_io == 1{
                   if rad.lowercaseString.rangeOfString("  io ") != nil{
                        label.text = "\(label.text!)\(rad)"
                        label.text! += "\r\r"
                    }
                }

                if user_useract == 1{
                    if rad.lowercaseString.rangeOfString("  useract") != nil{
                        label.text = "\(label.text!)\(rad)"
                        label.text! += "\r\r"
                    }
                }

                if user_error == 1{
                    if rad.lowercaseString.rangeOfString("  error ") != nil{
                        label.text = "\(label.text!)\(rad)"
                        label.text! += "\r\r"
                    } 
                }
               n=n+1
            }
        }
aurelius
  • 448
  • 7
  • 23

1 Answers1

3

Your app doesn't respond since it's doing a lot of work on the main thread (especially your non-optimal string processing and lots of property accesses are likely to be the main bottlenecks). You should try to offload that into a background thread. Note that you will need to assign the text property of UILabel on the main thread! So build the string in a variable and assign that to the text property on the main thread once you're done with processing.

Also, a lot of autoreleased object are created and won't get released until some time after you're returning from viewDidLoad. You should add an autorelease pool that's emptied every iteration, like this:

while n < lines {
    autoreleasepool {
        // Insert your "while" body here.
    }
}

Last but not least, since the last statement in your while is n=n+1, you should use a for loop instead:

for n in 0..<lines {
    autoreleasepool {
        // Insert your former "while" body here.
    }
}

With NSMutableString processing the flow would look something like this:

let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0)) {
    // Background thread. Do NOT access your label here!
    let string = NSMutableString()
    for n in 0..<lines {
        autoreleasepool {
            let rad: String = textArr[n]

            if user_event == 1 {
                if rad.lowercaseString.rangeOfString("  event ") != nil {
                    string.appendString(rad)
                    string.appendString("\r\r")
                }
            }

            // ...
        }
    }

    dispatch_async(dispatch_get_main_queue()) {
        // Update label on main thread.
        label.text = string
    }
}
Community
  • 1
  • 1
DarkDust
  • 90,870
  • 19
  • 190
  • 224
  • Thank you! That helped, it now takes 4min to display the log-file with 25000lines. An improvement! Any suggestions to get it even faster? – aurelius Apr 10 '15 at 08:44
  • 1
    I guess the biggest issue is how the string processing is done. AFAIK, Swift strings are considered to be values instead of objects, so if you do `stringVariable += something;` that creates a new string. I suggest you try to explicitly use a `NSMutableString` and append to that, it should avoid lots of copying and thus save some time. Also, avoid string formats if possible, their processing is expensive as well. So instead of doing `string.appendFormat("foo%@bar", something)`, do `string.appendString("foo"); string.appendString(something); string.appendString("bar");`. – DarkDust Apr 10 '15 at 08:55
  • Sorry, I don't get it. What should I replace the code below with to get a better flow?: var rad: NSString! = textArr[n] label.font = UIFont(name: label.font.fontName, size: 8) self.label.text! = "\(self.label.text!)\(rad)" – aurelius Apr 10 '15 at 13:02