Objective-C: Do non-imported classes get compiled and linked into final binary? - objective-c

In Objective-C, do all source files in the project get compiled and linked into the final binary even if they are not imported by any classes?

That depends on your target settings. If you go to the target's Build Phases, anything listed under Compile Sources will be compiled.

If the source file is part of the build then its classes end up in the binary and available at runtime. That's because the runtime is reflective and can be queried later to find them. The compiler cannot be certain that won't happen.
EDIT: a classic use case is an informal protocol. You may write code like:
- (void)initWithObject:(id)object
{
self = [super init];
if(self)
{
_title = [object title];
_value = [object value];
}
return self;
}
Then you might decide you want to add an implementation of that informal protocol to e.g. NSString:
#interface NSString (MYPropertyProtocol)
#end
#implementation NSString (MYPropertyProtocol)
- (NSString *)title
{
return #"Content";
}
- (NSString *)value
{
return self;
}
#end
Just because nobody imports the NSString category doesn't mean the methods aren't used.
Going beyond that, in my current app we have a protocol like (this is a heavily cut-down version):
#protocol MYTableViewCell
+ (NSArray *)acceptedObjectClasses;
- (void)setObject:(id)object;
#end
Subsequently UITableView subclasses can opt to implement MYTableViewCell. If so they are found automatically by traversing objc_getClassList (and a lot of caching) and used automatically by my table view data source if it wants to display any object type declared as supported by acceptedObjectClasses. So that's taken most of the hassle out of writing the table view data source — I've got just one, which can accept any combination of objects used anywhere in the app, yet it's very short and easy mentally to check for errors, and need never grow even as the app learns about more things that need to be displayed in tables.
Nobody ever explicitly imports those table view cells, because it'd just mean having to do the same work twice — declare the class in Objective-C, which the compiler will check for me, then go and redeclare it to the custom cut-down lookup system that I've implemented instead of just using Objective-C's, which I probably need to do manual verification on.

Click on a .m file in the Project Navigator on the left side of Xcode, then open the right-hand tile (with the right-hand "View" button in the toolbar) and look at the "Target Membership" section. There is a checkbox with your project name beside it. If the box is checked, that .m file is included in the build. If the checkbox is not checked then the .m is essentially ignored.
You can also check this by clicking on your project name at the top of the ProjectNavigator column, selecting the project under "TARGETS", selecting "Build Phases", and selecting "Compile Sources". All of the included .m files will be shown and you can add or delete files from the list using the + - buttons at the bottom of the list.

Related

Swift CocoaPod Library in Objective-C Project Migration from Swift 3 to 4/5

