Assign Enum to Variable in Objective-C - ios

How can I assign an enum to a variable and access its value later? I thought this would be pretty simple, but every time I try to assign the enum value to a variable (no type mismatches or warnings in Xcode appear) my app crashes with an EXC_BAD_ACCESS error.
Here's how I setup my enum in my header file (BarTypes.h):
typedef enum {
BarStyleGlossy,
BarStyleMatte,
BarStyleFlat
} BarDisplayStyle;
No issues there (reading and using the values at least). However, when I create a variable that can store one of the enum values (BarStyleGlossy, BarStyleMatte, or BarStyleFlat) then try to set that variable, the app crashes. Here's how I setup and use the variable:
//Header
#property (nonatomic, assign, readwrite) BarDisplayStyle barViewDisplayStyle; //I've also tried just using (nonatomic) and I've also tried (nonatomic, assign)
//Implementation
#synthesize barViewDisplayStyle;
- (void)setupBarStyle:(BarDisplayStyle)displayStyle {
//This is where it crashes:
self.barViewDisplayStyle = displayStyle;
}
Why is it crashing here? How do I store the value of an enum in a variable? I think the issue has to do with a lack of understanding about enums on my end, however if I follow conventional variable setup and allocation, etc. this should work. Any ideas on what I'm doing wrong?
Please note that I'm new to enums, so my vocabulary here may be a bit mixed up (forgive me - and feel free to make an edit if you know what I'm trying to say).
I found a few references about enums across the web:
What is a typedef enum in Objective-C?
Using enum types as properties in Objective C
How to create global enum
How do I define and use an ENUM in Objective-C?
I also tried searching Apple's Developer site but only came up with results about types for Apple APIs (ex. Foundation, UIKit, etc.)
EDIT: Here's how I call the setupBarStyle method:
BarView *bar = [[BarView alloc] init];
[bar setupBarStyle:displayStyle];

Just in case anyone out there is still trying to figure out how to assign an enum value to an enum typed variable or property...
Here is an example using a property.
In the header file...
#interface elmTaskMeasurement : NSObject
typedef NS_ENUM(NSInteger, MeasurementType) {
D,
N,
T,
Y,
M
};
#property(nonatomic) MeasurementType MeasureType;
#end
In the file where the object is created...
elmTaskMeasurement *taskMeasurement = [[elmTaskMeasurement alloc] init];
taskMeasurement.MeasureType = (MeasurementType)N;

The method you implement is called setupBarStyle:, but you call setupBarShape: on the object.

I had this error myself but the error was caused by a different bug I off course create myself.
The setter of my property "myApplicationState" was as follows:
-(void)setApplicationStyle:(myApplicationStyle)applicationStyle{
self.applicationStyle = applicationStyle;
//some more code
}
Off course this would result in an endless loop because in the setter, the setting is called again, and again, and again.
It had to be:
-(void)setApplicationStyle:(myApplicationStyle)applicationStyle{
_applicationStyle = applicationStyle;
//some more code
}

Related

Understanding why assign is breaking code in swift

