How are colons used with ivars? - ios

I'm doing some code refactoring and I've come across some ivars syntax that I haven't seen before. The code looks like
#interface Object : NSObject {
#private BOOL aBool:1;
}
#end
My question is, what does the :1 do?

This syntax has the same meaning for an ivar as it does inside a struct; you're declaring a bitfield of the specified size.
This likely doesn't have any effect on the actual size of the class in this case -- I don't think you can allocate less than a byte -- but the compiler will warn you if you try to put a value into the variable that's too large for the bitfield size you indicated:
#interface BittyBoop : NSObject
{
unsigned char bit:1;
unsigned char bits:4;
}
#end
#implementation BittyBoop
- (void)doThatThingIDo
{
bit = 2; // Implicit truncation from 'int' to bitfield changes value from 2 to 0
bits = 2; // no warning
}
#end

Related

Override a static constant int in .h file in Objective-C?

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.

After updating to Xcode 6 many incompatible conversion assignment warnings / errors appeared

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;
}

Getting a char out of NSarray in Objective-C

I am trying to get a char out of an NSArray by generating a random number. Here is my code
#interface ClassName : NSObject
#property (nonatomic) char letter;
-(id) init;
-(char) changeLetter;
#end
Implementation File:
#import "ClassName.h"
#interface ClassName()
#property (strong, nonatomic) NSArray *alphabet;
#end
#implementation ClassName
-(id) init {
[self alphabet];
return self;
}
-(NSArray *) alphabet
{
if (!_alphabet) _alphabet =
#[#'A', #'B',#'C', #'D',#'E', #'F',
#'G', #'H',#'I', #'J',#'K', #'L',
#'M', #'N',#'O', #'P',#'Q', #'R',
#'S', #'T',#'U', #'V',#'W', #'X',
#'Y', #'Z'];
return _alphabet;
}
-(char) changeLetter
{
//Picks a number between 0-26
int nextLetter = arc4random_uniform(26);
//Changes the value of the bubble
[self setValue: nextLetter];
return [self.alphabet objectAtIndex:nextLetter];
}
#end
The problem I am running into is the return statement coming from the changeLetter. It is telling me "Incompatible pointer to integer conversion returning 'id' from a function with result type 'char'
I have no idea what the problem is. I am new to Objective-C and am already getting very frustrated it already. It seems so hard and complicated compared to Java.
Can someone tell me what I am doing wrong?
Any help is greatly appreciated.
Coming from Java you might be confused as Java supports autoboxing and autounboxing.
In both Java & Objective-C you cannot store non-object types, such as integers and characters, directly in collections, such as arrays. Instead such values are stored as objects which wrap (or box) the basic value.
In Objective-C the common wrapper is NSNumber which is capable of storing integers, floating point numbers, characters and booleans. There is also an NSValue wrapper for storing other basic values.
Java will wrap a basic value as an object, and unwrap an object to produce the basic value, according to the context automatically. In Objective-C you must do this yourself.
Your expression: #'A' et al takes the character literal, 'A', and wraps it as an NSNumber. In Java the equivalent of the # would be inserted automatically.
So your array _alphabet contains instances of NSNumber. When you access an element of the array using:
[self.alphabet objectAtIndex:nextLetter]
the NSNumber instance in the array is returned, you still need to unwrap it to obtain the character and you do this with the charValue method:
[[self.alphabet objectAtIndex:nextLetter] charValue];
Again Java does the equivalent automatically for you.
Note: for the actual task you are doing there are better ways to return a random capital letter, in your code you could even just use 'A' + nextLetter (remember in C characters are just integers...).
HTH

How to get the correct autocomplete in XCode for a block variable?