I'm using a Swift CocoaPod in my Objective-C project. Originally it was a Swift 3 project and I simply had to add the #import "<Project Name>-Swift.h" into my .m or .h file. I've since updated to the latest version of the CocoaPod which I believe is now Swift 5. Therefore this is no longer working as none of the properties can be found.
I've looked at a number of different resources online including Apple's documentation and the procedure is unclear here what to do. It seems I have to go in and edit the .swift file and add #objc in front of each property or method I wish to access? This seems to go against what CocoaPods is about as the next time I update all my changes will be blown away.
I tried looking at the generated header but when I highlight the #import "<Project Name>-Swift.h" line I just get taken to the NSObject declaration. Obviously it is just a build time item I'm guessing.
According to the Apple documentation (https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/importing_swift_into_objective-c) the Swift declarations must be marked with the public or open modifier and when I checked the swift file I found that they indeed are.
The pod I'm using is HGCircularSlider. An example of what I'm trying to access is in the RangeCircularSlider class is here:
open var startPointValue: CGFloat = 0.0 {
didSet {
guard oldValue != startPointValue else { return }
if startPointValue < minimumValue {
startPointValue = minimumValue
}
if distance > 0 {
endPointValue = startPointValue + distance
}
setNeedsDisplay()
}
}
As we can see here the declaration is public. I'm assuming I need to set some sort of build setting?
In addition I have set use_frameworks! in my Podfile. I've also checked that the build target within the pod project and Objective-C Generated Interface Header Name is HGCircularSlider-Swift.h. This is exactly what I'm using in my Objc .m file.
Welcome any thoughts or comments where else I should look?
Update 1
One other thing of note. In my .h and .m I have a #property of type RangeCircularSlider which it can see fine. No error there. It's accessing the properties and methods within the object that is causing the errors.
Property 'startPointValue' not found on object of type 'RangeCircularSlider *'
So it sees the swift file but as originally stated, no properties can be found.
Update 2
So I've discovered that if I add #objc to the front of the individual properties I'm trying to access in the Swift file the generated header file then see's them and I can therefore access them. So for the example function above it would then be
#objc open var startPointValue: CGFloat = 0.0 {
But these seems to be a bit of a hack as for when I want to install an updated version of this CocoaPod, then those changes I made to the pod's file would be blown away.
Any suggestions how to do this without having to edit the swift files in the CocoaPod?
For anyone else that runs across this. The online resources I found were helpful but didn't detail this specific case.
First off. Leave the CocoaPod code as it is. This will allow you to update it without having to worry about modifying it each time.
Next create a Swift file within your Objective C Project. xCode will ask if you want a header generated for it, say yes. This file will be called -Swift.h.
In this Swift file subclass the Swift file from the CocoaPod you are interested in accessing.
For example :
import Foundation
import HGCircularSlider
class CircularSliderObjc: RangeCircularSlider {
}
Next add in the properties you wish to access with getter and setters.
#objc override open var startPointValue: CGFloat {
get {
return super.startPointValue;
}
set {
super.startPointValue = newValue;
}
}
Then finally change the import in your Objective-C file to your project's generated header file that I mentioned above (#import "-Swift.h"
). If you have a property pointing to the class in the CocoaPod change it to your new Swift Class. For example :
#property (weak, nonatomic) IBOutlet CircularSliderObjc *rangeSlider;
In this example I have it setup as an Outlet for InterfaceBuilder.
After that, you're done. It seems like a lot of work but it's quite easy and simple. Not quite as fast as just importing the header but since Swift 4 and 5 you can no longer just access open vars in Swift as properties in objc. They need the #objc declaration to make that work. Added security to the language I'm guessing.
Hope this helps someone.

Changing the implementation of Objective C method in Swift subclass

I am using an external library in my project. It is integrated via CocoaPods. I have access to the source code. The library is working well, but I need some modifications at some places in order for it to serve my purposes. It is written in Objective C. My project is in Swift.
I need to change the implementation of one method in the library. The problem is it is in the .m file and uses a lot of stuff declared only in the .m file. For example:
-(NSString*)methodToChange
{
NSArray<NSNumber*>* data = [self someInternalMethod:1];
uint value = data[0].unsignedIntValue;
return [self anotherInternalMethod:value];
}
I tried subclassing it like this:
class MySubclass : MySuperclassWithMethodToChange {
override var methodToChange: String {
//trying to use someInternalMethod and anotherInternalMethod
//unsuccessfully because they are not visible
}
}
The internal methods are using and modifying properties from the .m file that are also not accessible. Is there any way to solve this?
I would suggest forking the original library repository and making the necessary changes in your fork. You can then have your Podfile point to that. If your changes could be useful to others, make them in a way that doesn't break existing functionality and contribute them back to the library by opening a pull request.
If the original library gets updated later, you will have to merge those changes from the so-called "upstream" repository into yours. This does not happen automatically, so you'll have full control (and responsibility) over that process. See https://help.github.com/en/articles/syncing-a-fork for how this would look like.

no Objective-C description available - Why?

