I'm trying to build an app in OC and have a constant in .h file like this that defines how many columns should be on a menu:
// cellManager.h
static int const cellNumberPerRow = 4;
Now in my view manager file(.m), I need to change the number of columns to 3 when the font size changes. So far I have tried:
// menuManagerView.m
if ([self isBigFontSize]) {
// ....
cellNumberPerRow = 3;
// ...
But this gives me an error Cannot assign to variable 'cellNumberPerRow' with const-qualified type 'const int'; And when I tried to add identifier before like this:
static int const cellNumberPerRow = 3;
There is a warning Unused variable 'cellNumberPerRow' and the column number remains 4;
I feel like there should be an elegant way to do this but couldn't find it anywhere. I'm really new to iOS dev so would appreciate anyone's input, Thanks!
UPDATE
I defined a new integer variable, assigned the value of const variable to it, and replaced all old cellNumberPerRoe with the new variable in the .m file. Now it worked. But I wonder if there's any better way to do this?
static int newCellNumberPerRow = cellNumberPerRow;
If you want to change the value of a constant under some circumstances it implies it's no longer a constant. You could circumvent the const compiler check by using a pointer, but that's counterproductive. It'd be much easier to just drop const from definition altogether.
What I would suggest as an alternative is a computed property in your file manager class defined like this:
typedef NS_ENUM(int, CellNumberPerRow) {
defaultCellNumberPerRow = 4,
smallerCellNumberPerRow = 3,
};
#interface YourManager: NSObject
#property(readonly,nonatomic) int currentCellNumberPerRow;
#end
#implementation DocumentItem
-(int)currentCellNumberPerRow {
if ([self isBigFontSize]) {
return smallerCellNumberPerRow;
}
return defaultCellNumberPerRow;
}
#end
Now to get the appropriate cell number per row you would use currentCellNumberPerRow property instead.
Perhaps making the currentCellNumberPerRow a class property #property(class,...) instead of instance property could also turn out convenient.
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
In C, we could do the following to create a 2D array:
int intArray[10][10];
In C99, we could create a VLA:
size_t col = 10;
size_t row = 10;
int array[row][col];
Within a method in Objective-C, I can create a 2D array that hold ids as follows:
id genObjectArray[10][10];
Is it possible to create an 2d array ivar in Objective-C?
The following is what I have tried:
#interface myClass ()
{
id objArray[][];
//This doesn't work, unless I specific size.
//I want to do this, so that I could specific the size later during
//runtime
}
In C, I could do the following and allocate space for a 2D array later within a block scope:
int **array;
int *elements;
I can do the same within Objective-C, too, but the problem arises when I use id or other object types; other words, the following is not valid:
id **array;
id *elements;
Thus, my question is, is it possible to declare a C-style 2D array as ivar that holds ids?
I understand that we could achieve that using normal NS(Mutable)Array; but this just serves for educational purposes.
You can't do this. For a C99 VLA, the space required for the array is allocated at the point the array is declared. For an ivar, the analogous time to do that would be when the object was allocated and initialized, but there's no support in Objective C to do that. You'd need to have a stronger definition of what an object constructor can do (more like Java's constructors than Objective C's initializers).
The closest you can get would be something like this:
#interface myClass () {
id * objArray;
}
-(instancetype)initWithRow:(size_t)row col:(size_t)col {
self = [super init];
if (self) {
objArray = calloc(row * col * sizeof(id));
}
return self;
}
-(void)dealloc {
free(objArray);
}
In that case, you're declaring the ivar as a pointer and managing the storage yourself (and the stride, for a multi-dimensional array).
Obviously, NSArray is better in all possible ways.
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.
This question already has answers here:
Objective-C: how to declare a static member that is visible to subclasses?
(3 answers)
Closed 8 years ago.
I want a static varibale in .h of a class and want it to be inherited to its child class.
#interface :UIViewController
static bool isSearchWindowOpen ; //something like this.
#end
If i write like :
static bool isSearchWindowOpen ;
#interface :UIViewController
#end
it works fine but cannot be inherited by child classes.
pls suggest.
This sounds a bit like you are confusing this with some other programming language, like C++. In Objective-C, just like C, a static variable is a variable with file scope. If you declare a static variable in a header file, then any source file including that header file has its own copy of the static variable.
You'd probably want a class method
+ (BOOL)isSearchWindowOpen
with implementation
static BOOL sSearchWindowOpen;
+ (void)setSearchWindowOpen:(BOOL)open { sSearchWindowOpen = open; }
+ (BOOL)isSearchWindowOpen { return sSearchWindowOpen; }
Probably even better to write code that checks whether the search window is open, instead of relying on a static variable that you have to track correctly all the time.
The variable as declared has nothing to do with the class. It’s a global static in the “C sense”: only code in the same file can access it. (See Wikipedia for details.) You can write class accessors:
static BOOL foo;
+ (void) setFoo: (BOOL) newFoo
{
foo = newFoo;
}
That way the class descendants can access the variable, too. But it’s not a good idea anyway. What problem are you trying to solve?