I'm reading the Assertions section in the Swift e-book and it looks like assertions work very similarly to their Objective-C counterparts. However, nowhere in the docs can I find anything about runtime behaviour while running as a production app. Objective-C's NSAssert promises never to terminate a production application as a result of an assertion failure. Is it the same case in Swift?
Based upon the language Apple use in their documentation, I'd say assertions are ignored in a production environment.
If your code triggers an assertion while running in a debug
environment, such as when you build and run an app in Xcode, you can
see exactly where the invalid state occurred and query the state of
your app at the time that the assertion was triggered. An assertion
also lets you provide a suitable debug message as to the nature of the
assert.
And in the "Note" block:
Assertions cause your app to terminate and are not a substitute for
designing your code in such a way that invalid conditions are unlikely
to arise. Nonetheless, in situations where invalid conditions are
possible, an assertion is an effective way to ensure that such
conditions are highlighted and noticed during development, before your
app is published.
The difference between debug and release is the difference between compiler arguments. The most likely answer is that there will be some special compiler settings (similar to -ea in Java).
EDIT
The Swift compiler has an argument called -assert-config
-assert-config Specify the assert_configuration replacement. Possible values are Debug, Release, Replacement.
In Release, the assertions are ignored. Not sure about the difference between Debug and Replacement.
Check your Optimization Level and make sure it is not Onone for release configuration. See my note https://github.com/onmyway133/blog/issues/39
Asserts are documented along with preconditions in the Swift standard library documentation.
Debug -> Stop on assertion failure.
Release -> Compile with assertions ignored
Release and "Disable safety checks" -> Assume all assertion statements are statements of truth and hints to the compiler so that following and preceding code may be removed if only reached in conditions where the assertion would have failed. This means that if you follow the assertion by code on handle the exceptional case it may be ignored. If the assertion would ever have failed the behaviour is completely undefined.
I've not checked but the "Disable safety checks" may correlate with the -assert-config Replacement that #Sulthan mentions.
Related
When developing iOS apps using XCode, you can use NSLog calls to write information to the console for debugging. In Xamarin.iOS, the same can be done with Console.WriteLine.
When running a release build of an app, without the debugger attached, these calls serve no purpose. So I've thought of excluding them with pre-processor directives:
#if DEBUG
Console.WriteLine("foo");
#endif
So that the calls don't even exist in the release build. I appreciate this may be micro-optimisation - but I'm curious if Xamairn already does this, making this unnecessary?
Console output is not removed in iOS release builds.
FYI: Xcode does not "automatically" remove NSLog or print from Obj-C/Swift "release" code either. (You typically define a single global preprocessor macro to remove them all, if desired...)
Your options are many... including:
DEBUG preprocessor as you show in your question.
Use System.Diagnostic.Debug.WriteLine:
Any calls to Debug.* will be removed by the compiler due to the [Conditional("DEBUG")] attribute being applied.
Create your own "Logger" class as a wrapper to the stdout writers and [Conditional("DEBUG")] it
Use Fody and re-weave the assemblies to remove/NOP/redirect the WriteLine
I do this to redirect the calls to in internal log and upon crash or user stat requests, forward this log to our crash reporting servers.
etc, .....
Ref: Xamarin's NSLogWriter Helper Class
I have several assert(condition, "message") statements in my project.
They are used to check invariant conditions during development. I thought they would be ignored in production/release build (as stated in this answer). They are not. Instead they cause crashes during TestFlight testing. When I comment asserts the app does not crash. Something usually gets wrong a bit but it does not crash.
Can it be something with my build settings?
All my archive schemes use release configuration:
The asserts are in Cocoa Touch Framework project, that is used from custom keyboard extension.
All the targets in all projects (Cocoa Touch Framework, and the main project with keyboard extension target) have these Build Settings:
Enable Foundation Assertions
Debug YES
Release NO
Disable Safety Checks NO
What's wrong?
EDIT:
Sulthan's answer shows how to disable asserts globally for both debug and relase builds. That is not what I need. I want it to work as expected - asserts should be enabled in debug but disabled in release builds.
By default it works that way - and it also works that way in my main project. But it does not work for asserts located in Framework project that is linked from that main project (details in this question). Why? How to fix it?
The options you have tried:
Enable Foundation Assertions is in the preprocessing section (Macros). Swift is not preprocessed and does not use macros. This option disables NSAssert, NSParameterAssert and similar macros commonly used in Objective-C.
Disable Safety Checks is a performance option:
By default, the standard library guarantees memory safety. Many functions and methods document the requirements that must be satisfied by the caller, such as an array index being valid; memory safety is guaranteed even if a requirement is violated. However, violating a requirement can trigger a runtime error. APIs that include the word “unsafe” in their name let you explicitly disable safety checks in places where you need the additional performance. It’s your responsibility to verify the memory safety of code that uses unsafe APIs. Memory safety is also not guaranteed if there is a race condition in multithreaded code.
(Swift Library Reference)
You should probably try my answer here
(use -assert-config Release in Other Swift Flags).
Or just keep the asserts in production builds. Every failing assert is a bug and in general it's better to know about a bug as soon as possible.
I want to test the contract of my API so if, for example, an object is created with some parameter to nil an assertion is raised, but if the assertions are disabled (Release config) it simply returns nil.
My test scheme has the build configuration to Debug, so in my API contract tests I check if the creation of an object with some parameters to nil returns a nil object. But the assertion of the constructor is raised before the XCTAssertNil is executed, so my test always fails.
Can I disable the assertions while testing? I tried to add NS_BLOCK_ASSERTIONS to the scheme arguments passed on launch but that doesn't work.
Solved, I added a new configuration, duplicated from Debug, called Test.
Then added NS_BLOCK_ASSERTIONS=1 in the build settings preprocessor macros.
Finally, change the test action in the test's scheme.
Now it works :)
As explained in your self-answer, it is useful to create a configuration dedicated to unit testing
In Objective-C you'll need to play with NS_BLOCK_ASSERTIONS and the more recent ENABLE_NS_ASSERTIONS indeed.
For Swift assertions, I found out from Apple Developer it depends on the compiler optimization level you chosed.
With -Onone the assertions will be triggered, but not with -O
If we run the app on device with NSLogs then it may slow down the app to some extent.
Is NSAssert act in the same way as NSLog in the memory aspect?
Any comments or suggestions would be appreciated.
Thank you in advance.
Maybe this will answer your question
It's important to note that as of Xcode 4.2, assertions are turned off
by default for release builds, which is accomplished by defining the
NS_BLOCK_ASSERTIONS macro. That is to say, when compiled for release,
any calls to NSAssert & co. are effectively removed.
Source: http://nshipster.com/nsassertionhandler/
If you leave them enabled, then yes they come at a cost (obviously they need to be evaluated) and depending on what code you have them execute it differs. For simple nil comparisons it is negligible.
For further reference see: http://www.mikeash.com/pyblog/friday-qa-2013-05-03-proper-use-of-asserts.html
Specifically this passage
The runtime cost should be negligible, and if it's not, then you should redo your asserts to fix that.
In XCode, by default assertion are disabled in release, so they won't take any processing time. If you check in your build settings, NS_BLOCK_ASSERTIONS is set to 1 and looking at the NSAssert define in that case
#define NSAssert(condition, desc, ...) do {} while (0)
empty loop that will be removed at compilation time.
I'm revisiting an an older project and converting to ARC, my first time through Xcode's conversion tool (Edit -> Refactor -> Convert to Objective-C ARC...), and I'm seeing a couple things that I'm not sure are real issues or red herrings somehow.
I get, as expected a big list of things that the tool finds that prevent it from completing, but:
Many (all?) instances of retain/release/autorelease appear to be flagged as errors e.g. "release is unavailable: not available in automatic reference counting mode". Am I really supposed to get rid of all these myself? I thought that's what the tool did.
In many of my classes, I'm seeing a bunch of errors that look like phantom parse/build errors that have nothing to do with ARC. E.g. in a simple class that apparently has no ARC-related issues, I'll get an "undeclared identifier" on some arbitrary method implementation, and then a bunch of "Parse error: expected }" at the end of the file, etc. These are not real-- the project builds fine, and I don't see any proximate cause or resolution for the errors.
There are "real" issues in the list as well (expected bridging issues that need to be explicitly clarified in code) but there are so many random errors of the above variety that it's hard to even find the signal in the noise. This seems wrong to me.
Am I misunderstanding what this tool is really doing? Apple's docs say this:
Xcode provides a tool that automates the mechanical parts of the ARC
conversion (such as removing retain and release calls) and helps you
to fix issues the migrator can’t handle automatically
Thanks.
The tool does not get rid of them for you, but simply adds retain/release code as need under the hood at the time of compile.
Those problems very well may go away when you get rid of old reference counting code.
EDIT: Further explanation:
In Xcode 4.2, in addition to syntax checking as you type, the new
Apple LLVM compiler makes it possible to offload the burden of manual
memory management to the compiler, introspecting your code to decide
when to release objects. Apple’s documentation describes ARC as
follows:
“Automatic Reference Counting (ARC) is a compiler-level feature that
simplifies the process of managing object lifetimes (memory
management) in Cocoa applications.”
In other words, ARC does not "strip" reference counting from your code, but rather does it on it's own under the hood. You no longer have to type release or retain or dealloc again. One thing the ARC needs to work is for it to do the reference counting entirely on it's own (with no user reference counting to "get in the way").
Took a long time to resolve, but both of these issues seemed to stem from some custom macros I was using. I had a macro for release-and-set-to-nil that I was using frequently, like this:
#define RELEASENIL(x) [(x) release]; \
(x) = nil;
I'm still not sure why, but for some reason, the ARC conversion tool didn't take this in stride, and choked on it, throwing the release warnings and the parse errors. (Some interaction with the preprocessor?) When I changed the macro to remove the release line, the conversion proceeded much more in line with my expectations.
And yes, it does of course remove the messages for you. (I'm answering my own question on the off chance that someone else ever has this issue.)