Linker cannot find a class that should be there - objective-c

I have a project that uses the Core Telephony framework. Recently my code stopped working on a CTCarrier category, the linker complains that it can’t find the CTCarrier class:
Undefined symbols:
"_OBJC_CLASS_$_CTCarrier", referenced from:
l_OBJC_$_CATEGORY_CTCarrier_$_Foo in CTTests.o
ld: symbol(s) not found
This is a sample code that triggers the error above:
#import <CoreTelephony/CTCarrier.h>
#interface CTCarrier (Foo)
- (void) doFoo;
#end
#implementation CTCarrier (Foo)
- (void) doFoo {}
#end
If I change the category to class extension, the code suddenly builds:
#import <CoreTelephony/CTCarrier.h>
#interface CTCarrier ()
- (void) doFoo;
#end
#implementation CTCarrier
- (void) doFoo {}
#end
What’s going on? Sample code on GitHub.

There is a bug in 4.2 that doesn't allow the direct creation of a CTCarrier object, the proper way to access CTCarrier is via the CTTelephonyNetworkInfo object like so:
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#import <CoreTelephony/CTCarrier.h>
CTTelephonyNetworkInfo *telephony = [[CTTelephonyNetworkInfo alloc] init];
CTCarrier *carrier = telephony.subscriberCellularProvider;
[telephony release];

In the first example you don't really are implementing CTCarrier class but only add a method to it. The categories provide a way to add methods to an already defined implementation.

Related

Objective-C warning: Class 'ViewController' does not conform to protocol 'CBPeripheralManagerDelegate'

I'm trying to create an Obj-C, CoreBluetooth virtual peripheral app and get this warning.
//
// ViewController.h
// sim_backend_empty3
//
//
#import <UIKit/UIKit.h>
#import <CoreBluetooth/CoreBluetooth.h>
#interface ViewController : UIViewController <CBPeripheralManagerDelegate>
#property (nonatomic, strong) CBPeripheralManager *peripheralManager;
#end
//
// ViewController.m
// sim_backend_empty3
//
//
#import "ViewController.h"
#implementation ViewController >>>>>>>>>> WARNING >>>>>>>>>> Class 'ViewController' does not conform to protocol 'CBPeripheralManagerDelegate'
- (void)viewDidLoad {
[super viewDidLoad];
}
-(void)start_BLE_advertisements
{
[[CBPeripheralManager alloc] initWithDelegate:self queue:nil options:nil];
}
#end
You need to implement the required CBPeripheralManagerDelegate protocol method:
peripheralManagerDidUpdateState:
as mentioned in the documentation here: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanagerdelegate?language=objc
The protocol’s required one method, peripheralManagerDidUpdateState:, which Core Bluetooth calls whenever the peripheral manager’s state updates to indicate whether the peripheral manager is available.
You can check out an Objective-C example of how to setup CoreBluetooth using this repo:
https://github.com/LGBluetooth/LGBluetooth
Back when I still coded in ObjC, it was the library I used - and it was pretty great.
At the very least, it will give you some ideas on how you should be implementing the interfaces.
Here is a Swift implementation of what it sounds like you're trying to do (based on your other question asked a few days ago). Maybe you can backport it to ObjC (disclaimer: I'm the author).
https://github.com/RobotPajamas/SwiftyTeeth/blob/master/Sources/SwiftyTooth/SwiftyTooth.swift

Objective C run 'private' method defined in one .mm file from another .mm file

