I'm just learning Cocoa (coming from C#) and I'm getting a strange error for something that seems really simple. (charsSinceLastUpdate >= 36)
#import "CSMainController.h"
#implementation CSMainController
//global vars
int *charsSinceLastUpdate = 0;
NSString *myString = #"Hello world";
//
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
...
}
//other functions
- (void)textDidChange:(NSNotification *)aNotification {
NSLog(#"charsSinceLastUpdate=%i",charsSinceLastUpdate);
if (charsSinceLastUpdate>=36) { // <- THIS line returns the error: Comparison between pointer and integer
charsSinceLastUpdate=0;
[statusText setStringValue:#"Will save now!"];
} else {
charsSinceLastUpdate++;
[statusText setStringValue:#"Not saving"];
}
}
//my functions
- (void)showNetworkErrorAlert:(BOOL)showContinueWithoutSavingOption {
...
}
//
#end
Any help would be appreciated, thanks!
In your code, charsSinceLastUpdate is a pointer, you need to define it without the *:
int charsSinceLastUpdate = 0;
Unless, of course, you meant to define it as a pointer, in which case you'd need to use the dereference operator to retrieve the value that it's pointing to, like so:
if(*charsSinceLastUpdate >= 36) {
//...
}
Related
I'm working on a react-native project that requires some native modules. One of them is a Bluetooth module that allows me to access some CSRGaia methods. Ultimately, I want to be able to read the eq values on the PS-key so that I can set my equalizer to the corresponding values. I know almost nothing about Objective-C
Currently there is a method that looks like this:
RCT_EXPORT_METHOD(setEQValues:(NSArray *)values callback:(RCTResponseSenderBlock)callback)
{
CSRPeripheral *connectedPeripheral = [CSRConnectionManager sharedInstance].connectedPeripheral;
if( connectedPeripheral == nil )
{
callback(#[DISCONNECTED]);
return;
}
[[CSRGaia sharedInstance] setEQValues:values];
}
This works with no issues. However, when I tried to write my own
RCT_EXPORT_METHOD(getUserEQ: (NSArray *)values callback:(RCTResponseSenderBlock)callback)
{
CSRPeripheral *connectedPeripheral = [CSRConnectionManager sharedInstance].connectedPeripheral;
if( connectedPeripheral == nil)
{
callback(#[DISCONNECTED]);
return;
}
[[CSRGaia sharedInstance] getUserEQ: values];
}
I get the following error:
No visible #interface for 'CSRGaia' declares the selector 'getUserEQ:'
I double checked the CSRGaia.m file to verify that both methods exist.
- (void)setEQValues:(NSArray *)values {
NSMutableData *payload = [[NSMutableData alloc] init];
for( NSNumber *value in values ) {
uint8_t hex = [value unsignedCharValue];
[payload appendBytes:&hex length:1];
}
[self sendCommand:GaiaCommand_SET_HEP_EQ_PSKEY
vendor:CSR_GAIA_VENDOR_ID
data:payload];
}
- (void)getUserEQ {
[self sendCommand:GaiaCommand_GetUserEQControl
vendor:CSR_GAIA_VENDOR_ID
data:nil];
}
you are calling this method:
'getUserEQ:'
notice the 2 dots colon
it's different from method
'getUser'
with no colon
and in your .m file there is only
- (void)getUserEQ {}
i guess you wanted to use the setter method, instead
- (void)setEQValues:(NSArray *)values{}
like this:
[[CSRGaia sharedInstance] setEQValues: values];
add anyway both
- (void)getUserEQ;
- (void)setEQValues:(NSArray *)values;
in CSRGaia.h file
between
#interface OSRGaia
and
#end
I'm developing an app in Objective-C/OSX (first try). I had a struct (NSDevice custom type) which need to be accessed as a public/extern variable to allow the different interface of the class to access it.
Here is the declaration in the header.
struct NSDevice{
LIBMTP_raw_device_t * usbrawdevice;
int numusbrawdevice;
uint32_t rawdeviceID;
LIBMTP_mtpdevice_t *device;
};
extern struct NSDevice *Device;
#interface DeviceManager : NSObject
- (void) openDevice;
- (void) closeDevice;
#end
and how I implement it in the source file
#import "DeviceManager.h"
struct NSDevice *Device = NULL;
#implementation DeviceManager
- (id)init{
self = [super init];
if(self){
NSLog(#"Init");
LIBMTP_Init();
Device->device = NULL;
Device->numusbrawdevice = 0;
Device->rawdeviceID = 0;
Device->usbrawdevice = NULL;
}
return self;
}
- (NSMtp_error) openDevice {
LIBMTP_error_number_t error = LIBMTP_ERROR_GENERAL;
NSLog(#"Opening Device");
error = LIBMTP_Detect_Raw_Devices(&Device->usbrawdevice, &Device->numusbrawdevice);
When trying to init the struct in the init interface, I got an EXC_BAD_ACCESS. Any idea ?
Can I use the "struct" or is there a better way in Objective-C ?
Thx
Seb, you need to allocate the space for Device.
Try doing this:
- (id)init{
self = [super init];
if(self){
NSLog(#"Init");
LIBMTP_Init();
Device = malloc(sizeof(NSDevice));
Device->device = NULL;
Device->numusbrawdevice = 0;
Device->rawdeviceID = 0;
Device->usbrawdevice = NULL;
}
return self;
}
and declare your structure like this:
typedef struct {
LIBMTP_raw_device_t * usbrawdevice;
int numusbrawdevice;
uint32_t rawdeviceID;
LIBMTP_mtpdevice_t *device;
} NSDevice;
Also, I highly recommend changing the name of NSDevice to SebDevice or something that doesn't start with NS as those prefix characters usually signify something built into the MacOS SDK and it's going to confuse anyone else who has to look at your code after you depart the project.
And one last thing, global variables like this shouldn't start with capital letters. In Objective-C, best practice is for variables to start with lower case letters or maybe a g (for global) or an underscore. Change Device to gDevice.
I wonder is there any drawbacks when use alloc/free with pure C array inside Objective-C class?
For example:
#import "CVPatternGrid.h"
#implementation CVPatternGrid
#synthesize row = _row;
#synthesize column = _column;
#synthesize count = _count;
#synthesize score = _score;
- (id)initWithRow:(NSInteger)row column:(NSInteger)column {
if (self = [super init]) {
_grid = [self allocateNewGrid:row column:column];
}
return self;
}
- (NSInteger)moveCount {
return _count;
}
- (bool**)allocateNewGrid:(NSInteger)row column:(NSInteger)column {
bool **p = malloc(row * sizeof(bool*));
for (int i = 0; i < row; ++i) {
p[i] = malloc(column * sizeof(bool));
}
return p;
}
- (void)generateNewGrid:(NSInteger)row column:(NSInteger)column {
[self freeGrid];
_grid = [self allocateNewGrid:row column:column];
_count = [self.algorithmDelegate generateGrid:_grid];
_score = _count * 100;
}
- (BOOL)isMarkedAtRow:(NSInteger)row column:(NSInteger)column {
return YES;
}
- (void)freeGrid {
for (int i = 0; i < _row; ++i) {
free(_grid[i]);
}
free(_grid);
}
- (void)dealloc {
[self freeGrid];
}
#end
It's perfectly normal to use a C array in an Obj-C class. There are no low level data types in Obj-C — every class, including NSArray, NSString, etc, is using primitive C types internally.
However you are doing a few things wrong:
Do not use #synthesize unless you need to. In this case you don't need it, so delete those lines of code.
Do not use _foo to access variables unless you need it, again in this case you don't need it in any of your use cases (except, arguably, in your init and dealloc methods. But I would argue it should not even be used there. Other people disagree with me). My rule is to only use _foo when I run into performance issues when using self.foo syntax. There are also edge case issues such as KVO where you might run into problems when using an accessor inside init/dealloc. In the real world I have never run into any of those edge cases in more than 10 years of writing Obj-C — I always use accessors, unless they're too slow.
Some implementation details about how to declare an #property of a C array: Objective-C. Property for C array
So, I have to add a functionality to an old .cpp file.It's huge. So rewriting it in Objective C is not an option. Instead, I have added the necessary functionality using Objective-C (because I need a lot of the NSDate/NSDateFormatter functions). It worked fine. BUT, when calling the getter (on my view controller) I get this error: EXC_BAD_ACCESS.
Here is a fragment of the code:
//.h file -----------------
// C/C++ headers
#import <Foundation/NSDate.h>
#import <Foundation/NSDateFormatter.h>
namespace MySpace {
class Session {
private:
// C++ stuff
NSDate * startTime;
public:
// C++ stuff
NSDate * getStartTime();
Session(NSDate * startTime );
};
}
// .cpp file -----------------
using namespace MySpace;
Session:Session (NSDate * startTime) {
// unrelated code
if (startTime == nil ){
startTime = [NSDate date];
}
setStartTime( startTime);
// unrelated code
}
void Session::setStartTime( NSDate * startTime){
this->startTime = [startTime copy];
}
NSDate * Session::getStartTime() {
return this->startTime; // here I get the EXC_BAD_ACCESS
}
The entire project is compiled as Objective-C++ and ARC enabled. I believe this issue is caused because the member 'startTime' is released by ARC, and, when I call the getter, it points to nil?
How may I solve this problem?
Thanks.
Try that:
NSDate * Session::getStartTime() {
if (this == NULL) return nil;
return this->startTime; // here I get the EXC_BAD_ACCESS
}
The change makes getStartTime immune to a NULL this pointer.
Does that helps? If so, somewhere, you are using a dangling Session* pointer.
Step 2
Not that. That then:
#interface MyNSDate: NSDate
#end
#implementation MyNSDate
- (id) init
{
self = [super init];
if ( self == nil ) return nil;
NSLog( #"MyNSDate %# initialized", self );
return self;
}
- (void) dealloc
{
// ARC: no super call
NSLog( #"MyNSDate %# deallocated", self );
}
#end
And replace the NSDate* in your class with MyNSDate. Check the messages, breakpoint in dealloc... You should be able to find out when the date is deallocated, appropriate or not, or rules out that hypothesis.
The other idea that crossed my mind is the missing copy construcoperators. If you are copying your Session between ARC and non-ARC compilation units, it may break. You shouldn't do that, but hey, it happens.
Session::Session( const Session& rhs )
{
this->startTime = [rhs.startTime copy];
}
Session& Session::operator=( const Session& rhs )
{
if ( this->startTime != rhs.startTime )
{
this->startTime = [rhs.startTime copy];
}
return *this;
}
I'm currently learning Objective C and in the process I've made the silly little program below. The program compiles fine - however I get the warning "multiple methods named '-setName:' found".
I've only interfaced and implemented the method once.
What does this warning mean, and how do I correct it?
#import <Foundation/Foundation.h>
// these are the three yoga-exercises we can perform
typedef enum {
kCobra,
kUniversal,
kDog
} ExerciseName;
// translating our variables into human
NSString *nameExercise (ExerciseName nameExercise)
{
switch (nameExercise) {
case kCobra:
return #"Cobra Pose";
break;
case kUniversal:
return #"Universal Stretch";
break;
case kDog:
return #"Dog Pose";
break;
}
return #"no clue!";
} // nameExercise
#interface Exercise : NSObject
{
ExerciseName name;
}
-(void) setName: (ExerciseName) name;
-(void) exerciseDo;
#end
#implementation Exercise
-(void) setName: (ExerciseName) n {
name = n;
} // setName
-(void) exerciseDo {
NSLog(#"Exercise: %#",
nameExercise(name));
}
#end
void executeExercises(id exercises[], int count) {
int i;
for(i=0; i<count; i++) {
id exercise = exercises[i];
[exercise exerciseDo];
}
}
int main (int argc, const char * argv[]) {
id exercises[1];
exercises[0] = [Exercise new]; // initiating an object of class Exercise
[exercises[0] setName:kDog];
executeExercises(exercises, 1);
return 0;
} //main
the meaning of the message is that there are multiple selectors with the name setName: in the translation (that is, it is declared in at least on other place among all included headers). the compiler may choose the wrong selector (which can introduce undefined behavior).
you can typically correct the problem using one (or more) of the following approaches:
1) rename the method to a unique name: e.g. setExerciseName may be ok, if not used in other translations.
2) match the signature of the other selector. e.g. setName:(NSString *)name
3) use type safety:
Exercise * ex = [Exercise new];
[ex setName:kCobra];
4) cast the variable to the type: [(Exercise*)exercise setName:kCobra];
5) restore the type with a new variable: Exercise * ex = exercise;
since you have declared the var as an id, you have erased the type, and it means that the object may respond any visible selector. in general, you should not erase the type in this manner, except when truly necessary.
the best approach i see is a combination of 1 and 3:
[ex setExerciseName:kCobra];