Remove println() for release version iOS Swift

前端 未结 19 1380
被撕碎了的回忆
被撕碎了的回忆 2020-11-29 15:55

I would like to globally ignore all println() calls in my Swift code if I am not in a Debug build. I can\'t find any robust step by step instructions for this a

相关标签:
19条回答
  • 2020-11-29 16:43

    As noted, i am a student and need things defined a little more clearly to follow along. After lots of research, the sequence I needed to follow is:

    Click on the project name at the top of the File Navigator at the left of the Xcode project window. This is line that has the name of the project, how many build targets there are, and the iOS SDK version.

    Choose the Build Settings tab and scroll down to the "Swift Compiler - Custom Flags" section near the bottom. Click the Down Arrow next to Other Flags to expand the section.

    Click on the Debug line to select it. Place your mouse cursor over the right side of the line and double-click. A list view will appear. Click the + button at the lower left of the list view to add a value. A text field will become active.

    In the text field, enter the text -D DEBUG and press Return to commit the line.

    Add a new Swift file to your project. You are going to want to make a custom class for the file, so enter text along the lines of the following:

    class Log {
    
      var intFor : Int
    
      init() {
        intFor = 42
       }
    
      func DLog(message: String, function: String = __FUNCTION__) {
        #if DEBUG
          println("\(function): \(message)")
        #endif
      }
    }
    

    I was having trouble getting the class to be accepted by Xcode today, so the init may be a bit more heavyweight than necessary.

    Now you will need to reference your custom class in any class in which you intend to use the new custom function in place of println() Add this as a property in every applicable class:

       let logFor = Log()
    

    Now you can replace any instances of println() with logFor.DLog(). The output also includes the name of the function in which the line was called.

    Note that inside class functions I couldn't call the function unless I made a copy of the function as a class function in that class, and println() is also a bit more flexible with the input, so I couldn't use this in every instance in my code.

    0 讨论(0)
  • 2020-11-29 16:44

    The problem with all these approaches, including mine, is that they do not remove the overhead of evaluating the print arguments. No matter which of them you use, this is going to be expensive:

    print(myExpensiveFunction())
    

    The only decent solution is to wrap the actual print call in conditional compilation (let's assume that DEBUG is defined only for debug builds):

    #if DEBUG
    print(myExpensiveFunction())
    #endif
    

    That, and only that, prevents myExpensiveFunction from being called in a release build.

    However, you can push back evaluation one level by using autoclosure. Thus, you could rewrite my solution (this is Swift 3) like this:

    func print(_ item: @autoclosure () -> Any, separator: String = " ", terminator: String = "\n") {
        #if DEBUG
        Swift.print(item(), separator: separator, terminator: terminator)
        #endif
    }
    

    This solves the problem just in the case where you are printing just one thing, which is usually true. That's because item() is not called in release mode. print(myExpensiveFunction()) thus ceases to be expensive, because the call is wrapped in a closure without being evaluated, and in release mode, it won't be evaluated at all.

    0 讨论(0)
  • 2020-11-29 16:45

    Varun Naharia has the better solution so far. I would combine his answer with Rivera's ...

    1. create a -D DEBUG flag on the compiler directives, build settings.
    2. then add this code:

      #if !DEBUG
       public func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
      }
      #endif
      

    This code will convert every print into nothing for release.

    0 讨论(0)
  • 2020-11-29 16:46

    Even simpler: take advantage of the fact that asserts are removed from release builds and only from there call the print. This removes all log calls (yes, even the calls to Log.da) as they are empty when building for release.

    But I also heard that prints are removed for release builds, but not been able to find it in writing. So for now, I am using something like this Log below. I have a more meaty version on GitHub with emojis (for readability) and log topics (for consistency):

    https://github.com/Gatada/JBits/blob/master/Project/Utility/Log.swift

    public enum Log {
    
        /// A date formatter used to create the timestamp in the log.
        ///
        /// This formatter is only created if it is actually used, reducing the
        /// overhead to zero.
        static var formatter: DateFormatter?
    
        // MARK: - API
    
        /// Call to print message in debug area.
        ///
        /// Asserts are removed in release builds, which make
        /// the function body empty, which caused all calls to
        /// be removed as well.
        ///
        /// Result is zero overhead for release builds.
        public static func da(_ message: String) {
            assert(debugAreaPrint(message))
        }
    
        // MARK: - Helpers
    
        /// The function that actually does the printing. It returns `true` to
        /// prevent the assert from kicking in on debug builds.
        private static func debugAreaPrint(_ message: String) -> Bool {
            print("\(timestamp) - \(message)")
            return true
        }
    
        /// Creates a timestamp used as part of the temporary logging in the debug area.
        static private var timestamp: String {
    
            if formatter == nil {
                formatter = DateFormatter()
                formatter!.dateFormat = "HH:mm:ss.SSS"
            }
    
            let date = Date()
            return formatter!.string(from: date)
        }
    }
    

    In code:

    Log.da("This is only handled in a debug build.")
    

    Seen in the Xcode debug area only when running a debug build:

    13:36:15.047 - This is only handled in a debug build.

    0 讨论(0)
  • 2020-11-29 16:47

    The simplest way is to put your own global function in front of Swift's println:

    func println(object: Any) {
        Swift.println(object)
    }
    

    When it's time to stop logging, just comment out the body of that function:

    func println(object: Any) {
        // Swift.println(object)
    }
    

    Or you can make it automatic by using a conditional:

    func println(object: Any) {
        #if DEBUG
            Swift.println(object)
        #endif
    }
    

    EDIT In Swift 2.0 println is changed to print. Unfortunately it now has a variadic first parameter; this is cool, but it means you can't easily override it because Swift has no "splat" operator so you can't pass a variadic in code (it can only be created literally). But you can make a reduced version that works if, as will usually be the case, you are printing just one value:

    func print(items: Any..., separator: String = " ", terminator: String = "\n") {
        Swift.print(items[0], separator:separator, terminator: terminator)
    }
    

    In Swift 3, you need to suppress the external label of the first parameter:

    func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
        Swift.print(items[0], separator:separator, terminator: terminator)
    }
    
    0 讨论(0)
  • 2020-11-29 16:49

    Swift 4 Xcode 10.0

    maybe you could use this

    func dPrint(_ message: @autoclosure () -> Any) {
        #if DEBUG
        print(message())
        #endif
    }
    

    The reason of using @autoclosure is that if you pass a function as the message parameter, the function will be called only in debug mode, it will cause a performance hit.

    unlike the Swift.print(_ items: Any..., separator: String = default, terminator: String = default) function, my solution has only one parameter, because in most cases, we don't pass multiple parameters as the print function only shows information in console, we can just convert the parameters to String: "\(param1)"+"\(param2)", right? hope u like my solution

    0 讨论(0)
提交回复
热议问题