I have a couple of time been caught out putting function code in NSAssert or NSParameter assert, like
NSParameterAssert( [self doSomeWork] );
The problem with this is when you do a release build not only does the code that does an abort if the test fails get omitted from the code, but my code within the () is also omitted also.
Obviously this fix is simple but to me it still this seems wrong, the logic of the code is changed between test build and release build.
I should make it clear I only use this pattern for situation where if the assert fail it is a programmer error.
Personally, I prefer the AssertMacros, which guarantee that the code will be executed, but don't use assertions.
http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/EXTERNAL_HEADERS/AssertMacros.h
I use both assert and NSAssert, and so define the appropriate values to disable them in Distribution builds (I also normally work with a configuration ReleaseWithAsserts to get optimized code with the asserts enabled, so testing as close to the actual distributed code as possible).
What I do is this (you can use the other define if you want NDEBUG is for assert()):
#ifndef NDEBUG
BOOL didWorkSucceed =
#endif
[self doSomeWork]; // returns a BOOL if succeeds
assert(didWorkSucceed);
You can use the defines for all kinds of stuff too - logging etc. Mostly I do test the return codes in Distribution, and return a nil object if it fails instead of ignoring it.
Related
XCode 7.2.1
iPad Retina iOS 9.2 Simulator
I have several breakpoints set in a particular class in an XCode project.
Everything I discuss below takes place in this one class file.
I set the breakpoints on -(int16_t)areaNbr by clicking in the gutter, and set no conditions or anything on them. I confirmed they existed as far as LLDB is concerned by running breakpoint list from the LLDB prompt.
The project scheme is set to build for debugging, not release.
I run the project in the simulator, and stop at a breakpoint in a different method than the one in question, at which time I want to go to the LLDB prompt and call po [self areaNbr] and step through areaNbr.
Please note, as this may be quite relevant, I have NO code in the project itself that calls
-(int16_t)areaNbr
Now, I CAN get this to stop at my breakpoints on -(int16_t)areaNbr if I add some code to the project that calls the method.
For example, if I add something like NSLog(#"... %d", [self areaNbr])
I know the issue has nothing to do with compiling away the method simply because nothing calls it, because if that were true, then my call to po [self areaNbr] wouldn't be spitting out the result to the debugger window as pictured below. So the method is being compiled, and certainly recognized as existing by the debugger for execution purposes... just not for stepping purposes.
FYI, [self area] is returning "Area01"
Calling breakpoint list in LLDB returns the following
By default, lldb does not stop at breakpoints in hand-called code. The majority of folks use expr & expr -O -- i.e. po to print values & objects and were annoyed if they stopped at breakpoints they had set for other purposes.
However, it is easy to control this behavior, just use:
(lldb) expr -i 0 -- [self areaNbr]
Then you will stop at your breakpoint.
In this example, I left out the -O which is the object printing part, since if you just want to call this method, you likely don't care about calling description on the result after the expression is evaluated.
You can see all the options for expression evaluation by doing:
(lldb) help expr
I got this compiler error when using goto in my ARC iOS project.
Cannot jump from this goto statement to its label. Jump bypasses
initialization of retaining variable
I know goto is bad in general but ... please just tell me how to fix it. The codes are like following,
//some process
NSArray *current = ... ;
if (current.count ==0) goto cleanup;
//proceed to next
if (processed failed) goto cleanup;
//further process
cleanup:
//clean up codes
I finally figured it out! Actually the warning said it clearly, "Jump bypasses initialization of retaining variable" so in the section next
//In proceed to next section I declare & init some object!
My codes/problem is basically the same as c99 goto past initialization
The solution is simple, just added a {} block to it, as here mentioned Why can't variables be declared in a switch statement?
For those who are wondering why I still need goto, I think this explained it Is it ever advantageous to use 'goto' in a language that supports loops and functions? If so, why? , especially "Cleanly exiting a function", check here for an example
http://eli.thegreenplace.net/2009/04/27/using-goto-for-error-handling-in-c
Without goto mainline code is deep inside the nested conditions (Of course we can also introduce a helper function to deal with it).
I have a method someMethod. This method, at some point has the following if-else condition.
- (void) someMethod {
// ... some more code ...
if ([userArray[0] isKindOfClass:[Me class]]) {
// some code
}
else {
// some other code
}
}
Now this if-condition is always met when I execute the code normally. But when I call it from one of my test-cases, the else-part gets executed instead. I am calling this method exactly the same way (it has no side-effects, etc).
When I debugged the thing in both normal run, and testing run. I saw something different.
While running in Test, the userArray had 1 object, (Me_Me_2 *)0x00007fa61d39dbf0.
And while running it normally, the userArray had the same object, but there was one difference. It said (Me_Me_ *)0x00007fce71459ae0.
When I print the value of NSStringFromClass([userArray[0] class]), they both print "Me".
"Me" is a NSManagedObject.
Another interesting thing is, if I add an expression in the debugger and evaluate it, it always evaluates to true - ([((NSObject*)userArray[0]) isKindOfClass:[Me class]]) returns (bool)true. This is totally bizarre! If the condition is true, why does it ever go into the else block?
Now some questions -
What is happening over here? Are Core Data objects treated different when running in tests?
Why is the type of the object "Me_Me_2" while testing and "Me_Me_" otherwise? Why is it not just "Me"?
This sounds similar to the following issue: isKindOfClass doesn't work as expected
In short, is the class being compared a target member of the test target? It should only be a target member of the application.
I have an app with a couple of thousand lines and within that code there are a lot of println() commands. Does this slow the app down? It is obviously being executed in the Simulator, but what happens when you archive, submit and download the app from the app store/TestFlight. Is this code still "active", and what about code that is "commented out"?
Is it literally never read or should I delete commented out code when I submit to test flight/app store?
Yes it does slow the code.
Both print and println decrease performance of the application.
Println problem
println is not removed when Swift does code optimisation.
for i in 0...1_000 {
println(i)
}
This code can't be optimised and after compiling Assembly code would perform a loop with 1000 instructions that actually don't do anything valuable.
Analysing Assembly code
The problem is that Swift compiler can't do optimal optimisation to the code with print and println commands.
You can see it if you have a look on generated Assembly code.
You can do see assembly code with Hopper Disassembler or by compiling Swift code to the Assembly with by using swiftc compiler:
xcrun swiftc -emit-assembly myCode.swift
Swift code optimisation
Lets have a look on few examples for better understanding.
Swift compiler can eliminate a lot of unnecessary code like:
Empty function calls
Creating objects that are not used
Empty Loops
Example:
class Object {
func nothing() {
}
}
for i in 0...1_000 {
let object = Object3(x: i)
object.nothing()
object.nothing()
}
In this example Swift complier would do this optimisation:
1. Remove both nothing method calls
After this the loop body would have only 1 instruction
for i in 0...1_000 {
let object = Object(x: i)
}
2. Then it would remove creating Object instance, because it's actually not used.
for i in 0...1_000 {
}
3. The final step would be removing empty loop.
And we end up with no code to execute
Solutions
Comment out print and println
This is definitely not the best solution.
//println("A")
Use DEBUG preprocessor statement
With this solution you can simple change logic of your debug_print function
debug_println("A)
func debug_println<T>(object: T) {
#if DEBUG
println(object)
#endif
}
Conclusion
Always Remove print and println from release application!!
If you add print and println instruction, the Swift code can't be optimised in the most optimal way and it could lead to the big performance penalties.
Generally you should not leave any form of logging turned on in a production app, it will most likely not impact performance but it is poor practice to leave it enabled and unneeded.
As for commented code, this is irrelevant as it will be ignored by the compiler and not be part of the final binary.
See this answer on how to disable println() in production code, there is a variety of solutions, Remove println() for release version iOS Swift
As you do not want to have to comment out all your println() calls just for a release, it is much better to just disable them, otherwise you'll be wasting a lot of time.
printLn shouldn't have much of an impact at all as the bulk of the operation has already been carried out before that point.
Commented out code is sometimes useful, although it can make your source difficult to read it has absolutely no bearing on performance whatsoever and I've never had anything declined for commented out code and my stuff is full of it.
In Xcode, is there a way for me run a single test case n times automatically?
Reason for doing this is that some of my beta testers are encountering random crashes in my app. I see the crash logs in TestFlight, along with the stack trace, but I can't reproduce the crash.
The crash happens infrequently but when it does, it always happens when users are trying to create a DB record, which then gets uploaded to a server. The problem with the crash logs is that my code does not make an appearance in their stack traces (all UIKit & CoreFoundation stuff - and different each time).
My solution is to run the test for that part of the app 100s of times, with the exception breakpoint set, to try to trigger the bug in my dev environment. But I don't know how to do this automatically.
It took 7 years, but as of Xcode 13, support for test repetition is now built-in.
From the Xcode 13 release notes:
Enable test repetition in your test plan, xcodebuild, or by running your test from the test diamond by Control-clicking and selecting Run Repeatedly to bring up the test repetition dialog.
You can read my answer here.
Basically you need to override invokeTest method
override func invokeTest() {
for time in 0...15 {
print("this test is being invoked: \(time) times")
super.invokeTest()
}
}
In Xcode as such, no.
You can create an XCTestCase class that hooks into the test-running methods it inherits to return multiple runs, but that tends to be annoying and mostly undocumented.
It's probably easier to instead have a "meta-test" that calls out to your other test method repeatedly:
func testOnce() {}
func testManyTimes() {
for _ in 0..<1000 { testOnce() }
}
You might need to call out to some per-test setup/teardown methods. You could perhaps work around that by instead making the loop body be something like:
let test = XCTestCase(selector: #selector(testOnce))
test.invokeTest()
This would lean on the XCTest machinery that your standard tests use, but it might gripe about not being wired into an XCTestCaseRun (or not).