I'm seeing a strange behavior with Xcode 6 debugger.
I have created a singleton shared instance using the following code:
+ (instancetype)shared
{
static DataBaseManager *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[DataBaseManager alloc] init];
});
return sharedInstance;
}
Right after the object is initialized by calling the method like this:
DataBaseManager *manager = [DataBaseManager shared];
NSLog(#"");
I have placed a breakpoint on the "NSLog", and i'm seeing the following debugger status:
I have made sure I'm launching on debug mode, and that the build settings are fine, following the question here: Xcode debugger doesn't print objects and shows nil, when they aren't
Any ideas on why this is happening?
This is the first time i have ever seen this kind of strange behavior.
Any help would be much appreciated.
**UPDATE**
A bug was reported to apple bug report system.
The bug status is: Duplicate of 17164538 (Closed)
so it is probably a known bug in Xcode.
You shouldn't be in Release mode while you are debugging your code.
If you want to see variable values you have to be in Debug mode. The steps are
Click on your project name on the top left corner near start/stop buttons
Go in Edit scheme
Go in Run settings
Go in Info tab and then Build Configuration
Set it to Debug
If it was on "Release" that's the matter you saw all nil.
If still not working then try following in the project Build Settings
Set Strip debug symbols during copy to NO
Optimization Level to None -O0
Try setting Deployment Postprocessing to NO inside your Build Settings and check.
Make sure you have Link-Time Optimization set to No for debug mode in Build Settings.
Related
I got a EXC_BAD_ACCESS code=1 because of a line like this:
NSDictionary* params = #{};
I didnt create the project, but i notice that have link to a c++ library, and have build setting that is not default (the setting generated by XCode). If i change this line by
NSDictionary* params = [NSDictionary new];
The issue is gone. Can it be something to do with the compiler's setting in project's build setting?
As a comment said this error is likely to be deep in your code. If the culprit is a zombie, the easiest way to find it is to run it (preferably in the latest Xcode, currently Xcode 8, as it has been improved) in profiler and choose "Zombies". When it fails, you can see the history of everything that has happened to the object.
Also, set an exception breakpoint. You may get a break when the error happens instead of in main, where the exception gets passed up.
Currently in React-Native, according to the documentation, to build your iOS app for production, you need to :
change your scheme to Release
change your AppDelegate.m to load the correct bundle
change your Info.pList for ATS
This is a strong violation of 12 factor config recommandation, and it leads to mistakes being made in a continuous integration process.
RN does not provide either out-of-the box strategies to know the configuration environment in the JS code, leading to the existence of the package react-native-config, which does a great job already, but is not perfect (Xcode is not fully supported).
Why is it so? Is it because there are actually so few RN app in production today that nobody cares about this? Can we do better than react-native-config so that steps listed above are not required? I would like a command line that archives my app in the same way that I can run cd android && ./gradlew assembleRelease, without changing anything to my config.
EDIT:
Fastlane makes deployment a lot easier through its gym command (thank you Daniel Basedow). Apparently, the philosophy of Xcode is to call environments "schemes", only you cannot store variables in them, or know which scheme you're running in your code... Anyway, David K. Hess found a great way to export your scheme name in your Info.plist, and then in your Objective C code, which means I'm now able to chose my bundle according to the current scheme, and not touch my code.
Here is my code:
NSString *schemeName = [[[NSBundle mainBundle] infoDictionary] valueForKey:#"SchemeName"];
if ([schemeName isEqualToString:#"scheme1"]) {
jsCodeLocation = [NSURL URLWithString:#"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];
} else if ([schemeName isEqualToString:#"scheme2"]) {
jsCodeLocation = [NSURL URLWithString:#"http://<my_local_ip_address>:8081/index.ios.bundle?platform=ios&dev=true"];
} else if ([schemeName isEqualToString:#"scheme3"]) {
jsCodeLocation = [[NSBundle mainBundle] URLForResource:#"main" withExtension:#"jsbundle"];
}
Now my problem is : I also want to know which scheme I'm running in my JS code. react-native-config's way is self-described as hacky, and overly complicated considering the fact the information is already in my Objective C code. Is there a way to bridge this information to my JS code?
Only knowing which scheme I'm running is not as good as being able to set environment variables, but at least I'll be able to switch between environments only by changing my scheme.
EDIT 2:
I managed to export my scheme to my JS code. I created a cocoa touch class with the following code:
// RNScheme.h
#import <Foundation/Foundation.h>
#import "RCTBridgeModule.h"
#interface RNScheme : NSObject <RCTBridgeModule>
#end
// RNScheme.m
#import "RNScheme.h"
#interface RNScheme()
#end
#implementation RNScheme
{
}
RCT_EXPORT_MODULE()
- (NSDictionary *)constantsToExport
{
NSString *schemeName = [[[NSBundle mainBundle] infoDictionary] valueForKey:#"SchemeName"];
NSLog(#"%#", schemeName);
return #{
#"scheme_name": schemeName,
};
}
#end
and then in my JS code:
import {NativeModules} from 'react-native'
let scheme = NativeModules.RNScheme.scheme_name
EDIT 3:
There is actually another way than using schemes. You can create new "configurations" ("Release" and "Debug" are called configurations) with the following steps (thanks CodePush):
Open up your Xcode project and select your project in the Project
navigator window
Ensure the project node is selected, as opposed to one of your
targets
Select the Info tab
Click the + button within the Configurations section and select
which configuration you want to duplicate
Then you can define keys with different values according to your configuration.
Select your app target
Chose Build Settings
Go to User-Defined section (at the bottom of the scroll area)
You can define constants with a different value according to your configuration (for instance API_ENDPOINT)
You can then reference this value in your Info.plist file :
Open your Info.plist file
Create a new value and give it a name (ApiEndpoint)
Give it the value $(API_ENDPOINT) or whatever name you gave to your constant
Now you can reference this value in your code using the code I gave you in my second edit to this question.
You can create one scheme per configuration to switch quickly from one to the other, or change the build configuration each time (option click on the run button).
In your AppDelegate you can use the correct bundle like this
#ifdef DEBUG
jsCodeLocation = [NSURL URLWithString:#"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];
#else
jsCodeLocation = [[NSBundle mainBundle] URLForResource:#"main" withExtension:#"jsbundle"];
#endif
When you do a release build, the DEBUG flag is not set. You can also use different files as your Info.plist depending on build type. There will probably be situations where you want an Xcode debug build with a production JS bundle or vice versa. In that case you need to touch code.
Building ios apps from command line can be a bit tricky. The problems you're describing are not specific to react-native but the Xcode build system. If you haven't already, check out fastlane especially the gym command. It is much simpler than using xcodebuild directly.
But you still have to define your schemes.
I have an iOS app with two targets: the main (the app), and a today extension widget. In the main target I have a class called "TimetablesInformation", and if I call a method of that class from the own target everything goes right. But if I tried to access to that class from the other target (the widget) with this:
TimetablesInformation *info = [[TimetablesInformation alloc] init];
[info getTimes:#"1" forLine:lineName];
it crashes with this code error:
2015-12-26 01:48:04.420 Closer Times[15689:307103] __extensionContextClass != nil -
/BuildRoot/Library/Caches/com.apple.xbs/Sources/libextension/libextension
82/libextension/Common/NSExtensionContext.m:164: Unable to find
NSExtensionContextClass (_NCWidgetExtensionContext) in extension bundle -
did you link the framework that declares the extension point?
TimetablesInformation is targeted to both targets on the right inspector of Xcode... So I have no clue about what can I be doing wrong... Could you help me, please?
EDIT:
The getTimes method:
-(NSArray *)getTimes:(NSString *)actualView forLine:(NSString *)lineName {
if ([actualView isEqual: #(1)]){
if ([lineName isEqual: #"Point A - Point B"]) {
self.workingDayTimesArray = #[#"07:00",#"07:30",#"08:00",#"08:30",#"09:00",#"09:30",#"10:00", #"10:30",#"11:00",#"11:30",#"12:00",#"12:30",#"13:00",#"13:30", #"14:00",#"14:30",#"15:00",#"15:30",#"16:00",#"16:30",#"17:00", #"17:30",#"18:00",#"18:30",#"19:00",#"19:30",#"20:00",#"20:30", #"21:00",#"21:30",#"22:00",#"22:30"];
}
}
I have tried with a void method, but I have the same error. If I call a method from the other target (main app) I haven't crashes, but if I do from my widget, yes I do. Have you an idea of the cause?
It means that you or somebody accidentally removed a framework or library from the target you're running.
In my case, I was getting this crash error when somebody removed NotificationCenter.framework from "Linked Frameworks and Libraries" in General tab or "Link Binary With Libraries" in Build Phases tab. Thus, to fix it, you just need to add it again and everything will work.
So when you re-created TodayExtension target it linked NotificationCenter.framework by default. That's why everything worked for you after this.
My app gets stuck on splash screen in iOS 9 both on iPhone and simulator. I can run it on iOS 8 or lower on device and simulator with no problem. My colleague working on the same app has exactly the same problem.
There is no error or anything, just hangs on splash screen. If I stop it on xcode and try to run it from the phone or simulator directly, it would run without any problem.
By the way, I don't see didFinishLaunchingWithOptions or willFinishLaunchingWithOptions getting called!
In your "answer" you include the code:
+(void)initialize
{
titles = #[NSLocalizedString(#"CODE", nil), NSLocalizedString(#"ERROR", nil), NSLocalizedString(#"TROUBLESHOOTING", nil)];
}
This is indeed the source of your issue. It's wise to be very careful when implementing +load or +initialize. #bbum has a great article on exactly that topic.
+initialize is invoked the first time the class (or category) is touched - when the class is initialized +initialize is called by the class loading mechanism. There is no guarantee of when in the class loading process this may happen, which is part of your problem.
In your case you are using NSLocalizedString - which under the hood can be fairly heavy. It has dependancies on several other classes (NSString, etc) and can potentially access the file system. As #bbum points out in his article, that can lead to serious trouble. In your case, this may be a nasty deadlock.
Move your titles = #[NSLocalizedString... line to a more appropriate place in your object, like an initializer, awakeAfterUsingCoder:, etc. and your immediate problem should be solved. After doing so you should check your entire codebase for instances where +initialize and +load are implemented and audit them to make sure those uses are in line with #bbum 's recommendations.
OK I found the problem. It sounds ridiculous though!!
I am using UITabBarController and inside the first controller I have a UITableViewController with a customised datasource class which would initiate a hard code table header and these headers are localised!!
+ (void)initialize {
titles = #[NSLocalizedString(#"CODE", nil), NSLocalizedString(#"ERROR", nil), NSLocalizedString(#"TROUBLESHOOTING", nil)];
}
After I traced the stacks, I realised the process gets stuck right there with no trace and error! I still don't know why!
So I came up with a workaround:
+ (void)initialize {
titles = #[#"Code",#"Error",#"Troubleshooting"];
}
And only retrieve the localised value when returning the text:
- (NSString *)titleAt:(NSInteger)index {
return NSLocalizedString(titles[index],nil);
}
I have both debug and release set to NO
You sure "any SDK" also has arm64?
Ok, I think I found the answer.
You have to specify arm64 in all "Valid Architectures".
If you don't specify arm64 or forget one the app won't start and stays on the splashscreen.
Just verified this.
Is this an Xcode 7 bug?
I have upgraded to Xcode 5.0. And when I run an app in debug mode and try to print an NSString value in console, it gives me the below error. Any ideas?
error: warning: couldn't get cmd pointer (substituting NULL): Couldn't load '_cmd' because its value couldn't be evaluated
Couldn't materialize struct: the variable 'stringValue' has no location, it may have been optimized out
Errored out in Execute, couldn't PrepareToExecuteJITExpression
Here is the code:
NSString *stringValue = [[self.responseArray objectAtIndex:i] valueForKey:#"merchant_name"];
The reason is stated in the error message: it may have been optimized out.. this means that you are compiling and running your code in an optimized manner.
you gotta change your compiler optimization level from Fastest,Smallest to none:
go to your target build settings
search for optimization level
change it to none (whatever mode you are in ie debugging, distribution or release)
profit
do the same for your project settings
Make sure you are in debug mode. Go Edit Scheme > Build Configuration > Debug
You might be trying to debug in the "release Scheme". Go to "Product/Scheme/Edit Scheme" and pick the "run" in the left panel. Then change the build configuration to "debug".
One alternate answer: instead of fixing "it may have been optimized out" by removing the optimization, you can stop it from being optimized by using the variable.
So, in your question, if you do something with stringValue:
NSString *stringValue = [[self.responseArray objectAtIndex:i] valueForKey:#"merchant_name"];
NSLog(#"%#", stringValue);
stringValue will no longer be optimized out because you're using it.
If you want to see all instances of optimized-out variables in your project, Use Product -> Analyze to analyze your project. Any "Dead store" warnings (in which a value is stored, and never read) will be optimized out at compile time if you have compiler optimization turned on.