I have a block thats stored as an instance variable in a class
typedef void ((^didSelectWord)(NSString* word));
#property (nonatomic,strong) didSelectWord wordSelected;
and i want xcode to auto fillout the block like when you type [UIView animateWithDuration and xcode autocompletes a block for it.
When i autocomplete my block it just fills out
[self.suggestedSearchTermView setWordSelected:(didSelectWord)wordSelected
instead of
[self.suggestedSearchTermView setWordSelected:^(NSString *word) {
Is it possible to change something to make Xcode understand how to autocomplete this block?
Ok I did some testing.
Apparently you have two (far from perfect) options:
avoid the typedef and declare the property as
#property (nonatomic,strong) void (^wordSelected)(NSString * word);
As noted in the comments, this has the drawback of skipping the parameter name in the autocompletion.
explicitly add a setter declaration in the interface
typedef void ((^DidSelectWordBlock)(NSString* word));
#interface YourClass : NSObject
#property (nonatomic,strong) DidSelectWordBlock wordSelected;
- (void)setWordSelected:(DidSelectWordBlock)wordSelected;
#end
this will cause Xcode to resolve the type definition before the setter definition, giving you the nice autocompletion that you would expect. The obvious drawback is the extra setter declaration in the interface.
That said, you should fill in a bug report: http://openradar.appspot.com/
Declare your property without typedef, like this:
#property (nonatomic,strong) void (^wordSelected)(NSString *word);
With this definition Xcode would give you the expansion below:
MyClass *test = [MyClass new];
[test setWordSelected:(void (^)(NSString *))wordSelected];
In exacerbated frustration, I made a macro consolidating this gross process..
#define BlockProperty(SIGNATURE,TYPENAME,varname,Varname) typedef SIGNATURE; #property (nonatomic,copy) TYPENAME varname; - (void) set##Varname:(TYPENAME)_
Now what Previously would've required (for proper autocompletion)..
typedef void(^OnEvent)(BOOL ok,id result);
#property (nonatomic,copy) OnEvent varname;
- (void) setVarname:(OnEvent)_;
is simply
BlockProperty(void(^OnEvent)(BOOL ok, id result),OnEvent,varname,VarName);
QUITE a bit easier, less verbose, AND you get the benefit of the typedef AND and you don't have to create the unsightly, theoretically unneeded setter declaration!
If you WANT to reuse a "type" you'll need another one (which this time will only take THREE parameters (as the block type cannot be redeclared).
#define BlockProp(TYPENAME,varname,Varname) #property (nonatomic,copy) TYPENAME varname; - (void) set##Varname:(TYPENAME)_
BlockProp(OnEvent,anotherVar,AnotherVar);
You could just create a new block type (name) for each property even if their signatures match (using the first macro), but that's kind of gross. Enjoy!

get error when to use global Variable

I have 2 class in my program
first class is class1 and second class is class2.I want create and initialize global variable in class 1 and to use in class 2 but compiler give me this ERROR XD :
Undefined symbols for architecture i386:
"_saeid", referenced from:
-[class2 viewDidLoad] in class2.o
ld: symbol(s) not found for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I create global variable in class1 and run that in class2 with this way but don't work:
class1.h
extern int saeid; // this is global variable
#interface class1 : UITableViewController<UITableViewDataSource,UITableViewDelegate>
#property (nonatomic,strong) IBOutlet UITableView *table;
#end
class1.m
#import "class1.h"
#import "class2.h"
#implementation class1
{
int saeid;
}
#synthesize table;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
int x = (indexPath.row)+1;
saeid = x; //initialize global variable
NSLog(#"X & SAEID: %d & %d",x,saeid);
}
class2.h
#import "class1.h"
#interface class2 : UIViewController<UIScrollViewDelegate>
{
}
#end
class2.m
#import "class2.h"
#implementation class2
{
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Saeid in class2 : %d",saeid);
}
There seems to be some confusion here. Most importantly, a global variable cannot be "in" a class--global variables are by definition outside of any classes. So if you really want a global variable (more on this later) then you need to take the int saeid; definition in class1.m outside of the class definition, and just have it at the file level.
After you've done that, things still won't compile. The statement extern int saeid; roughly says to the compiler "I've defined an integer named saeid somewhere else, so just pretend it exists and let the linker figure out how to hook it up." There is no reason to have this statement in class1.h because this global variable is not used anywhere in that file. Instead, you should put this extern statement near the top of class2.m. It is used in that file, so you need to assure the compiler that the variable is defined somewhere as it is compiling that file.
Those steps should get your code to compile. But now you should stop and think about whether or not you really want a global variable. Global variables tie your classes together and make it hard to change one without affecting (and possibly breaking) others. They make it harder to test your code, and they make it more confusing to read your code. Another option to consider here is to create saeid as a property on the class1 class, and add a class1* property to class2. Then when you create your class2 instance, pass along a pointer to the existing class1 instance. The class2 instance can keep that pointer and use it to access the saeid property as needed.
In Objectice-C you can't have class variables, just instance variables.
If you want to have a global var you'd write:
#import "class1.h"
#import "class2.h"
int saeid;
#implementation class1
{
}
#synthesize table;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
int x = (indexPath.row)+1;
saeid = x; //initialize global variable
NSLog(#"X & SAEID: %d & %d",x,saeid);
}
But that's just a global var, it's got nothing to do with the class!

Resources