I made an xcframework (in objective c) which is working fine in objective C but throws an error when using swift.
On debugging, I realized that it was breaking in swift because I was using assign.
I went through his answer: https://stackoverflow.com/a/4511004/10433835
where they say this
In most cases you'll want to use weak so you're not trying to access a deallocated object
I didn't quite get what assign does, but I don't think I am trying to access a deallocated object.
This is what I am doing
I have config with these properties
#import <Foundation/Foundation.h>
#interface Config : NSObject
#property(nonatomic, assign, readwrite) NSString *name;
#property(nonatomic, assign, readwrite) NSString *id;
#property(nonatomic, assign, readwrite) NSString *api;
#end
This is my Config.m file
#implementation Config
- (id)init {
if (self = [super init]) {
_api = #"https://api.xyz.in"
}
return self;
}
- (NSMutableDictionary *_Nonnull)configProperties {
if (!_name) {
[NSException raise:#"name" format:#"Room name cannot be null, please set room name"];
}
NSLog(#"Room name: %#", _name);
At this line it will throw Thread 1: EXC_BAD_ACCESS
NSLog(#" name: %#", _name);
if I remove assign, it won't throw any error
How am I calling it?
let config:Config = Config();
config.name = "varun_bindal";
let props = config.configProperties()
Can someone please explain me why using assign in swift is crashing my code? and why not using it doesn't.
You actually don't want a weak or assign set for that property. The object will be deallocated as soon as it is set. You want a strong reference because the object owns the property. weak will nil the pointer as soon as it's set. So checking the pointer will return nil. assign will keep the address of the pointer without keeping the actual memory set. Hence you're pointing to something that has been deallocated. ie: pointing to bad memory location. It's kind of a remnant of the old days really..
You can read here for more info on keywords:
https://exceptionshub.com/property-and-retain-assign-copy-nonatomic-in-objective-c.html
The answer is: It's complicated.
Swift takes care of object ownership for you. Your only responsibility is to create strong or weak variables to avoid retain cycles. When you stay in Swift, it takes care of everything else.
Life is more complicated in Objective-C, and more complicated still when you need to have Objective-C and Swift code interact.
In order for Swift and Objective-C to work correctly together, you have to declare the memory semantics of your Objective-C classes correctly.
When you declare an Objective-C property as assign, you're telling the compiler not to do any memory management on it. Your Swift code won't set up strong references, and won't be notified if the object has been deallocated. If the Objective-C code doesn't need it any more and releases it, and your Swift code tries to reference it, you will crash.
(As mentioned in comments, Objective-C's assign is equivalent to unowned in Swift.)
As Larme says, assign is ok for non-object scalar types, but not for objects.

How to create a new object and assign properties in Objective-C?

I'm trying to bridge an Objective C SDK with React Native and I'm having some trouble. I have a Subclass of NSObject and I'm trying to set some property values but I can't get it to work.
I have tried to change the property in the header, and in the imp file with out any difference.
PrinterSDK.h (which has libPrinterSDK.a)
#interface Printer : NSObject
#property (nonatomic, readonly) NSString* name;
#property (nonatomic, readonly) NSString* UUIDString;
#end
RNPosPrint.m
#interface Printer ()
#property (readwrite) NSString* name;
#property (readwrite) NSString* UUIDString;
#end
RCT_EXPORT_METHOD(printTestPaper:(NSString*)name:(NSString*)uuid)
{
Printer* printer = [[Printer alloc] init];
printer.name = name;
}
But I keep facing issue with the setter for some reason I can't figure out.
ExceptionsManager.js:94 Exception '-[Printer setPrinterName:]: unrecognized selector sent to instance 0x13fd25b90' was thrown while invoking printTestPaper on target RNPosPrint with params (
"Test Printer",
"XXX-XXX-XXX"
)
You do not report the names of your .h and .m files or what else is in the .m – e.g. #implementation of Printer? The class printTestPaper belongs to? Without details like this it is difficult for anyone to help you, you need to help people help you.
That said some points that may help you:
The #interface Printer () where you open up the properties to be writeable should be in the your Printer.m file – in general do not try to open up access to a type's properties from outside the type's implementation, it is both bad design and may not work as you hope (as you just found out).
The code to support a #property is generated by the compiler when it compiles the #implementation, #interface's themselves produce no executable code – they describe the accessible parts of the #implementation.
setter=<name> provides a different name for the auto-created property setter function. While a method <name> will be created to set the property using dot syntax the properties name is still used, e.g. in your case printer.name = ... is still used even with the setter=setPrinterName:. You can call the auto-created method using standard method syntax, that failed in your case for the reasons above.
Using setter=<name> or getter=<name> are really advanced features and you probably will never need to use them – when you do need to use them you will know! Just avoid them till then.
If you wish to provide a method which creates the object and sets properties then do this in the type's implementation. The usual way of doing this is to provide an init method that does this, e.g. in this case it might be - initWithName:(NSString *)printerName { ... }, or an equivalent class method which does the allocation and sets the parameters, e.g. in this case it might be + newWithName:(NSString *)printerName { ... }.
HTH
Since it's an interface from statically linked library it is simply not possible to extend or manipulate. Not without tempering with the compiler.

Explicitly importing Objective-C properties into Swift as optionals

Update: As of Xcode 6.3 this is now possible via nullability annotations.
I've been wondering if anyone has figured out a way to explicitly import Obj-C properties into Swift as a optional.
The Problem
Consider the following ObjC class:
#interface Foo : NSObject
#property (strong, nonatomic) NSObject* potentiallyNil;
#end
This is great and all, but when you go to use it in Swift, Xcode tends to import the property as either an instance proper or as an implicitly unwrapped optional.
I've seen this come in as both
class Foo : NSObject {
var potentiallyNil : NSObject
}
as well as
class Foo : NSObject {
var potentiallyNil : NSObject!
}
Now here's the problem with that - within Swift, you've now got this ugly problem of knowing that your property may be nil, but having to avoid the beauty of implicit optional conversion. You're left with one of the following options:
An explicit nil check...
if(potentiallyNil == nil) {
bar()
}
Or a manual conversion to an optional type with an unwrap - also ugly...
let manuallyWrappedPotentiallyNil : NSObject? = potentiallyNil
if let unwrapped = manuallyWrappedPotentiallyNil {
bar();
}
Both of these seem far from ideal. It seems to me there MUST be a way around this! In fact, as #matt has pointed out, Apple has done this themselves throughout the betas while "auditing for optional compliance". Can we do the same?
The Question
It boils down to this: Given the ObjC property
#property (strong, nonatomic) NSObject* potentiallyNil;
is there a way to cause this to be imported to Swift with the signature
var potentiallyNil : NSObject?
Many thanks to all you other iOS/OS X devs out there.
At the time of asking this question, this was not possible. However, in Xcode 6.3, there is a new nullability annotation feature to enable this previously private functionality. Question is therefore no longer relevant.
If you inspect the API interface code you'll see that there are both Swift and Objective C versions of classes. So I guess that's Apple's solution to the issue?

Im working on "Matchismo" the app for the stanford itunes university courses, and on the first assignment I get an issue on my PlayingCrad.m file.

#import "PlayingCard.h"
#implementation PlayingCard
-(NSString *)contents
{
NSArray *rankStrings = [PlayingCard rankStrings];
return [rankStrings[self.rank] stringByAppendingString:self.suit];
/Users/Pichard93/Desktop/Matchismo2/Matchismo2/PlayingCard.m:18:33: Property 'rank' not found on object of type 'PlayingCard'
//This is the issue i get ^^
It looks like rank should be declared as a property and is not. Declare it the interface file or as a class extension in the implementation file.
Basically the error messages do mostly make sense, try to figure out what them mean pertaining to your code. In this case the error message rather explicitly stated that rank is not found for the class PlayingCard which means not declared.
Add this line to your PlayingCard.h
#property (nonatomic)NSUInteger rank;

valueForKey: on packed struct?

Given the following packed struct:
typedef struct __attribute__((packed)) {
BOOL flag;
int x;
} Foo;
And the following class:
#interface Obj : NSObject
#property (nonatomic) Foo foo;
#end
#implementation Obj
#end
Trying to call valueForKey: on a property which has type of packed struct:
Obj *obj = [Obj new];
id boo = [obj valueForKey:#"foo"];
causes a crash inside valueForKey: (actually it's crashing not inside valueForKey: but in random places depending on moon magnitude, I guess it's memory corruption).
If I remove __attribute__((packed)) it works fine.
Any possibility to get struct's data without a crash? Is it Apple's bug?
PS. I do need to do it at runtime, i.e. I can't just call .foo directly, I only have #"foo" string at runtime. (What I'm trying to achieve actually is to recursively print object contents).
I don't know if this is possible with properties, but if it is I don't think you're using the right syntax.
Have you tried changing
id boo = [obj valueForKey:#"foo"];
to read
Foo boo = obj.foo;
?
Foo is not nor never will be an id. valueForKey: returns id, and the runtime might be barfing trying to squeeze struct Foo into an NSValue.
If you need to use valueForKey: for some reason, your accesses need to look like.
Foo myFoo = FooFactory();
Object *myObj = [Object new];
[myObj setValue:#( myFoo ) forKey:#"foo"];
Foo myFooOut;
[[myObj valueForKey:#"foo"] getValue:&myFooOut];
//I bet `getValue:` is where things are barfing.
In this case, if NSValue's machinery indeed can't handle the packed struct, you just have to write the accessors the old fashioned way: -<key> and -set:`.
PS: Never name a class "Object", there actually is an Object in some SDKs that NSObject inherits from. I assume that's just in your example.
Avoid KVO for your usage case, and stick to the handy dandy <objc/runtime.h> header. You can re-implement a basic part of KVO using this kind of introspection. Struct packing is used primarily to make sure the compiler doesn't align internal fields properly so the CPU does not need to handle it at the hardware level during execution. In this day and age, it's better to let the compiler take care of things for you.
If you'd really prefer this struct packing (as you said, in a network transmission scenario), I'll need further information to determine where the issue lies. Perhaps attempt to change #property (nonatomic) Foo foo; to #property (nonatomic) NSValue *foo; and then box it and unbox it yourself? This way, the exception/error will be in your application's domain.

Resources