I am looking for the best way to pass a Difficulty level between View Controllers.
At present I have this setup as a String. There are three options Easy/Medium/Hard, I know this is not the best way to do this so am looking for what would be the correct approach here.
At the moment I check the tag on the button and set a string value like below:
if (sender.tag == 10) {
self.turnDifficulty = #"Easy";
} else if (sender.tag == 20) {
self.turnDifficulty = #"Medium";
} else if (sender.tag == 30) {
self.turnDifficulty = #"Hard";
}
I then pass the value over in the prepareForSegue method. What is the alternate to this approach? Although there is no issue here and this works fine, it is not very clean working with strings here.
One alternative to working with strings in Objective-C (indeed, in C and C++ as well) us using an enumeration:
typedef enum Difficulty {
DIFFICULTY_EASY
, DIFFICULTY_MEDIUM
, DIFFICULTY_HARD
} Difficulty;
Declare this enum in a header included from all your view controllers, and use enumeration constants as if they were numeric constants. The language will ensure that the constants remain distinct, even when you choose to add more items to the enumeration.
When you declare a #property or a parameter of type Difficulty, do not use an asterisk, because enums are primitive types, not reference types. For example:
#property (nonatomic, readwrite) Difficulty difficultyLevel;
or
-(void)openWithDifficulty:(Difficulty)level;
EDIT : (thanks, Rob!)
As of Xcode 4.4, you might also use an explicit fixed underlying type, e.g.
typedef enum Difficulty : NSUInteger {
kDifficultyEasy
, kDifficultyMedium
, kDifficultyHard
} Difficulty;
If your problem is just working with string you should declare an enumeration.
Write in a Enum.h that will be imported by all your controllers :
typedef enum
{
EASY = 0,
MEDIUM,
HARD
} DIFFICULTY;
Just for someone-who-wouldn't-know-what-this-is' information, this declare a restrictive integer type that can have only 3 values : EASY (= 0), MEDIUM (= 1), HARD (= 2).
It will be far cleaner (and better for your memory management).
Related
In ClassA, I have a CGFloat value x that I want to pass by reference to ClassB such that if I make a change to the CGFloat in ClassA, it will be reflected in the reference to x in ClassB. Also, when I pass it to ClassB, I want to store it as a property.
I've thought about using a CGFloat pointer, but I'm struggling to figure out the proper syntax to make it a property:
#property(nonatomic) CGFloat *x;
And then to dereference it:
self->x
I thought about using NSNumber but there is no way to set the value using NSNumber such that it will update in ClassB. I thought about giving up and making a wrapper class to store the CGFloat, but this seems like overkill.
What is the best pattern to go about doing this?
I thought about giving up and making a wrapper class to store the CGFloat, but this seems like overkill.
The advantage of this approach is safety, you create an object, both classes reference it, and ARC takes care of the memory management.
The class is easy to define, for example:
#interface ABShare1 : NSObject
#property CGFloat x;
#end
#implementation ABShare1
#end
(in a .h & .m file – same for other examples)
A class using this would be something like:
#implementation ClassA
{
ABShare1 *one;
}
...
one = ABShare1.new; // somewhere in initialisation
...
... one.x = 42; ... z = one.x * 24; ...
Note: the above stores the ABShare1 reference in a private instance variable, you can store it in a property if you wish but there is no need to.
You can call a method on another class passing the object, e.g.:
ClassB *myB;
...
[myB using:(ABShare1 *)sharedVariable];
and that other class can keep the reference as long as it requires, memory management is automatic.
I've thought about using a CGFloat pointer
This is the standard C (a subset of Objective-C) way of "passing by reference".
You can store a CGFloat * in a property, all "object" valued properties in Objective-C just store pointers (e.g. #property NSString *name; stores a pointer to an NSString object).
You must create the variable that the CGFloat * references, the equivalent of new or alloc/init in Objective-C. You can use the address of a variable, e.g. something like:
CGFloat actualX;
CGFloat *x = &actualX;
but you have to manually ensure that the referenced variable, actualX, lives at least as long as its pointer, stored in x, is in use – failure to do that results in a dangling pointer.
The other option is to dynamically allocate the storage, the direct equivalent of new, e.g. something like:
CGFloat *x = malloc(sizeof(CGFloat));
However you are now responsible for determining when the storage is no longer required and releasing it (using free()).
The first solution to you is "overkill" – maybe because while you are freed from concerns over memory management you don't get a "variable" but two functions/methods to get/set a value.
The second solution is closest to feeling like a "variable", you just use *sharedVariable rather than sharedVariable. However while the manual memory management required is standard for C programmers, it is not for Objective-C programmers.
A third approach mixes the two building on how structures (struct) in C can be used: to share a collection of variables rather than share each one individually by address, instead define a struct with a member for each variable, allocate one and share its address, something like:
typedef struct ABShare
{ CGFloat x;
CGFloat y;
} ABShare;
ABShare *one = malloc(sizeof(ABShare));
one->x = 42;
one->y = 24;
The above has the same memory management issues as the second solution, but we can convert it to a very close Objective-C equivalent:
#interface ABShare : NSObject
{
#public // required
CGFloat x;
CGFloat y;
}
#end
#implementation ABShare
#end
Note: Objective-C classes are effectively implemented using structs, indeed the first Objective-C compilers actually translated them into C struct code.
Using this is very close to the C:
ABShare *one = ABShare.new;
one->x = 42;
one->y = 24;
Same "variable" look as C but with automatic memory management.
This last scheme is essentially how Objective-C manages sharing variables when a block is created – all the local variables accessed by the block are moved into a dynamically allocated object/struct and the variables then accessed using ->.
Which is best in Objective-C? The first and the third are both "Objective-C" style, the second is usually avoided accept when interacting with C APIs. Of the first and third pick whichever feels "right" semantically, rather than concerns over performance, to you. HTH
[NSMutableData dataWithLength:sizeof(CGFloat)] and cast mutableBytes to CGFloat*
You can implement getter and setter of property #property(nonatomic) CGFloat x -without pointer
Using Swift again, am I missing something?
I have this line:
self.itemDescription?.setContentCompressionResistancePriority(UILayoutPriorityRequired, forAxis: UILayoutConstraintAxis.Vertical);
But Xcode is giving me an error:
Undefined symbols for architecture i386: "_UILayoutPriorityRequired"
Another one of Swift's quirks?
Solution: replace UILayoutPriorityRequired with 1000
self.itemDescription?.setContentCompressionResistancePriority(1000, forAxis: UILayoutConstraintAxis.Vertical);
This is not a bug. It is a misunderstanding of how Objective-C libraries are imported into Swift. It should be understood how Swift imports code (even from the Apple UIKIt Libraries) from Objective-C into Swift.
UILayoutPriority is a float. In Objective-C, a couple of values have been pre-defined for us. The pre-defined values appear to be an enum. We might expect that the same enums would be available to us in Swift.
The documentation does seem to imply an enum:
SWIFT (Documentation)
typealias UILayoutPriority = Float
OBJECTIVE-C (Documentation)
enum {
UILayoutPriorityRequired = 1000,
UILayoutPriorityDefaultHigh = 750,
UILayoutPriorityDefaultLow = 250,
UILayoutPriorityFittingSizeLevel = 50,
};
typedef float UILayoutPriority;
But in Xcode, if you ask to see the defintion of one of these enum values (UILayoutPriorityRequired, for example), you will see that they are actually defined in the header file as constant floats.
OBJECTIVE-C (Header file)
typedef float UILayoutPriority;
static const UILayoutPriority UILayoutPriorityRequired NS_AVAILABLE_IOS(6_0) = 1000; // A required constraint. Do not exceed this.
static const UILayoutPriority UILayoutPriorityDefaultHigh NS_AVAILABLE_IOS(6_0) = 750; // This is the priority level with which a button resists compressing its content.
static const UILayoutPriority UILayoutPriorityDefaultLow NS_AVAILABLE_IOS(6_0) = 250; // This is the priority level at which a button hugs its contents horizontally.
So although we may like to think of the pre-defined layout priorities as enum values (as the documentation suggests) the layout priorities are not really defined as enums; they are defined as constant floats.
A hint that there is a mis-match -- for anyone that knows the C programming language -- is that a C enum may only contain int values. The following is legal and will compile:
enum myEnum {
JGCEnum_one = 1, // this is O.K.
JGCEnum_two,
JGCEnum_three
} JGCEnum;
But we can not define floats as values for C enums. The following will not compile:
enum myEnum {
JGCEnum_one = 1.5, // compile error
JGCEnum_two,
JGCEnum_three
} JGCEnum;
Objective-C enums are the same as C enums (Swift enums are different). It is important to know if we are dealing with actual integers or floats. Because integers can be defined using the NS_ENUM macro, which can then be imported to Swift as a Swift enum.
The iBook says
Swift imports as a Swift enumeration any C-style enumeration marked with the NS_ENUM macro. This means that the prefixes to enumeration value names are truncated when they are imported into Swift, whether they’re defined in system frameworks or in custom code.
Excerpt From: Apple Inc. “Using Swift with Cocoa and Objective-C.” iBooks
That means that if UILayoutPriority had been defined as an integer using the NS_ENUM macro, it would have been imported into Swift as Swift enum. This is the case for UILayoutConstraintAxis.
SWIFT (Documentation)
enum UILayoutConstraintAxis : Int {
case Horizontal
case Vertical
}
OBJECTIVE-C (Documentation)
enum {
UILayoutConstraintAxisHorizontal = 0,
UILayoutConstraintAxisVertical = 1
};
typedef NSInteger UILayoutConstraintAxis;
Looking at the Objective-C header file confirms what the documentation says.
OBJECTIVE-C (Header File)
//
// UIView Constraint-based Layout Support
//
typedef NS_ENUM(NSInteger, UILayoutConstraintAxis) {
UILayoutConstraintAxisHorizontal = 0,
UILayoutConstraintAxisVertical = 1
};
So there are at least two ways to know if a pre-defined value you are used to using in Objective-C is available in Swift:
check the documentation
check the header file in Objective-C (found by right-clicking the value and then selecting "Jump to Definition")
There is one more way to see if a typedef you are used to using is a constant or an enum. In code, test to see if the address of the constant exists. Constants have a memory address, while enums do not. See the code below.
// this line will compile and run just fine.
// UILayoutPriorityDefaultHigh is a constant and has a memory address
// the value will be true if the device is running iOS 6.0 or later
// and false otherwise
BOOL predefinedValueIsAvailable = (NULL != &UILayoutPriorityDefaultHigh);
// this line will not compile
// UILayoutConstraintAxisHorizontal is an enum (NOT a constant)
// and does not have a memory address
predefinedValueIsAvailable = (NULL != &UILayoutConstraintAxisHorizontal);
References
Using Layout Priority in Swift
Xcode Documentation (iOS 8.2)
Apple Inc. “Using Swift with Cocoa and Objective-C.” iBooks
One thing to keep in mind is that, even though UILayoutPriority is an alias to Float, you're using Swift and you can use an extension to create semantic constants for these values:
extension UILayoutPriority {
static var Low: Float { return 250.0 }
static var High: Float { return 750.0 }
static var Required: Float { return 1000.0 }
}
Then you're code can read as:
self.itemDescription?.setContentCompressionResistancePriority(UILayoutPriority.Required, forAxis: .Vertical);
I've done this myself in one of my projects and thought it may be useful.
You can also write this:
self.itemDescription?.setContentCompressionResistancePriority(UILayoutPriority(<float number>), forAxis: .Vertical)
Example:
self.itemDescription?.setContentCompressionResistancePriority(UILayoutPriority(750), forAxis: .Vertical)
I have a Swift project that contains two UITableViewControllers. The second UITableViewController is linked to a MVC model called Model. According to the UITableViewCell I select in the first UITableViewController, I want to initialize some properties of Model with Ints or Strings. Therefore, I've decided to define those properties with Printable protocol type. In the same time, I want to perform Key Value Observing on one of these properties.
Right now, Model looks like this:
class Model: NSObject {
let title: String
let array: [Printable]
dynamic var selectedValue: Printable //error message
init(title: String, array: [Printable], selectedValue: Printable) {
self.title = title
self.array = array
self.selectedValue = selectedValue
}
}
The problem here is that the following error message appears on the selectedValue declaration line:
Property cannot be marked dynamic because its type cannot be
represented in Objective-C
If I go to the Xcode Issue Navigator, I can also read the following line:
Protocol 'Printable' is not '#objc'
Is there any workaround?
There is no way to do what you want. Non-#objc protocols cannot be represented in Objective-C. One reason is that Non-#objc protocols can represent non-class types (and indeed, you said that you wanted to use it for Int and String, both non-class types), and protocols in Objective-C are only for objects.
KVO is a feature designed for Objective-C, so you must think about what you expect it to see from the perspective of Objective-C. If you were doing this in Objective-C, you would not want to have a property that could either be an object like id or a non-object like int -- you can't even declare that. Instead, as you said in your comment, you probably want it to be just objects. And you want to be able to use Foundation's bridging to turn Int into NSNumber * and String into NSString *. These are regular Cocoa classes that inherit from NSObject, which implements Printable.
So it seems to me you should just use NSObject or NSObjectProtocol.
Unfortunately ObjC does not treat protocols as types, they are just a convenient way of grouping members. Under the covers they are of type Any, so regretfully you will have to make the property Any and cast to Printable.
The best I can thing of is:
dynamic var selectedValue: Any
var printableValue : Printable {
get {
return (Printable)selectedValue
}
set {
selectedValue = newValue
}
}
After updating to Xcode 6 many incompatible conversion assignment warnings / errors started to appeared
In the .h file:
#property (nonatomic) BOOL *done;
In the .m file:
#synthesize done;
- (id)init
{
if (self = [super init])
{
self.done = FALSE;
}
return self;
}
- (void) crashed {
self.done = TRUE; #this line gives an incompatible type conversion warning
}
Lots of these warnings appeared after the upgrade. Does anyone share similar problem?
This is not a localized problem, the issue spread across the entire project.
I thought some of my foundation was wrong, or is it ?
Not all variables in Objective C have to be declared with the * character as some newcomers to the language think. It is C legacy to show that the variable is a pointer to an object. Some basic types like int and BOOL are too simple to require the overhead of storing them in an object, so C-style primitive types are used. From the docs:
Scalar types are used in situations where you just don’t need the
benefits (or associated overheads) of using an object to
represent a value. While strings of characters are usually represented
as instances of the NSString class, numeric values are often stored in
scalar local variables or properties.
BOOL is a primitive data type in Objective C and is not supposed to be a pointer. The warnings are correct. Declare the variable like this:
#property (nonatomic) BOOL done;
Note the lack of the * character next to the variable name. Other primitive types like int or float also should be declared in a similar fashion.
Some other things about your code. The correct Objective C convention for BOOL values is YES instead of TRUE and NO instead of FALSE, so you should stick to it. Also, since Xcode 4.4, you don't need #synthesize outside of a few special cases described here. As pointed out in the comments, it's also better to use instancetype instead of id in your case as described in the docs.
I'm pretty sure you want to do it this way:
Header file
#property (nonatomic) BOOL done; // Note: NOT a pointer
Implementation file
- (instancetype)init {
if (self = [super init]) {
self.done = NO;
}
return self;
}
- (void)crashed {
self.done = YES;
}
I am looking for a way to properly perform the following.
I have multiple values in an object that are given default values (some a physical number, other calculated). The user selects a few parameters and the rest are populated for them.
After the values are populated the user can then overwrite any value of their choosing which then may cause values to be re-calculated. If the value is a calculated value that the user has entered I don't want it to change.
Take for example the following:
Class values {
NSString *userSelected:
double value1;
double value2;
double value3;
double calc1;
double calc2;
double calc3;
}
The user then selects (From a picker) values.userSelected. Upon selection, values 1 - 3 and calc 1 - 3 are assigned/calculated to their "default values".
The user can then go in and edit say value1. Once changed calc1-3 will re-calculate if they use value1. Now the user can also overwrite the calc values. So if the user overrides calc1 and then changes value1 I DO NOT want calc1 to change again since it was changed by the user.
One way I thought to do this was duplicate each value with a default and if the non-default is set return that instead:
ie
Class values {
double value1;
double defaultValue1; ... etc.
}
Hence if the non-default value is set then use that, else use the default value. This just seems like an inefficient way of doing it. Does anyone have a better thought process on how to do this. My class is around 20 properties so I'd rather not require 2 properties per variable. I am programming in iOS but this is more a methodology question vs a specific piece of code. If anything is unclear please feel free to ask.
Thanks,
DMan
One thing that could do what you're looking for: accessor overrides. Basically, you declare in your class a bunch of values using #property syntax, then do some stuff under the hood in the implementation, including overriding the getter/setters. For example:
//in MyClass.h
#interface MyClass : NSObject
#property (nonatomic, assign) double value1;
#property (nonatomic, assign) double calc1;
#end
//in MyClass.m
#implementation MyClass {
BOOL _calc1Changed;
}
//you should default that flag to NO, so...
- (id)init {
_calc1Changed = NO;
}
- (void)setValue1:(double)newValue1 {
if (!_calc1Changed) [self calculateCalc1]; //define this calculation also in this file
value1 = newValue1; //value1 is the synthesized ivar for the property you declared
}
And of course, somewhere you'll need to call _calc1Changed = YES; whenever the user sets calc1 manually, possibly in a similarly constructed override. Basically, the result is that every time you call myClassInstance.value1 = something or [myClassInstance setValue1:something] these checks will be performed and extra functionality can be performed to validate/respond to inputs.