There are many questions about accessing 'private' (I hear technically there is no such thing as a private method in Obj-C) messages in Obj-C. And there are many questions addressing No visible #interface for SomeClass declares the the selector 'SomeMethod'. However, there is not one that addressed both.
So here is some code.
Example.h
#import <Cocoa/Cocoa.h>
#interface Example : NSView
#end
Example.mm
#import "Example.h"
#interface Example()
- (void) printWordOne:(NSString*) firstWorld wordTwo:(NSString*) secondWord;
#end
#implementation Example
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
// Drawing code here.
}
- (void) printWordOne:(NSString*) firstWorld wordTwo:(NSString*) secondWord{
NSLog(#"The two words are %# %#", firstWorld, secondWord);
}
#end
ViewController.h
#import <Cocoa/Cocoa.h>
#import "Example.h"
#interface ViewController : NSViewController{
IBOutlet Example *example;
}
#end
The IBOutlet has been connected in storyboard.
ViewController.mm
#import "ViewController.h"
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[example printWordOne:#"Hello" wordTwo: #"World"];
}
- (void)setRepresentedObject:(id)representedObject {
[super setRepresentedObject:representedObject];
// Update the view, if already loaded.
}
#end
The issue I am having is with this method call.
[example printWordOne:#"Hello" wordTwo: #"World"];
The error is No visible #interface for 'Example' declares the selector 'printWordOne:wordTwo'
I need a way to call that function without declaring it in the Example.h file. If I #import Example.mm in my ViewController.mm file I get the following:
duplicate symbol _OBJC_CLASS_$_Example in:
/path/Example.o
/path/ViewController.o
duplicate symbol _OBJC_METACLASS_$_Example in:
/path/Example.o
/path/ViewController.o
ld: 2 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I know using class_copyMethodList I can get the method list and list that method from ViewController.mm. But again is there anyway to execute the method.
Any help would be greatly appreciated.
You can simply declare category to Example class with private method declaration inside your ViewController.mm:
#import "ViewController.h"
#interface Example()
- (void) printWordOne:(NSString*) firstWorld wordTwo:(NSString*) secondWord;
#end
#implementation ViewController
// ...
#end

Creating an Instance of Class in IOS 6 [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I have a problem that I hope you can help me with. I have an app with several storyboard views, each with a separate viewcontroller. I want to be able to use my own class in all views in the storyboard. This will make the code in each viewcontroller much cleaner and the whole app easier to debug etc. The class will contain variables and methods.
The overall aim for me is to collect data from the user via buttons and then store these in a database. It will be possible to view and amend data, as well as generating statistics.
As most variables and methods will be used in different views and at different times, I would like to separate all these in separate files.
I can also tell you that this is not a lazy short-cut attempt from me, I have surfed the internet for many, many hours reading hundreds of posts etc and I am still nowhere nearer a solution.
Any input is very much appreciated!
Thank you for taking your time to read this...
SomeClass.h has the following code:
#import <Foundation/Foundation.h>
#interface SomeClass : NSObject
{
NSString *dataOne;
NSString *dataTwo;
NSString *dataThree;
}
- (void) SetDataOne: (NSString*) dataOneReceived;
- (void) SetDataTwo: (NSString*) dataTwoReceived;
- (void) SetDataThree: (NSString*) dataThreeReceived;
- (void) saveSomeData;
#end
SomeClass.m has the following code:
#import "SomeClass.h"
#implementation SomeClass
- (void) SetDataOne: (NSString*) dataOneReceived {
dataOne = dataOneReceived;
}
- (void) SetDataTwo: (NSString*) dataTwoReceived {
dataTwo = dataTwoReceived;
}
- (void) SetDataThree: (NSString*) dataThreeReceived {
dataThree = dataThreeReceived;
}
- (void) saveSomeData {
// Here I do stuff with dataOne etc…
}
#end
SomeView.h has the following code:
#import <UIKit/UIKit.h>
#import "HeadViewController.h"
#import "SomeClass.h"
#interface SomeView : UIViewController
// contains stuff not needed to show here
- (IBAction)Done:(id)sender;
#end
SomeView.m has the following code:
#import "SomeView.h"
#import "SomeClass.h"
#interface SomeView ()
#end
#implementation SomeView
- (void)viewDidLoad
{
[super viewDidLoad];
SomeClass *someClassObject = [[SomeClass alloc] init];
// Do any additional setup after loading the view.
}
// Other standard methods omitted
- (IBAction)Done:(id)sender {
[someClassObject SetDataOne: #”whatever text”];
[someClassObject SetDataTwo: #”whatever text”];
[someClassObject SetDataThree: #”whatever text”];
[someClassObject SaveSomeData];
Error Msg for all the above: ”Use of Undeclared Identifier ’someClassObject’
}
#end
Comment: You can see the error message I get at the end of the code above. I have no clue what I am doing wrong. I have looked at a lot of examples on how to create and call classes, but cannot seem anything that solves my problem. Also, I see that some of the put the ”SomeClass *someClassObject = [[SomeClass alloc] init];” in the ”main.m file”. If I understand correctly, that file is the first one to load when app starts. If so, then I cannot place it there as I will have to create instances of my class in several different views and other times than when the app starts. That is why I have placed it in the viewDidLoad-method.
A couple of thoughts:
You've made someClassObject a local variable of the viewDidLoad method. Looks like you meant to make it a class instance variable (or, better, a private class property, which will have the instance variable synthesized for you). Thus:
#interface SomeView ()
#property (nonatomic, strong) SomeClass *someClassObject;
#end
#implementation SomeView
- (void)viewDidLoad
{
[super viewDidLoad];
self.someClassObject = [[SomeClass alloc] init];
}
- (IBAction)done:(id)sender {
[self.someClassObject setDataOne: #"whatever text"];
[self.someClassObject setDataTwo: #"whatever text"];
[self.someClassObject setDataThree: #"whatever text"];
[self.someClassObject saveSomeData];
// should resolve the Error Msg for all the above: ”Use of Undeclared Identifier ’someClassObject’
}
BTW, as a matter of convention, your method names should start with a lowercase letter (e.g. setDataOne not SetDataOne, done rather than Done, etc.), as illustrated above.
If you're going to write your own setters, setDataOne, setDataTwo, etc., you might as well remove those three instance variables, remove your three setData___ methods, and replace the three instance variables with class properties (and let the compiler synthesize not only the instance variables, but the setters, too).
someClassObject is set inside viewDidLoad and because it is not stored anywhere inside the view will be deleted at the end of that very same method
You should add your object inside each header file's interface section like this:
SomeView.h
#import <UIKit/UIKit.h>
#import "HeadViewController.h"
#import "SomeClass.h"
#interface SomeView : UIViewController
{
SomeClass *someClassObject;
}
// contains stuff not needed to show here
- (IBAction)Done:(id)sender;
#end
Then when you instantiate someClassObject inside ViewDidLoad it will persist throughout that view.

Why Does This Category Work? Category vs. Class Extension

This is what I have in my implementation file for one of my classes...
Code Setup #1
#interface MyViewController (PrivateMethods)
- (NSString *)myPrivateMethod;
#end
#implementation MyViewController
- (void)viewDidLoad
{
NSString *myString = [self myPrivateMethod];
NSLog(#"%#", myString);
}
- (NSString *)myPrivateMethod
{
return #"someString";
}
#end
With this code, everything works and it logs "someString".
But shouldn't my code look differently somehow? I actually am using that category by accident (I had copy/pasted something and didn't notice "PrivateMethods" was there; I meant to be using a class extension).
Shouldn't my code actually look like one of the following:
Code Setup #2
#interface MyViewController ()
- (NSString *)myPrivateMethod;
#end
#implementation MyViewController
....
Or:
Code Setup #3
#interface MyViewController (PrivateMethods)
- (NSString *)myPrivateMethod;
#end
#implementation MyViewController (PrivateMethods)
....
What are the nuances behind what is happening in this situation? How is Code Setup #1 different from Code Setup #2?
Edit: Question about Setup #3
What does setting it up like this accomplish? Would this even "work"?
#interface MyViewController (PrivateMethods)
- (NSString *)myPrivateMethod;
#end
#implementation MyViewController
- (void)viewDidLoad
{
NSString *myString = [self myPrivateMethod];
NSLog(#"%#", myString);
}
#end
#implementation MyViewController (PrivateMethods)
- (NSString *)myPrivateMethod
{
return #"someString";
}
#end
the selectors just get pushed into the same flat namespace at runtime. the compiler adds no additional code to distinguish that the selector is a method defined in a category (when messaging) --it's all flat.
the categories' symbols are exported differently, but that does not really matter to the runtime once loaded.
you should generally use Setup #3: if a method is declared in a category, it should be defined in the category's #implementation. the compiler will save you occasionally and it is a purer structure. (of course, not every method belongs in a category). Similarly, the declarations in the #interface should be defined in the corresponding #implementation, and definitions of declarations in the class continuation (#interface MONClass ()) should also appear in the primary #implementation:
#implementation MONClass
// add your #interface MONClass definitions here
// also add your #interface MONClass () definitions here
#end
Updated Question
Yes, that would work fine. All you should need to do is #import the header which contains #interface MyViewController (PrivateMethods). I actually do this in some classes to categorize/organize by topic.
Typically, "Private Methods" are declared in the class continuation, but it is not necessary to do so (ivars/properties OTOH…).

Objective-c class extensions not effectives in static library

while creating a library that will be used on several projects, I encountered an error that I was not able to resolve by myself.
The library is composed of several "modules" that each declares its set of classes. The modules declares a header file that references the classes. Each module header is included in the library header, and all of them are copied to the library target.
The "GMData" module defines the ORM layer of the library, it declares a "GMInitializerBase" class, its purpose is to initialize the module. It must be called once in the UIApplicationDelegate.
The "GMModel" module contains the base model for the application (Categories, Articles, ...), It must register itself to "GMData" in order to function properly.
Structure:
<Library Root>
Library.h
<GMData>
GMData.h
GMInitializerBase.{h,m}
<GMModel>
GMModel.h
GMInitializerBase+GMModel.{h,m}
Contents of Library.h
#import "GMData.h"
#import "GMModel.h"
Contents of GMData.h
#import "... ORM related headers ..."
#import "GMInitializerBase.h"
Contents of GMInitializerBase.{h,m}
#import "... ORM Classes ..."
#interface GMInitializerBase : NSObject {
}
+ (void) bootstrap;
+ (GMInitializerBase*) initializer; // autoreleased instance creator
- (void) setup;
- (void) setupStore:(GMManagerFactory*)factory; // Setup database connection
- (void) setupHelpers:(GMHelperFactory*)factory; // Register helpers (abstract)
- (void) setupManagers:(GMManagerFactory*)factory; // Register managers (abstract)
#end
#implementation GMInitializerBase
+ (void) bootstrap {
GMInitializerBase* initializer = [self initializer];
[initializer setup];
}
- (void) setup {
/* Breakpoint 01 */
GMHelperFactory* helperFactory = [GMHelperFactory sharedInstance];
GMManagerFactory* managerFactory = [GMManagerFactory sharedInstance];
[self setupStore:managerFactory];
[self setupHelpers:helperFactory];
[self setupManagers:managerFactory];
}
#end
Contents of GMModel.h
#import "... Base Models files ..."
#import "GMInitializerBase+GMModel.h"
Contents of GMInitializerBase+GMModel.{h,m}
#interface GMInitializerBase (GMModel_Additions)
- (void) setup;
- (void) setupGMModelHelpers:(GMHelperFactory*)factory;
- (void) setupGMModelManagers:(GMManagerFactory*)factory;
#end
#implementation GMInitializerBase (GMModel_Additions)
- (void) setup {
/* Breakpoint 02 */
GMHelperFactory* helperFactory = [GMHelperFactory sharedInstance];
GMManagerFactory* managerFactory = [GMManagerFactory sharedInstance];
// parent implementation
[self setupStore:managerFactory];
// current implementation
[self setupGMModelHelpers:helperFactory];
[self setupGMModelManagers:managerFactory];
// parent implementation
[self setupHelpers:helperFactory];
[self setupManagers:managerFactory];
}
- (void) setupGMModelHelpers:(GMHelperFactory*)factory { /* ... */ }
- (void) setupGMModelManagers:(GMManagerFactory*)factory { /* ... */ }
#end
Contents of ProjectAppDelegate.m (located in another project, it includes the library.a and search the "Headers" directory)
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[GMInitializerBase initializer] setup];
}
Stops at the first breakpoint (Breakpoint 01)
It crashed when in the library:
I declare an addition without overloading a method;
I declare an addition to a Cocoa class ([NSString toto]) without overloading;
In works when in the test project:
I declare an addition to a Cocoa class ([NSString toto]) without overloading;
I didn't try to overload a library class but I assume it will work too.
My problem is the following: I can't get the addition workingm and I need it.
Thanks for reading, thanks for answering.
Make sure you have the -all_load and -ObjC flags set in the "Other Linker Flags" in the project settings. Categories in a library won't work without them.
In Objective-C, you shouldn't override the method in a category of a class. Say you have
#implementation MyClass
-(void)foo
{
NSLog(#"%#",#"original!");
}
#end
and later you have
#implementation MyClass (MyCategoryA)
-(void)foo
{
NSLog(#"%#",#"categoryA!");
}
#end
#implementation MyClass (MyCategoryB)
-(void)foo
{
NSLog(#"%#",#"categoryB!");
}
#end
Then the result of
MyClass* myInstance=...;
[myInstance foo];
is not reliable, see the discussion in the official documentation. The documentation says it works if you have only one category, but the documentation says at the same time you shouldn't use that feature. So, don't do this.
The sole exception is +load. If a category defines this method, the runtime calls it for each category you define. So, if you want to perform some initialization task per category, +load is the way. Read the official documentation and this blog post by Mike Ash.