The WWDC 2013 lecture slides contain a section that in some cases the explicit bridging casts can be omitted. I wrote this in Xcode 5 (using 10.8 though, not 10.9), and the compiler complains I need a bridging cast. Am I just totally not understanding the concept?
#import <Foundation/Foundation.h>
CF_IMPLICIT_BRIDGING_ENABLED
CFStringRef MyCreateStringFromNothing();
CF_IMPLICIT_BRIDGING_DISABLED
void SomeFunction() {
// compiler requires bridging cast here...
NSString* x = MyCreateStringFromNothing();
}
The reason I ask is I wanted to write:
NSString* s = CFUUIDCreateString(NULL, uuid);
and thought that the new work on implicit bridging should simply enable me to write this. But this also needs a bridging cast.
This appears to be sort of black magic, but from my experiments functions with "Create" in them don't work with implicit bridging.
When I compiled this:
CF_IMPLICIT_BRIDGING_ENABLED
CFStringRef MyCreateStringFromNothing();
CFStringRef MyGetStringFromNothing();
CF_IMPLICIT_BRIDGING_DISABLED
void SomeFunction() {
NSString *const fails1 = MyCreateStringFromNothing();
NSString *const fails2 = CFStringCreateCopy(kCFAllocatorDefault, CFSTR("FOO"));
NSString *const works = MyGetStringFromNothing();
}
The first two fail but the third one "works."
Note that implicit bridging apparently STILL does NOT actually keep the compiler from warning you about converting CFStringRef to NSString *, it just stops requiring you to insert a __bridge as well.
You'll notice that in slide at WWDC2013 in session 404 at 50:30, he uses CFDictionaryGetValue(), which returns a "void *", so it's one of the few functions that won't raise a warning. Any of the CF functions that return an actual type (e.g. CFStringRef, or CFDictionary) are still going to throw warnings, so, meh.
Check the compiler settings on the project and/or target. If this is an older project, you might have a stray option there causing you to use an older compiler. (This tripped me up a few times when I moved to Xcode 5 and wanted to use the latest Obj-C newness.)
These days, I always choose "Default compiler" in my build options.
Related
Recently i noticed strange behaviour with Objective C memory management in Xcode.
Here is the code:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSString *firstString = [[NSString alloc] initWithString: #"Hello"];
NSString *secondString = firstString;
[firstString release];
NSLog(#"%#", secondString);
}
return 0;
}
I think, that secondString points to nil after releasing firstString and NSLog should produce error.
But this code does not produce any errors and successfully prints "Hello" string.
I manually compiled and run code with such command and does not noticed any errors too:
% clang -framework Foundation -fno-objc-arc main.m && ./a.out
I tried to compile this code with online objective-c compiler (GCC) (http://rextester.com/l/objectivec_online_compiler) and error was occurred.
What am i doing wrong?
ARC support is turned off in Xcode.
Thanks in advance.
If you perform a static analysis (shift+command+B in Xcode, or "Analyze" on Xcode's "Product" menu), it will warn you that you're trying to reference an object after it is released:
The issue is that in manual reference counting code, your references to deallocated objects are not set to nil automatically. So you can end up with dangling pointers to an object that was previously deallocated unless you manually nil those pointers.
The static analyzer is remarkably good at identifying these sorts of issues, amongst others. I would advise making sure you have a clean bill of health from the static analyzer before moving on.
The second line of defense in situations like this is to enable the zombies runtime debugging option. This will report any attempts to interact with objects after they've been deallocated. Zombies can be enabled in the "Diagnostics" section when you edit your Xcode target's scheme.
Unfortunately, you are using NSString, which doesn't follow the typical memory management rules (it can keep its own references to strings, so they're not always deallocated when you'd otherwise expect them to be).
Consider an example similar to yours, except with a custom class:
#import <Foundation/Foundation.h>
#interface MyObject: NSObject
#end
#implementation MyObject
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
MyObject *firstObject = [[MyObject alloc] init];
MyObject *secondObject = firstObject;
[firstObject release];
NSLog(#"%#", secondObject);
}
return 0;
}
If you run this with zombies on, you'll get a corresponding error message indicating that you're trying to interact with a deallocated instance:
2017-05-27 08:19:18.154033-0700 MyApp[36888:7215135] *** -[MyObject isProxy]: message sent to deallocated instance 0x100303620
But you may not get this warning from NSString. Bottom line, one should avoid drawing any broader memory management conclusions from NSString behavior. Instead, rely upon Xcode's static analyzer and zombies. (Note, remember to turn off zombies when you're done debugging your app.)
I have an NSString category class (NSString+URLEncoding.h).
I am running into and unknown selector crash, because the string I am calling the category method has been optimized into an NSCFConstantString by iOS.
-[__NSCFConstantString URLEncodedString]: unrecognized selector sent to instance 0x290174
I learned of the NSCFConstantString vs. NSCFString optimizations in iOS 5 from:
http://www.cocoanetics.com/2012/03/beware-of-nsstring-optimizations/
Is anyone aware of how I can get the NSString category to include the Constant strings or even force the var to be an NSString/NSCFString and not an NSCFConstantString?
Cheers,
Z
-edit-
Linker flags -ObjC -all_load are both already implemented
NSString+URLEncoding.m is included in the targets compile sources
NSString+URLEncoding.m implements the URLEncodedString method.
Checked for zombies.
I am adding a sharing service to ShareKit 2.0
header:
#interface NSString (OAURLEncodingAdditions)
- (NSString *)URLEncodedString;
implementation:
#implementation NSString (OAURLEncodingAdditions)
- (NSString *)URLEncodedString
{
NSString *result = (NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
(CFStringRef)self,
NULL,
CFSTR("!*'();:#&=+$,/?%#[]"),
kCFStringEncodingUTF8);
[result autorelease];
return result;
}
There's an issue with the linker that can cause its dead-code stripping to completely omit any object files that only contain obj-c categories (or that are otherwise unreferenced). Theoretically passing the -ObjC flag to the linker should fix this, but that doesn't seem to always work. You can work around this issue by providing the -all_load linker flag, which will cause the linker to always link in all object files.
Note that you might have to set -all_load on the parent project if your category is part of a sub-project or library that you-re including somewhere.
Update: I believe -ObjC is reliable now and has been for years so you can stop using -all_load for this issue.
Just spent 30 minutes figuring out exactly the same issue. After fiddling with linker I found out that the category wasn't present in Compile Sources list in my target's Build Phases. Be sure to check it's there.
__NSCFConstantString is a subclass of NSString, so any categories on NSString apply to __NSCFConstantString too.
Either you're not linking in your category, or your category doesn't define a URLEncodedString method in its #implementation.
Cocoa Touch Frameworks provide support for versioning, which can be found in Build Settings under the Versioning section.
To access this value at runtime, we can use the FrameworkVersionNumber and FrameworkVersionString[] variables which are auto-generated for us as part of the build process.
When working with a Swift project, these can be found auto-generated at the top of the Objective-C compatibility header:
//! Project version number for Framework.
FOUNDATION_EXPORT double FrameworkVersionNumber;
//! Project version string for Framework.
FOUNDATION_EXPORT const unsigned char FrameworkVersionString[];
However, whilst FrameworkVersionNumber is accessible from Swift, FrameworkVersionString[] is not. In fact looking at the contents of the framework module, I can see that only the first variable is exposed to Swift:
//! Project version number for Framework.
var FrameworkVersionNumber: Double
The problem with this is that since FrameworkVersionNumber is a Double, any version numbers like 3.2.1 simply get changed to 3.200000...
Does anyone know whether this is a flaw in my project setup, a bug in Xcode, or whether there is a way of getting the framework version in Swift as a String or array, so that I can provide more granular versioning than major.minor?
I have actually found a potential workaround for this issue, it's not so clean but it does work:
By default, when Xcode creates a framework it sets the Version to 1.0 and the Build to $(CURRENT_PROJECT_VERSION) which is great, because this value is actually being copied from the Current Project Version field in Build Settings > Versioning.
So what you can do to get this value at runtime is as follows:
let bundle = NSBundle(identifier: "com.yourframework.Framework")! // Get a reference to the bundle from your framework (not the bundle of the app itself!)
let build = bundle.infoDictionary![kCFBundleVersionKey] as! String // Get the build from the framework's bundle as a String
This does work but it feels quite circuitous for something that used to (I believe) be readily accessible from a variable in Objective-C.
IMPORTANT UPDATE - OCT 2021 - XCODE 13
When submitting an app to the App Store, Xcode 13 has a new option called "Manage Version and Build Number" which is ticked by default. If left checked, Xcode will automatically set your app's version number which (rather counter-intuitively), will also apply to all included frameworks. In other words, if your app version is 1.0, your framework version will be overwritten with 1.0.
Make sure you disable this option to avoid your framework version being overwritten.
You can also opt-out of this new behaviour by setting manageAppVersionAndBuildNumber in your export options plist.
For further details, see this discussion on the Apple Developer Forums.
These variables are populated in an automatically generated .c file when building from the project's CURRENT_PROJECT_VERSION. It looks like this:
extern const unsigned char FrameworkVersionString[];
extern const double FrameworkVersionNumber;
const unsigned char FrameworkVersionString[] __attribute__ ((used)) = "#(#)PROGRAM:Mark2SDK PROJECT:Framework-1" "\n";
const double FrameworkVersionNumber __attribute__ ((used)) = (double)1.;
The C array doesn't make it to Swift for some reason. Modifying the array definition to a pointer causes an EXC_BAD_ACCESS crash. What you can do though is create a pointer to the array and use that. In Framework.h:
//! Project version string for Framework.
FOUNDATION_EXPORT const unsigned char FrameworkVersionString[];
// add this
extern const unsigned char * FrameworkVersionStringPtr;
Then, either by creating Framework.c or in another c or m file, add this:
#import "Framework.h"
const unsigned char * FrameworkVersionStringPtr = FrameworkVersionString;
You can then use the string pointer in Swift to get the version:
func version() -> String? {
let ver = String(cString: Mark2SDKVersionStringPtr)
guard let range = ver.range(of: "-") else {
return nil
}
return String(ver[range.upperBound...])
}
print(version())
// 1.0.1
for swift 4.2 it works:
if let bundle = Bundle(identifier: "com.ingconti.SampleFramework") {
if let build = bundle.infoDictionary?["CFBundleShortVersionString"] {
print(build)
}
}
I have gone through the ADLivelyTableView demo project but have not been able to import the ADLivelyTableView h and m files into my project successfully. It appears that the main issue is to do with ARC. I have experimented by converting the demo project into arc, specifically but converting just the LDMasterView.m file, and this simply removes all references to releasing objects, and so after this conversion, the use ARC option under build settings is now ON and the app works. So i figured that the ADLivelyTableView .m and .h files dont need converting, but when these are imported into my project, i get all sorts of ARC errors for these two blocks of code:
if (block != _transformBlock) {
Block_release(_transformBlock);
_transformBlock = Block_copy(block);
}
}
and
#implementation ADLivelyTableView
- (void)dealloc {
Block_release(_transformBlock);
[super dealloc];
}
I dont get why these errors didnt show when turning on ARC in the demo project. id prefer finding a solution rather than trying to import my entire application to the demo project instead! The errors are as follows:
ARC Casting Rules: Cast of block pointer type 'ADLivelyTransform (aka NSTimeINterval (^)CALayer*_strong, float) to C pointer type 'const void *' required a bridged cast.
ARC Casting Rules: Cast of C Pointer ....(Same as above)
Also, once this issue is resolved, it is supposed to be as simple as just importing the ADLivelyTableView .h and .m files and then adding the line :
ADLivelyTableView * livelyTableView = (ADLivelyTableView *)self.tableView;
livelyTableView.initialCellTransformBlock = ADLivelyTransformFan;
into my viewDidLoad section? or is that bit supposed to be edited for my specific table?
Thanks for your help,
Regards,
Rami
You can modify the Compiler Flags for ADLivelyTableView.m.
Kindly try to add -fno-objc-arc.
I have a library of my own. And there are lots of constants defined in headers (for example animation duration). But, all my headers are visible and changeable. How can I prevent others to change my default values?
There are some const value in headers of Apple libraries like this:
CA_EXTERN NSString * const kCATransitionMoveIn
__OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);
Objective-C is still, well, C. Maybe older systems had issues, which is why you see the macros there. Anyway, you should still be safe with any built-in type.
If you declare something as "extern" the compiler will treat it something like, "OK, I see that someone has declared and external thingy. I don't have to know what it is, because some external unit will define it. The linker will handle the rest.
That paragraph will get me in trouble with the C-police, but it's close enough for a practical explanation. Thus, you can do this in your header file...
extern int const TheAnswerToLifeTheUniverseAndEverything;
And then, in one of your implementation files (outside of the #implementation/#end section)...
int const TheAnswerToLifeTheUniverseAndEverything = 42;
Note, in "modern" Xcode versions, you can do the same thing with objects. Note the "const" which means we have a constant-pointer-to-NSString.
// In header
extern NSString * const TheAnswerToLifeTheUniverseAndEverythingString;
// In source
NSString * const TheAnswerToLifeTheUniverseAndEverythingString = #"42";