I really have no idea why on this particular project my debugger is extremely 'disabled'.
For example I'll want to get info on an object:
(lldb) po [_model dictionaryValue]
[no Objective-C description available]
I'm wondering why this is. It's making debugging extremely difficult and it's only on this current project. I'm guessing I've done something to the settings at some point. It's on almost any po someObject I try to inspect. The variables in scope can be seen in the pane to the left of the debug console however.
I'm on Xcode 5, I have Cocoapods in my project, and it's a Unit Testing Target.
Any insights or any way to fix this?
Update:
For clarity, part of how the test case is implemented:
#interface WWCGateModelTests : XCTestCase
{
WWCGate *_model;
}
#end
#implementation WWCGateModelTests
- (void)setUp
{
[super setUp];
// Put setup code here; it will be run once, before each test case.
_model = [WWCGate loadGateModelWithIdentifier: kGateName]; // defined, not nil
}
- (void)tearDown
{
[super tearDown];
NSError *error = nil;
[_model saveModelOrError:&error];
// Breakpoint here. po _model does not print the model.
// This has been possible with other projects... po error will print
// nil to the console. How is an ivar not in scope?
}
This is likely happening because Unit Testing Targets typically are set up to run with "Release" configurations. "Release" configurations are ones where the debugging symbols have been stripped or optimized away.
I suspect you won't have this problem if you make certain you're running with a non-optimized, symbols-in-place Debug version of your app. You can change that in Xcode's scheme editor (e.g. when doing "Test" or "Profile", use the "Debug" configuration).
Are you sure you aren't using it on primitive types? Use p intVariable on those.
Every object will respond to description by at least printing class and memory address.
I've tracked down the issue (but haven't fixed it fully yet). It has to do with the Mantle Framework. In its description method it wants to spit out the contents of an NSDictionary that it generates at that point. There's something wrong with the way I set up my model I believe so creating this dictionary (based probably on how I configured some property) is basically failing.
I overrode the description method to return a standard description:
- (NSString*)description
{
return [NSString stringWithFormat:#"<%#: %p>", self.class, self];
}
and everything is fine and good again on planet Earth. ;-)
Thanks to those who were particularly patient. A more detailed discussion about this issue can be found at UPDATE 3 of the following post: http://horseshoe7.wordpress.com/2013/05/26/hands-on-with-the-mantle-model-framework/

xcode 4.6.1 and LLVM 4.2: ld: 2 duplicate symbols for architecture armv7

My first attempt at building a company iOS library/framework was this week, by following the steps found at this blog post here.
For reasons beyond this question, I can only link when building for a Device and not for simulator.
However, now I am getting a very bizarre error:
ld: 2 duplicate symbols for architecture armv7 clang: error: linker
command failed with exit code 1 (use -v to see invocation)
The lines in question suggest:
duplicate symbol _OBJC_CLASS_$_iContactsGridCell in:
/Users/*/Desktop/Projects/contactservice/branch/ContactServicesClient/DerivedData/iContacts/Build/Intermediates/iContacts.build/Debug-iphoneos/iContacts-5.1.build/Objects-normal/armv7/iContactsGridCell.o
/Applications/Xcode.app/Contents/Developer/Library/Frameworks/athium-iOS.framework/athium-iOS
duplicate symbol _OBJC_METACLASS_$_iContactsGridCell in:
/Users/*/Desktop/Projects/contactservice/branch/ContactServicesClient/DerivedData/iContacts/Build/Intermediates/iContacts.build/Debug-iphoneos/iContacts-5.1.build/Objects-normal/armv7/iContactsGridCell.o
/Applications/Xcode.app/Contents/Developer/Library/Frameworks/athium-iOS.framework/athium-iOS
Obviously the Class in question is iContactsGridCell.h
The class itself inherits from Cell.h
#interface iContactsGridCell : Cell
The class Cell.h is part of the framework, if I do not import the correct file, then as expected I get a semantic error: Cannot find interface declaration for Cell etc etc.
However, when I do import it, I get the following duplicate error.
This file is not included anywhere else in the project, apart from iContactsGridCell.h!
The file Cell.h in turn, is just a class found under the framework:
#interface Cell : NSObject
#property (strong,nonatomic) UIView *view;
#property CGRect rect;
#property int UID;
#property BOOL highlighted;
#property UIColor *b_colr;
- (id) initWithRect:(CGRect)frame;
- (BOOL) hasCoordinates:(CGPoint)coord;
- (void) ripple;
- (void) cubefy;
- (void) flipfy;
- (void) rotate;
- (void) setBg:(UIColor *)bg withAlpha:(CGFloat)alpha;
- (void) highlight;
- (void) unhighlight;
- (void) updateWithRect:(CGRect)rect;
#end
Why on earth am I getting the duplicate error?
What could be causing this?
How could it get fixed?
How can I get more info more verbose output of where the duplicates are found?
PS: I have followed the intructions found at the blog to the letter. Yet I cannot link for simulator (getting a wrong architecture error) so my guess that maybe something is broken in the framework and not the project itself. Could this be the reason for the duplicate errors?
There are several places where this error could be arising. I would do the following to start searching for the problem:
Search the project folder with finder and see if anywhere else the file "iContactsGridCell.h" exists. Or if any two files exist somewhere.
Make sure that you don't have two Objects that are of Class iContactsGridCell that share the same name for example:
iContactsGridCell *myObj;
iContactsGridCell *myObj;
Make sure your not doing anything like this: #import
"iContactsGridCell .m"
Or like this: #import "iContactsGridCell.h" #import "iContactsGridCell.h"
Make sure your not re-declaring a class (including ones that Apple has provided in their frameworks)
Main Point: That's all I can think of off the top of my head. But the thing to remember is that somewhere in your project resides two objects, class declarations etc. That are the same. And the compiler is complaining because of it.
Why the Compiler would complain (more info): While this information is really "unnecessary" it's good practice as a programmer to understand what's going on under the hood and will often help debug, so here's a little info on compilers:
Remember that a Compiler (while much more complex) is a program just like the ones that you create using it. One of the steps in most (if not all) compilers go through at one point or another reading code is creating "Symbols or Keys" for each variable, class, struct ect. So, at some point the compiler reached a line of code that "repeats / duplicates" somewhere else. So the "Key/Symbol" creation process done routine by the compiler fails.

