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.)
Related
I am trying to run some code from some time ago, after recompiling Lua for proper architecture, I am having some issues with the following line.
KillScript* script = (KillScript*)lua_tointeger(L, -1);
I am getting the following error on that line:
Cast of 'lua_integer' (aka'long') to 'KillScript' is disallowed with ARC.
Does anybody know why wouldn't this logic work?
Thanks!!
ARC -- automatic reference counting -- doesn't allow you to move between non-object reference types and object reference types. A lua_integeris a C scalar.
You can do some unsafe casting dance to keep ARC enabled, but I suspect you'll run into other issues.
If it is a standalone project, turn off ARC and try compiling. You'll likely run into other issues-- deprecation, etc... -- but at least you'll probably have relatively sane memory management behavior.
After iPhone app that I'm writing in Swift become quite big (> 150 .swift files + various Objective-C libs), Xcode start behave pretty badly:
every second compilation I get various errors, e.g.:
Command failed due to signal: Segmentation fault: 11
compilation take enormous amount of time (> 2 min on MacBook Pro Retina)
and so on.
I just wonder if everyone has same problems and maybe someone found a way to reduce this nightmare?
What I have done so far — I split project into several dynamic frameworks that I link from main project, it helps to reduce compile time, but introduce some new problems.
I also use iRamDisk to keep DerivedData folder in RAM and periodically delete all files from it, it sometimes helps with SourceKit crashes.
Swift toolchain is still a bit gross, you'll need to use some temporary workarounds until Apple fixes it (see UPDATES below)
Here is a list of items that you can do to keep yourself from going crazy.
Slowness caused by immature Swift compiler
Change your development workflow using Injection for Xcode. Once you installed the plugin, you'll be able to inject code changes in your simulator\device without recompiling. You don't need to hardcode\modify anything in your project. We started using it recently at work and it made a huge impact on our side, even if it doesn't apply to every use case (for example you can't create new functions, you can only modify the existing ones).
Some particular code constructs that the compiler doesn't like and takes too much time to compile. The most common problem is with the Type Checker that slows down compile time exponentially based on how many type checks it needs to do (read more here for practical examples and here for a detailed explanation). In order to identify if you are suffering from this problem you can follow this blog post, you will gather information about the functions that creates slowness by using some compiler additional flags. Alternatively you can use this Xcode plugin to identify the source of the build slowness.
Use dynamic frameworks wisely, where it makes sense. A framework recompilation will be done only when you modify one of its Swift files (dynamic frameworks are only available for iOS >= 7).
Condense code in the same files. Lowering the number of Swift files speeds up the compile process sensibly. You can easily achieve it enabling "Whole module optimization" by adding a user-defined custom flag SWIFT_WHOLE_MODULE_OPTIMIZATION and set it to YES and at the same time set optimization level to none (to disable optimizations that would make it slow) OUTDATED You may consider to use this gist, it's a build script that collapses all your code in a "merge.swift" file.
You'll need to create a new target for it, but it is worth a
try.
Double check things listed here (there are a few some more misc reasons because the compilation is slow)
OUTDATED Try the approach described in this blog post, it involves creating a build script that generates a make file. It requires manual intervention on the build script (it contains the list of swift files).
OUTDATED Try this hacked up incremental compilation technique
UPDATE: Incremental builds introduced on Swift 1.2 (Xcode 6.3)
Apple finally introduced incremental builds with Swift 1.2 (shipped with Xcode 6.3). It's not still perfect, but it's a huge improvement.
From now on a class is recompiled only when it is changed (or when one of the class it depends on has been changed).
However the compiler still can’t understand if the changes to a class are to its interface or not. So any kind of change to a class causes a recompilation of that class and all of its dependencies.
UPDATE: Recompile dependent classes only when public interface changes introduced on Swift 2.1 (Xcode 7.1)
Starting from Swift 2.1 (Xcode 7.1), the dependent classes are recompiled only when you change the public interface of a class, and not at every change. This makes an huge difference in particular for big projects.
Project (mis)configuration (not related to Swift)
Be sure that "Build Active Architecture Only" is YES for debug.
Be sure that you didn't add pre\post compilation scripts that take too much time.
Apple has some advices for speeding up your Xcode build in Technical Note 2190. Have you thought about creating and precompiling an own framework for outsourcing unchanged Swift modules or some/all Objective-C code?
Remove all type inferences in Swift.
This SO topic has some nice ideas and this blog post suggest to
stop generating dSYM bundles and
avoid compiling with -O4 if using Clang.
Although lots of these improvements are related to Objective-C, I am quite sure, that some of them are still relevant for Swift.
The (re)compiling is a known issue that I am sure will be resolved soon. Some recommendations:
Use Objective C where possible - it compiles fast even if it is a part of a Swift project
Split code to frameworks
Specify types instead of leaving it up to the compiler to infer them
Again, there is a good chance that this will be fixed soon, so perhaps it is best not to make big investments in rewriting or reorganizing the code at this point in time.
you could try:
upgrading the amount of RAM in your computer
if you have multiple .swift files that do things on the same view controller, try condensing them into one .swift file per view controller
tweaking the settings under compile sources to see if you have any duplicates or if there are any scripts or settings you can add to make it compile faster...
you can also take a look at this post's answers for some hints as to what you can do to slow down compile time
I've discovered that one of the main causes of segmentation faults and slow compilation is hardcoding big arrays and dictionaries, especially when declaring them as global constants and trying to access values from them from within another .swift file. When I store all that data inside plists, these problems are gone.
In my experience avoid creating the large swift files, when I started a project in my new company, there was a 'UIViewController' with more than 2000 lines, little changes on this file taking much time to build, I made 4 extensions with <500 lines from that class, my speed improvement was incredible.
In XCode (version 5.1.1 in my case), when you click Edit/Refactor/Convert to Objective-C ARC, what exactly is supposed to happen?
My goal here was to convert my old code to use ARC.
I basically set Objective-C Automatic Reference Counting to Yes, then I did the above to "refactor" my code. It failed and I ended up spending 5 hours commenting out anything with autorelease, retain, release etc... from my code.
When I finally got all that done, I was able to proceed with the Convert to Objective-C ARC operation, which ended up telling me that no changes to my code were necessary, which was true because I had done it all manually.
Is it supposed to do what I did manually, but automatically? Why didn't it?
By turning "Automatic Reference Counting" on manually you told the compiler that the project was already using ARC. Therefore, conversion did nothing - you told the compiler it was already converted.
Obviously all the non-ARC features you used were now errors and you had to fix them.
The normal method is that you do not change to Automatic Reference counting. The compiler will then check out your code, make sure that it can convert everything (it won't convert your code if the static analyser finds reference counting bugs, or when things are too complicated), you fix all the things that it refuses to convert, and once everything is Ok you convert everything.
And the answer to your question is "NO".
Basically, I have an IOS app that functioned without issue.
While following the instructions at http://docs.urbanairship.com/build/ios.html#ios-push-getting-started, I reached the "Register Your Device" section asking me to compile.
After attempting to build the code in xCode 5 I received the following error "#implementation UAPushSettingsAddTagViewController #synthesize of 'weak' property is only allowed in ARC or GC mode".
Note:ARC mode is not in use.
Search for "weak" in your project code and the libraries you include. Change it to a "assign"
Edit:
As #TaylorHalliday points out in his comment below, my answer was rather incomplete.
Changing weak properties to assign will get rid of compiler errors, but it will potentially cause memory management problems if you don't understand how to use manual reference counting.
Since you're using manual reference counting you will need to go through your code and make sure that you retain objects that you need to persist, and then release all owning references to objects when you are done with them. Explaining the details is beyond the scope of a forum post. I suggest you search on "About Memory Management" in the Xcode help system, and read the entire Advanced Memory Management Guide.
You should probably also run the Analyze tool on your project to look for possible memory management problems.
Better yet, convert your project to use ARC. It's much easier to avoid memory management problems when using ARC.
I got same error when I added these two files to my project. My project wasn't enabled for ARC. I had to remove these files first and then had to convert my project to ARC. Then adding these files caused no error.
I'm getting the following exception when I run my application in Release mode from Visual C++.
Unhandled Exception:
System.AccessViolationException:
Attempted to read or write protected
memory. This is often an indication
that other memory is corrupt. at
_cexit() at .LanguageSupport._UninitializeDefaultDomain(Void
* cookie) at .LanguageSupport.UninitializeDefaultDomain()
at
.LanguageSupport.DomainUnload(Object
source, Eve ntArgs arguments) at
.ModuleUninitializer.SingletonDomainUnload(Objec
t source, EventArgs arguments)
This doesn't happen in Debug mode. Initially, I saw this exception on my home computer, but not work computer. When I continued to develop on my work computer, I ended up bumping into it.
Also, I found that when I added three const std::string variables the exception was thrown. If I removed then then all went well.
Another piece of information: I've found that turning off all the compiler optimizations in Release mode makes the exception go away
Something fishy is going on. Any ideas on how to track this down?
Thanks for the help,
Joe
Joe, you have a memory leak.
You're probably trying to use some memory that has been deleted.
See this article for common causes of memory leaks, and how to identify them, otherwise, search for "C++ memory profiler" + your compiler/platform, it'll give links to Memory profilers suitable for your compiler and platform, these will help track down the memory leak by watching how your program uses memory as it runs.
Hope this helps.
EDIT
How to track it down? This is off the top of my head, there may be better advice else where . . .
Find where the code crashes, it'll be when accessing the contents of some pointer (or deleting a pointer).
The problem is that that pointer has either a) never been assigned b) is already deleted.
Go through all references to pointers of that type, are they used in copy ctors/assignment operators?
If so, are it's contents being copied or just the pointer?
If just the pointer then is the containing class trying to delete the pointer? If so the first class to die will succeed, the second will throw an access violation.
If you don't explicitly code copy ctors and operator=, then you should hide them (declare private prototypes but don't implement them), this stops the compiler from generating default implementations for you.
When you hide them you'll get compiler errors everywhere they're being used, it might be that you can clean these up, or that you need to implement the copy ctor and operator= for each class.
I'm on vacation from tomorrow or two weeks, email me direct today (follow the link on my SO user page) if you've any questions on this.
Do you have any code that is #defined out for debuging in your code?
i.e.
#ifndef _DEBUG
//release only code such as liscensing code
#endif
That's one thing that could be causing the problem, and I've run into it before as well.
Another possibility is a VS issue (or whatever IDE you're using).
Try running the release .exe directly instead of through the develoment environment and see if you still have the same issue.
It's a while since I've done C++ "in anger" so to speak, so some (or indeed all) of what I say below may well be out of date.
Are you using managed C++? If not then it sounds like an uninitialised pointer. It used to be the case that all pointers were nulled in debug & I recall something about turning this behaviour off, but I can't remember the full details right now.
Are the strings overrunning their variables? Unlikely with std::string, but worth eliminating.
Couple of possibilities:
I would guess that you are reading/writing past local array end. In debug builds this may work, as memory is not tightly allocated. In release builds this is more likely to cause problems, depends on what is allocated right next to the array.
Another possibility is that you have an uninitialized pointer somewhere. VC default initializes local variables in debug mode, but not in release mode. Thus code like:
int* p;
if (p != NULL) { /* do something */ }
Typically fails on release mode.
The error message is strongly suggesting you have a memory issue, probably overwriting memory. These are hard to find, but you can find some possible solutions googling "visual c++ memory corruption tool".
The thing about memory corruption is that it's unpredictable. It doesn't necessarily have any consequences, and if it does they may not result in a crash. Crashing like that is good, because it informs you you've got a problem.
Fiddling with debug vs. release, adding or removing parts of code, changing optimization options and the like is unlikely to solve the problem. Even if it does, it's likely to crop up if any changes are made.
So, you've got a memory corruption problem. Those are almost always difficult to find, but there are tools. You need to fix that problem.
You might also look at your shop practices. Do you use less safe constructs (new arrays rather than vector<>, say)? Do you have coding standards to try to reduce risk? Do you have code reviews? Memory corruption can be insidious and damaging, and you want to avoid it as much as possible.
What your getting is a system exception from the OS. These are not handled because they are not C++ exception. However you can convert then into a C++ exception and catch them like a normal exception.
There is a great article here http://www.thunderguy.com/semicolon/2002/08/15/visual-c-exception-handling/ (page 3) that shows how to create a Windows Exception class that will catch the exception using the _set_se_translator method and throw a C++ exception. The great thing is you can get a stack from the EXCEPTION_RECORD structure, although your'll have to add that functionality to process the structure, but it will help narrow your search for that access violation.
I think the issue here is uninitialized local variable.
In Debug mode generally the variables get initialized and you don't get any exceptions.
But errors may occur in release mode because of this.
Try to look for uninitialized variable whose access may cause exception.
Suppose you have boolean local variable.
bool bRet;
In debug build bRet will get initailized to 0 and your code just works fine .
But in release it won't be 0 , it would be some random value and your code might be doing something based on bRet .It may later cause an exception because bRet value is wrong.