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.
Related
Assume the following class hierarchy. Class A is publicly declared:
#interface A : NSObject
+ (A)createInstance;
- (void)a;
#end
Class _B is a private subclass of A:
#interface _B : A
- (void)a;
- (void)b;
#end
Assume objects of class A should only be created using the factory method createInstance, which creates and returns an instance of _B.
I want to enhance the functionality of an instance of A on a per-instance basis. So I decided to do some ISA swizzling to achieve:
#interface ExtA : A
- (void)a;
#end
#implementation ExtA
- (void)a
{
NSLog("ExtA_a");
[super a];
}
#end
And I do the ISA swizzling using the following method on an NSObject category (naive implementation shown here):
- (void)changeToSubclass:(Class)cls prefix:(NSString*)prefix suffix:(NSString*)suffix
{
NSString* className = [NSString stringWithFormat:#"%#%#%#", prefix ? prefix : #"", NSStringFromClass(object_getClass(self)), suffix ? suffix : #""];
if([className isEqualToString:NSStringFromClass(object_getClass(self))])
{
className = [NSString stringWithFormat:#"%#(%#)", NSStringFromClass(object_getClass(self)), NSStringFromClass(cls)];
}
Class newSubclass = objc_getClass(className.UTF8String);
if(newSubclass == nil)
{
newSubclass = objc_allocateClassPair(object_getClass(self), className.UTF8String, 0);
objc_registerClassPair(newSubclass);
unsigned int listCount = 0;
Method *list = class_copyMethodList(cls, &listCount);
for(int i = 0; i < listCount; i++)
{
class_addMethod(newSubclass, method_getName(list[i]), method_getImplementation(list[i]), method_getTypeEncoding(list[i]));
}
free(list);
listCount = 0;
list = class_copyMethodList(objc_getMetaClass(class_getName(cls)), &listCount);
for(int i = 0; i < listCount; i++)
{
class_addMethod(objc_getMetaClass(class_getName(newSubclass)), method_getName(list[i]), method_getImplementation(list[i]), method_getTypeEncoding(list[i]));
}
free(list);
}
object_setClass(self, newSubclass);
}
Everything seemingly works, but I noticed that [super a]; does not behave as expected, actually the implementation of -[A a] is called, if if the superclass in runtime is actually _B.
Replacing the call to super with the following code works, but is ugly, and requires knowledge of and work by developers:
struct objc_super superInfo = {
self,
[self superclass]
};
objc_msgSendSuper(&superInfo, #selector(a));
What does the compiler emit when calling super and any way to change this emitted code?
The difference is minor, but important. The compiler is issuing a function call, not to objc_msgSendSuper, but to objc_msgSendSuper2.
What's the difference, you may ask? It's minor, but important.
From apple's open source:
/********************************************************************
*
* id objc_msgSendSuper(struct objc_super *super, SEL _cmd,...);
*
* struct objc_super {
* id receiver;
* Class class;
* };
********************************************************************/
ENTRY _objc_msgSendSuper
MESSENGER_START
// search the cache (objc_super in %a1)
movq class(%a1), %r11 // class = objc_super->class
CacheLookup SUPER // calls IMP on success
/* Snipped code for brevity */
/********************************************************************
* id objc_msgSendSuper2
********************************************************************/
ENTRY _objc_msgSendSuper2
MESSENGER_START
// objc_super->class is superclass of class to search
// search the cache (objc_super in %a1)
movq class(%a1), %r11 // cls = objc_super->class
movq 8(%r11), %r11 // cls = class->superclass
CacheLookup SUPER2 // calls IMP on success
For those reading who are unfamiliar with x86_64 assembly, the important line of code is here:
movq 8(%r11), %r11 // cls = class->superclass
What does this do, you may ask? It's fairly simple - instead of the caller passing the superclass to search, the objc_msgSend implementation does it.
However, this important distinction causes one crucial problem - when doing a super call, it does not invoke [self class]. Instead, it uses the class of the current implementation, which is, of course, ExtA.
Therefore, the only way to 'fix' this is to change the superclass of ExtA at run-time, which should cause your method invoking to perform as expected.
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
I have searched and can't find the assert to this. I know it must be a fundamental thing I'm missing here.
I have an enum:
typedef enum {
NINETYBEND, NINETYBEND_FAST, NINETYBEND_SLOW, STRAIGHT
} BlockTypeEnum;
I am trying to use these values in objects I am creating like this:
BlockTypeEnum blockType = STRAIGHT;
XMLLevelPiece* piece = [[XMLLevelPiece alloc] init];
[piece initPiece:blockType];
My problem occurs when I try to use the same variable twice. If I create one object with an enum, change the enum and then create a second object using it, the enum in my first object changes to the second enum value. This is not what I want. Example below:
BlockTypeEnum blockType = STRAIGHT;
XMLLevelPiece* piece = [[XMLLevelPiece alloc] init];
[piece initPiece:blockType];
blockType = NINETYBEND_FAST;
XMLLevelPiece* piece2 = [[XMLLevelPiece alloc] init];
[piece2 initPiece:blockType];
NSLog([NSString stringWithFormat:#"%d", [piece getBlockType]]);
NSLog([NSString stringWithFormat:#"%d", [piece2 getBlockType]]);
//BOTH BLOCK TYPES ARE NOW NINETYBEND_FAST, NOT WHAT I WANTED!!
As far as I understood, an enum is just a glorified int, not a pointer, and I am reassigning the variable after adding to the first object. Please can someone tell me what I'm missing! Thanks very much, Simon.
Here is my code for XMLPiece, thanks!
#import "XMLLevelPiece.h"
#import "BlockType.h"
#import "GridCord.h"
#import "BlockColor.h"
#implementation XMLLevelPiece
BlockTypeEnum mBlockType;
BlockColorEnum mBlockColor;
int mRotation;
GridCord* mGridCords;
BlockColorEnum mLeftColor;
BlockColorEnum mTopColor;
BlockColorEnum mRightColor;
Boolean mRotatable;
Boolean mMoveable;
int mGroupID;
-(id) init
{
if( (self=[super init])) {
}
return self;
}
-(void)initPiece:(BlockTypeEnum)pBlockType pBlockColor:(BlockColorEnum)pBlockColor pRotation:(int)pRotation pGridCords:(GridCord*)pGridCords pLeftColor:(BlockColorEnum) pLeftColor pTopColor:(BlockColorEnum) pTopColor pRightColor:(BlockColorEnum) pRightColor pRotatable:(Boolean) pRotatable pMoveable:(Boolean) pMoveable pGroupID:(int) pGroupID
{
mBlockType = pBlockType;
mBlockColor = pBlockColor;
mRotation = pRotation;
mGridCords = pGridCords;
mLeftColor = pLeftColor;
mTopColor = pTopColor;
mRightColor = pRightColor;
mRotatable = pRotatable;
mMoveable = pMoveable;
mGroupID = pGroupID;
}
-(void)initPiece2
{
NSLog(#"poo");
}
-(Boolean)getRotatable
{
return mRotatable;
}
-(Boolean)getMoveable
{
return mMoveable;
}
-(int) getGroupID
{
return mGroupID;
}
-(BlockColorEnum) getLeftColor
{
return mLeftColor;
}
-(BlockColorEnum) getTopColor
{
return mTopColor;
}
-(BlockColorEnum) getRightColor
{
return mRightColor;
}
-(BlockTypeEnum) getBlockType
{
return mBlockType;
}
-(BlockColorEnum) getBlockColor
{
return mBlockColor;
}
-(int) getRotation
{
return mRotation;
}
-(id) getGridCords
{
return mGridCords;
}
-(void) setRotatable:(Boolean) pRotatable
{
mRotatable = pRotatable;
}
-(void) setMoveable:(Boolean) pMoveable
{
mMoveable = pMoveable;
}
#end
TL;DR
The answer is - that is not how you define ivars in Objective-C. I'm not even sure how that is supposed to behave but I can reproduce the error if I code it the same as you have.
I'd be interested for someone with more knowledge to explain what the behaviour/scope of those variable should be when they are defined like you have.
There's a lot of flaws in that code
You have not actually shown an initPiece:.
The long init... with all the arguments is likely a bad idea. Generally only add things to an init as either a convenience or if the object simply cannot function without it.
The use of get is not really correct in Objective-C
The class should potentially be defined more like
XMLLevelPiece.h
// You will need to import the header with the BlockTypeEnum defined
#interface XMLLevelPiece : NSObject
#property (nonatomic, assign) BlockTypeEnum blockType;
// .. Other properties
- (id)initWithPiece:(BlockTypeEnum)blockType; // I'm not so sure you need this
#end
XMLLevelPiece.m
#import "XMLLevelPiece.h"
#import "BlockType.h"
#import "GridCord.h"
#import "BlockColor.h"
#implementation XMLLevelPiece
#synthesize blockType = mBlockType;
- (id)initWithPiece:(BlockTypeEnum)blockType;
{
self = [super init];
if (self) {
mBlockType = blockType;
}
return self;
}
#end
Then you can use it like
BlockTypeEnum blockType = STRAIGHT;
XMLLevelPiece *p1 = [[XMLLevelPiece alloc] initWithPiece:blockType];
blockType = NINETYBEND_FAST;
XMLLevelPiece *p2 = [[XMLLevelPiece alloc] initWithPiece:blockType];
NSLog(#"%d", p1.blockType);
NSLog(#"%d", p2.blockType);
Which for me results in:
2012-01-08 15:29:31.782 Untitled[1297:707] 3
2012-01-08 15:29:31.791 Untitled[1297:707] 1
Optional observations
If you can do away with the dedicated initializer, the usage would look more like:
BlockTypeEnum blockType = STRAIGHT;
XMLLevelPiece *p1 = [[XMLLevelPiece alloc] init];
p1.blockType = blockType;
// all other assignments
blockType = NINETYBEND_FAST;
XMLLevelPiece *p2 = [[XMLLevelPiece alloc] init];
p2.blockType = blockType;
// all other assignments
NSLog(#"%d", p1.blockType);
NSLog(#"%d", p2.blockType);
To remove a couple of superflous lines you could remove the local blockType variable and assign the value straight to the object:
XMLLevelPiece *p1 = [[XMLLevelPiece alloc] init];
p1.blockType = STRAIGHT;
Your method call to initWithPiece does not match the definition.
Call:
[piece initPiece:blockType];
Definition:
-(void)initPiece:(BlockTypeEnum)pBlockType pBlockColor:(BlockColorEnum)pBlockColor pRotation:(int)pRotation pGridCords:(GridCord*)pGridCords pLeftColor:(BlockColorEnum) pLeftColor pTopColor:(BlockColorEnum) pTopColor pRightColor:(BlockColorEnum) pRightColor pRotatable:(Boolean) pRotatable pMoveable:(Boolean) pMoveable pGroupID:(int) pGroupID
Comments:
In general method calls with more than a few parameters are best a poor idea. Probably better in this case is using individual setters.
The convention in Objective-C as per Apple is to name getters without the "get" prefix. In fact a get prefix signifies a value returned via a reference parameter, not as the return result. Such usage will confuse the Analyzer and cause problems if using ARC.
#implementation MonthView {
DayView *dayViews[6][7];
}
Xcode does not complain about this code, but AppCode gives a warning:
Pointer to non-const type 'DayView * * const * ' with no explicit lifetime
My intention was to create a 6x7 block of DayView pointers that will be part of the memory layout of any MonthView instance.
Is this code doing what I want, and how can I fix this warning?
What you're attempting to do is valid, but if the comments above are correct and this is due to a bug in AppCode and the warning you receive throws a wrench into the works (such as when using -Werror) or it just bothers you to receive it, you can get around it by just allocating the array inside -init.
Fair warning: This code is off the top of my head and I don't guarantee it to work as written.
#implementation MonthView {
DayView ***dayViews;
}
#interface MonthView
- (id)init {
if ((self = [super init])) {
int i;
// do stuff here
// Create the array
dayViews = malloc(sizeof(id) * 6);
dayViews[0] = malloc(sizeof(DayView *) * 6 * 7);
for (i = 1; i < 6; i++) {
dayViews[i] = dayViews[0] + (i * 7);
}
}
return self;
}
#end
This code should produce a two-dimensional array that you can access as normal, while minimizing the number of calls to malloc needed.
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];