How do i interface my objc program with an objc++ library?

I have an objc program and i would like to use a widget that is written in objc++ (namely https://launchpad.net/scintilla-cocoa). How do i go about this? Basically i want a new window controller object to interface with this objc++ library to define a scintilla text editor widget. Simply creating a new 'objc class' and accessing the library from there generates a bunch of errors related to the C++ class keyword and so on.
Thanks in advance
Since I'm the one who put you into the (hopefully rewarding :-)) trouble of using Scintilla, here I am.
Let's say we create a ScintillaView subclass, named ppScintillaEditor.
The file should have an .mm extension (e.g. ppScintillaEditor.mm)
The code would be roughly like this...
Interface
#import "Scintilla/ScintillaView.h"
#interface ppScintillaEditor : ScintillaView
{
// your iVars
}
// your properties / methods / whatever
Now, as for the implementation part, remember to put some initialization method to set up the view properly (as in the example accompanying Scintilla-cocoa; I mean the Test project)
Sidenote : Of course, you can create subclasses, categories or whatever on top the ScintillaView class, pretty much based on what you need - I, for example, have create a separate Category just in order to group there some ScintillaView specific commands (sooner or later, you'll notice that for some more advanced Scintilla manipulations, although it's there, it may need some polishing to be a bit more cocoa-friendly, so here you go...)
Now, last but not least...
To resolve the "bunch of errors related to the C++ class keyword and so on", as I've shown in my other video-response to your comment, all you have to do is :
Go to your project's Build Settings
Under Apple LLVM Compiler 3.0 - Preprocessing
Option Preprocessor Macros
Add to both Debug and Release :
SCI_NAMESPACE SCI_LEXER
And that's it. :-)
Hint : The above are defined by Scintilla to avoid clashes between C and non-C elements, like above... so, all it takes is to notify the preprocessor and the rest is taken care of....
you would create an objc class which has the interface your app needs, then implement and add the ivars and implement -- all behind a compilation firewall so the objc++ sources are not included in the header. your implementation would provide any necessary conversions.
it is like you have already done, but you remove the scintilla headers from the header for your wrapper -- they are visible only to your wrapper's implementation.
Update
To illustrate one possible approach:
MONScintillaWrapper.h
// no c++/scintilla sources should be included in this header
#import <Foundation/Foundation.h>
#interface MONScintillaWrapper : NSObject
- (void)setBackgroundColor:(NSColor *)pColor;
#end
MONScintillaWrapper.mm
#import "MONScintillaWrapper.h"
#implementation MONScintillaWrapper
{
scintilla::t_thing scintillaThing;
}
- (void)setBackgroundColor:(NSColor *)pColor
{
...convert pColor to a scintilla color and pass that to scintillaThing...
}
#end