I had to implement a C function in an Objective-C class that normally would get called with int main (in it's own file). Since I'm unfamiliar with splicing code I need to know how to have it called upon in either the awakefromnib or applicationDidFinishLaunching from within the Cocoa section. Or if there's a much better way to do this I'm listening.
Here's an example without getting too technical:
// Cocoa Imports
#import "AppDelegate.h"
...
// C Inlcudes
#include <stdio.h>
// (int main had to change to something else obviously)
int dos () {
printf ("I'm a C program\n");
}
// (back to cocoa)
#implementation AppDelegate
#synthesize window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
//[This is where I need my C function above to execute]
It's called just the same as you would from C and you can call any C function you like:
// Cocoa Imports
#import "AppDelegate.h"
...
// C Inlcudes
#include <stdio.h>
// (int main had to change to something else obviously)
int dos (const char *filename)
{
printf ("I was passed '%s'\n", filename);
}
// (back to cocoa)
#implementation AppDelegate
#synthesize window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
dos("/path/to/some/file");
}
Related
I am learning Objective-C inheritance and my program is getting lost in a recursive loop and won't come out. It gets hung up when calling a getter function.
I am using XCode version: Version 6.2 (6C101)
My program is given below
Vehicle.h
#ifndef exercise_2_Vehicle_h
#define exercise_2_Vehicle_h
#import <Foundation/Foundation.h>
#interface Vehicle : NSObject
#property float speed;
-(void) start;
-(void) stop;
-(void) park;
#end
#endif
Vehicle.m
#import "Vehicle.h"
#implementation Vehicle
-(void) setSpeed:(float)speed {
self.speed = speed;
}
-(float) speed {
return self.speed;
}
-(void) start {
NSLog(#"Starting the vehicle");
}
-(void) stop {
NSLog(#"Stopping the vehicle");
}
-(void) park {
NSLog(#"Parking the vehicle");
}
#end
Car.h
#ifndef exercise_2_Car_h
#define exercise_2_Car_h
#import "Vehicle.h"
#interface Car : Vehicle
#property (nonatomic) NSString* make;
-(Car*) initMake: (NSString*) make;
-(NSString*) make;
#end
#endif
Car.m
#import "Car.h"
#implementation Car
-(Car*) initMake:(NSString *)make {
self = [super init];
if (self) {
self.make = make;
}
return self;
}
-(NSString*) make {
return self.make;
}
#end
main.m
#import <Foundation/Foundation.h>
#import "Car.h"
#import "Vehicle.h"
int main(int argc, const char * argv[]) {
#autoreleasepool {
// insert code here...
Car* car = [[[Car alloc] init] initMake: #"Camry"];
//[car setSpeed:45];
NSLog(#"The model initialized is ");
[car make];
// [car speed];
}
return 0;
}
The issue you have is caused by creating the property for speed:
#property float speed;
and overriding setSpeed: method.
When you create #property compiler adds two methods for you, in your example setSpeed and speed.
This command:
self.speed = speed;
is equal to:
[self setSpeed: speed];
and inside setSpeed you have this command again which cause the loop. In your example you can remove both methods (setSpeed and speed) because compiler will add it for you. If you need it because you want to do some customisation you should use _speed instead self.speed.
_speed is backed variable added by compiler when using #property.
Change your method to:
-(void) setSpeed:(float)speed {
_speed = speed;
}
to remove the infinite loop.
In the
- (NSString*)make;
use
return _make
instead. The same with the speed.
If you return "self.x" in a getter method, then it's going to try and call the method again because you're requesting it on self. XCode will automatically convert the properties into variables that can be accessed with an '_' character, so you don't need to do any extra work.
You could also ignore our advice and remove both the "speed" and "make" getter methods you have made, because XCode automagically creates them for you.
I want to be able to have two classes that are responsible to respond to selectors differently depending if the platform is iOS or OSX.
However, I want to have code that uses only one class, and I want to avoid repeating #ifdefs.
Ideally I would want to have 3 classes:
UniversalClass
iOSSpecificClass
OSXSpecificClass
iOSSpecificClass and OSXSpecificClass both extend UniversalClass.
All calls would be done to UniversalClass, and that class is responsible to call the respective methods of iOSSpecificClass and OSXSpecificClass.
There are two solutions that I came up with:
#interface UniversalClass : NSObject
+ (void) universalMethod;
#end
#implementation UniversalClass
+(id)forwardingTargetForSelector:(SEL)aSelector {
#if TARGET_OS_IPHONE
return [iOSSpecificClass class];
#else
return [OSXSpecificClass class];
#endif
}
#end
The problem with this approach is that UniversalClass promises something in the .h that can or cannot deliver. The warnings also tell us that. Grr. Warnings.
The second approach would be like this:
#implementation UniversalClass
+ (Class)correctClass {
Class aClass = Nil;
#if TARGET_OS_IPHONE
aClass = [iOSSpecificClass class];
#else
aClass = [OSXSpecificClass class];
#endif
return aClass;
}
+ (void)universalMethod {
Class masterClass = [UniversalClass correctClass];
[masterClass universalMethod];
}
#end
The problem with this approach is that I have to perform changes for every method I add and I feel that I am kinda repeating myself without needing.
What are the edge cases I have to pay attention to in both solutions? Is there any better solution than those?
One option is to have a common header file and two different implementations for two targets (one for OSX and another for iOS) that both import and implement the header methods.
Something like this:
Another alternative is to examine if you really need two classes. One #interface and two #implementations (potentially in separate files) is a pattern that I've seen.
Something like (this from CodeRunner where I did my test):
#import <Foundation/Foundation.h>
// #define iPHONE 1
#interface MyClass : NSObject
- (NSString*) someString;
- (BOOL) aMethod: (NSString*) inString;
#end
// common implementations here
#interface MyClass (common)
- (NSString*) commonString;
#end
#implementation MyClass (common)
- (NSString*) commonString
{
return #"same";
}
#end
#ifdef iPHONE
// iPhone specific implementations
#implementation MyClass
- (BOOL) aMethod: (NSString*) inString
{
return [inString isEqualToString: #"iPhone Impl"];
}
- (NSString*) someString
{
return #"iPhone Impl";
}
#end
#else
#implementation MyClass
- (BOOL) aMethod: (NSString*) inString
{
return [inString isEqualToString: #"iPhone Impl"];
}
- (NSString*) someString
{
return #"OS X Impl";
}
#end
#endif
// test
int main(int argc, char *argv[]) {
#autoreleasepool {
MyClass * obj = [[MyClass alloc] init];
NSLog(#"is iPhone? %#", [obj aMethod: [obj someString]] ? #"YES" : #"NO");
NSLog( #"string: %#", [obj someString] );
}
}
You could obviously do this more elegantly by having two .m files and putting one implementation in each (iPhone in one, OS X in the other); or three if you are going to have common routines that are shared by both.
Anyway, just an alternative way to get the same / similar effect - single interface to differing functionality.
You could go with something like this:
#implementation UniversalClass
static Class class;
+ (void)load
{
class = [UniversalClass correctClass];
}
+ (Class)correctClass {
Class aClass = Nil;
#if TARGET_OS_IPHONE
aClass = [iOSSpecificClass class];
#else
aClass = [OSXSpecificClass class];
#endif
return aClass;
}
+ (void)universalMethod {
[class universalMethod];
}
This will keep the promise you made on the .h by implementing the corresponding method (no warnings) and get the right class only once.
How about just ignoring the warning for the specific case of your forwardingTargetForSelector: version? It's like saying “hey, I know what I'm doing!” :-)
Add something like these #pragma calls around your #implementation line:
...
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wincomplete-implementation"
#implementation UniversalClass
#pragma clang diagnostic pop
...
See this answer here on Stack Overflow.
The solution that you are proposing is the Class Cluster pattern, which is quite common in Cocoa (e.g. it is used in NSArray, NSValue, etc). Class clusters are classes that return a private subclass from their constructor instead of an instance of the class that was requested. Here is how you might implement that in this case:
MyClass.h
#interface MyClass : NSObject
- (void)someMethod;
#end
MyClass.m
#implementation MyClass
+ (id)alloc
{
if (self == [MyClass class])
{
#if TARGET_OS_IPHONE
return [MyClass_iOS alloc];
#else
return [MyClass_Mac alloc];
#endif
}
else
{
return [super alloc];
}
}
- (void)someMethod
{
//abstract, will be overridden
}
#end
MyClass_iOS and MyClass_Mac would be declared in separate files and privately imported in the McClass.m file.
This seems like a pretty elegant solution at first, but it's not really appropriate for this situation. Class clusters are great for swapping class implementation at runtime when you don't know which implementation you want at compile time (good examples would be supporting different iOS versions, or universal apps that behave differently on iPad and iPhone) but for Mac/iOS we know at compile time which code we need, so introducing a cluster of 3 separate classes is redundant.
This solution doesn't really offer any benefit over the ones suggested by https://stackoverflow.com/users/145108/dad or https://stackoverflow.com/users/3365314/miguel-ferreira because we still have to branch the import code:
#if TARGET_OS_IPHONE
#import "MyClass_iOS.h"
#else
#import "MyClass_Mac.h"
#endif
We could solve that by having a single header for both MyClass_iOS and MyClass_Mac (which was Miguel's solution) or by having both implementations in the same file (which was Dad's solution) but then we've just built a layer on top of one of the solutions you already rejected.
Personally, I would just use a single .m file with three clearly delineated sections:
#interface MyClass
#pragma mark -
#pragma mark Common code
- (void)someMethod1
{
}
#pragma mark -
#pragma mark iOS code
#if TARGET_OS_IPHONE
- (void)someMethod2
{
}
#pragma mark -
#pragma mark Mac code
#else
- (void)someMethod2
{
}
#endif
#end
This avoids creating unnecessary classes and gives you freedom to easily have shared methods or separate implementations for each platform without exposing any of that in the interface.
If the classes for the two platforms definitely won't have any code in common, I'd probably opt for Miguel's solution, which is very clean.
I don't accept the "user confusion" explanation. You'd basically have these three files:
MyClass.h
MyClass_iOS.m
MyClass_Mac.m
I think if someone is confused by what that means, they shouldn't be working on your code base ;-)
You could also combine this with the class cluster approach if you did want to inherit shared code between the two platforms, in which case your MyClass.m file would contain both the shared implementation and the private interface:
#interface MyClass_Private : MyClass
- (void)somePlatformSpecificMethod;
#end
#implementation MyClass
+ (id)alloc
{
if (self == [MyClass class])
{
return [MyClass_Private alloc];
}
else
{
return [super alloc];
}
}
- (void)someSharedMethod
{
//concrete implementation
}
#end
And your project structure would look more like this:
MyClass.h
MyClass.m
MyClass_Private_iOS.m
MyClass_Private_Mac.m
Hope that helps!
I am new to iOS and obective-c so I am not too sure how to best accomplish this seemingly simple task.
What I want is to make a class that looks like this in pseudocode:
class UtilityClass
{
// Have a method that I can pass parameters to
String doCalculation ( String art1 , String arg2 )
{
return arg1 + arg2;
}
}
My uncertainty is:
1) xCode seems to be inclined to lay out my file structure in a relatively flat way. So should I make a utils directory and have this file be under utils/fileName ? Usually I am kind of used to having at least some src directory, but so far I have not been prompted by anything to create one.
2) How do I import and call this class/function from my controllers?
Thanks,
Alex
Just create a new group called Utilities, and then create your class inside it. Like,
utils.h
utils.m
Later in your ViewController's header file just add.
#import "utils.h"
if this utils class is used by many controllers in very fat project then, find a file called, Should be inside supporting files group.
YourAppName-Prefix.pch
In that file you have a code block like this,
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#endif
Just edit this block and add your utils.h reference here, In this way your entire project can create utils object without explicitly importing into their own header.
Edit like this.,
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import "utils.h"
#endif
First of all create a new File in Xcode and uncheck xib file. Name the Project as you like . and extend it from NSObject .
for creating static method you have to replace function starting - to + like
interface. class
#interface Utility : NSObject
+ (int)getNumber;
+ (void)setNumber:(int)number;
#end
.m class
#import "Utility.h"
#implementation Utility
static int number = 1;
+ (int)getNumber {
return number;
}
+ (void)setNumber:(int)newNumber {
number = newNumber;
}
+ (id)alloc {
[NSException raise:#"Cannot be instantiated!" format:#"Static class 'ClassName' cannot be instantiated!"];
return nil;
}
#end
call it in any other ViewController like
NSLog(#"number = %d", [Utility getNumber]);
[Utility setNumber:3];
NSLog(#"number = %d", [Utility getNumber]);
for details..
Where you store the files is up to you, just make sure XCode knows where to find them. The class itself should be made like any other Objective C class, just make it inherit from NSObject instead of one of the graphical classes:
// MyClass.h
#interface MyClass : NSObject {
int instanceVar;
}
#property (nonatomic, assign) int property;
#end
// MyClass.m
#import "MyClass.h"
#implementation MyClass
#synthesize property;
-(id) init {
...
}
-(int) function {
...
}
#end
To use the class in another file, just import the header like any other class
#import "MyClass.h"
I'm trying to figure out how to call a c function from an obj-c file. I've seen some good examples of how to do the opposite.
In the example below I have an objective c file. It contains a c function named setup.
I want to be able to create an instance of my obj-c file in the regular way and then call the setup function.
Header
#import <Foundation/Foundation.h>
void setup(int,float);
#interface Test : NSObject {
}
#end
Source
#import "Test.h"
#include <stdio.h>
#include <stdlib.h>
void setup(int val1,float val2)
{
//do something with values
}
#implementation Test
#end
View did load
Test *test =[Test alloc]init]
//this does not work
test.setup(6,1.4);
Just call setup(). As declared, is in no way tied to an object - it's just a regular C function.
I am just learning how to use ScriptingBridges. I made a method that slowly fades the volume on iTunes, and would like to make it a category so I can do the following:
iTunesApplication* iTunes = [SBApplication applicationWithBundleIdentifier:#"com.apple.iTunes"];
[iTunes lowerVolume:50 speed:1];
I made another category for NSSpeechSynthesizer that works, but I can't get this one to. I keep getting the following build error:
"_OBJC_CLASS_$_iTunesApplication", referenced from:
l_OBJC_$_CATEGORY_iTunesApplication_$_iTunesApplicationAdditions in iTunesApplication.o
objc-class-ref-to-iTunesApplication in iTunesApplication.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
Is there something special I can do to make it work since I can't include the symbols?
Thanks,
Ryan Pendleton
UPDATE:
I only found one solution, which is below. It involves MethodSwizzling, so I'm open to better answers, but for now it's all I have.
The solution I found was to use the Objective-C runtime API. I'm sure there's a better way to organize this, but here's how I did it:
Here are my .h and .m files for creating the category. Notice how lowerVolume is not an actual method, but a C function with the arguments id self, and SEL _CMD. You'll also notice a setupCategories function. We'll call that later.
// iTunes+Volume.h
#import <objc/runtime.h>
#import "iTunes.h"
void lowerVolume(id self, SEL _cmd, int dest, float speed);
void setupCategories();
#interface iTunesApplication (Volume)
- (void)lowerVolume:(int)dest speed:(float)speed;
#end
// iTunes+Volume.m
#import "iTunes+Volume.h"
void lowerVolume(id self, SEL _cmd, int dest, float speed)
{
NSLog(#"Lower Volume: %i, %f", dest, speed);
}
void setupCategories()
{
id object = [[SBApplication alloc] initWithBundleIdentifier:#"com.apple.iTunes"];
Class class = [object class];
[object release];
class_addMethod(class, #selector(lowerVolume:speed:), (IMP)lowerVolume, "#:if");
}
Now that I've made the functions, I need to actually add them to the scripting bridge class using the Objective-C runtime API. I'll do this in main.m to make sure that the methods are ready to be used when the run loop starts.
// main.m
#import <Cocoa/Cocoa.h>
#import "iTunes+Volume.h"
int main(int argc, char *argv[])
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
setupCategories();
return NSApplicationMain(argc, (const char **) argv);
[pool drain];
}
Now, I can use my method wherever I want as long as I include the header files:
- (void)mute
{
iTunesApplication* iTunes = [[SBApplication alloc] initWithBundleIdentifier:#"com.apple.iTunes"];
[iTunes lowerVolume:0 speed:1];
[iTunes release];
}
If any of this doesn't make sense, just tell me and I'll try to explain it better.
I think you need to include -framework ScriptingBridge to your gcc arguments. That got it to compile for me!
As noted above, you can't easily do a category on iTunesApplication because it doesn't exist at compile time, and also because the runtime class name is ITunesApplication (capital "I").
The best solution I've found is to do your category on the class that DOES exist, SBApplication. Here's the code I tested that works and does what the original example was trying to do:
// SBApplication+Extensions.h
#import ScriptingBridge;
#interface SBApplication (Extensions)
- (void)lowerVolume:(int)dest speed:(float)speed;
#end
// SBApplication+Extensions.m
#import "iTunes.h"
#import "SBApplication+Extensions.h"
#implementation SBApplication (Extensions)
- (void)lowerVolume:(int)dest speed:(float)speed
{
NSLog(#"Lower Volume: %i, %f", dest, speed);
}
#end
// Caller, say in AppDelegate
#import "SBApplication+Extensions.h"
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
iTunesApplication *iTunesApp =
[SBApplication applicationWithBundleIdentifier:#"com.apple.iTunes"];
[iTunesApp lowerVolume:4 speed:3.3f];
}