Defining variables, setters and getters in Objective-C - ios

I have a hard time getting myself introduced to Objective-C and iOS programming. I tried to search for this problem, but the results weren't explained simple enough.
So I have a viewController.h and a viewController.m. In the .h I add my Labels/Buttons etc. between #interface and #end
#property ( nonatomic, strong ) UILabel *facebookLoginTextLabel;
#property ( nonatomic, strong ) UIButton *facebookLoginButton;
And in the .m I'm simply synthesizing them between #implementation and the first method
#synthesize facebookLoginTextLabel = _facebookLoginTextLabel;
#synthesize facebookLoginButton = _facebookLoginButton;
The problem I'm facing now, is what to do with other values? As an example. I have a boolean value. Where do I declare it? How do I set it? How do I get the value? It's way too confusing for me. The same counts for an NSInteger?
I want, dependent on the ifcase, set the NSInteger to 0, 1 or 2. How do I achieve this? I tried getting and setting like this
.h
#property ( nonatomic ) CGFloat *width;
.m
#synthesize width = _width;
viewDidLoadMethod
_width = [self returnScreenWidth];
returnScreenWith method
- ( CGFloat ) returnScreenWidth {
return [UIScreen mainScreen].bounds.size.width;
}
This doesn't work. Why? How do I set, how do I get? How do I declare variables? Way too confusing from a PHP and an Android-Developer.

First of all, it is CGFloat width not CGFloat *width as it is a C struct, not an Objective C class where you define the property in the header file:
#property (nonatomic) CGFloat width;
Instead of accessing the under-score instance variable, just use self.width whereby you access the #property using self.

Remember, all basic/primitive data types like (int, float, char, BOOL) need not be used as you are doing, always use assign.
Yes BOOL is a primitive, as this is typedef to char.
It should be defined as :
#property (assign) <primitivetype> propertyName;
Also to note down, with new compiler (I hope you are using Xcode4.2 onwards i.e LLVM),
#synthesize propertyName is done automatically by the compiler as
#synthesize propertyName = _propertyName;
atomic and nonatomic depends on your requirement, so is the pointer to int or float.

Related

Can I use category and runtime to add a BOOL type property "isScrolling" to UIScrollView?

I used to add a NSString *type property to UIButton,today however,I want to add a BOOL type property isScrolling to UIScrollView to indicate whether the scrollView is scrolling in the same way but there showed something wrong,here is my code:
#import <UIKit/UIKit.h>
#interface UIScrollView (Util)
#property (nonatomic,assign) BOOL isScrolling;
#end
#import <objc/objc-runtime.h>
#interface UIScrollView ()<UIScrollViewDelegate>
#end
#implementation UIScrollView (Util)
static void *strKey = &strKey;
- (void)setIsScrolling:(BOOL)isScrolling{
objc_setAssociatedObject(self, & strKey, isScrolling, OBJC_ASSOCIATION_ASSIGN);
}
- (BOOL)isScrolling{
return objc_getAssociatedObject(self, &strKey);
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
self.isScrolling = YES;
}
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView{
self.isScrolling = NO;
}
#end
And the error is:
Is there any way to deal with thses errors and can we use category and runtime to achieve the goal of adding a BOOL property to UIScrollView to indicate whether the scrollView is scrolling?
Hope someone can give me some advice,thanks a lot.
An associated object must be just that, an object, and so a value of the non-object BOOL type won't work unless wrapped as an object. Fortunately that is pretty easy:
In the call to objc_setAssociatedObject change isScrolling to #(isScrolling) and change OBJC_ASSOCIATION_ASSIGN to OBJC_ASSOCIATION_RETAIN_NONATOMIC. This will create and pass an NSNumber object, the second change requesting that this object's lifetime be tied to that of the first parameter, self.
In isScrolling change objc_getAssociatedObject(self, &strKey) to [objc_getAssociatedObject(self, &strKey) boolValue]. This will extract the BOOL value from the stored NSNumber object.
HTH
Try this :
Try to convert boolean to nsnumber.
-(void)setIsScrolling:(BOOL)isScrolling{
objc_setAssociatedObject(self, & strkey), [NSNumber numberWithBool:isScrolling], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
As the error is showing implicit conversion of BOOL to id, you need to send a object instead of primitive types.
The method signature for objc_setAssociatedObject is
/**
* Sets an associated value for a given object using a given key and association policy.
*
* #param object The source object for the association.
* #param key The key for the association.
* #param value The value to associate with the key key for object. Pass nil to clear an existing association.
* #param policy The policy for the association. For possible values, see “Associative Object Behaviors.”
*
* #see objc_setAssociatedObject
* #see objc_removeAssociatedObjects
*/
OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
Above you can see the value should be object.
Change your code
#property (assign, nonatomic) BOOL isScrolling; to #property (strong, nonatomic) NSNumber *scrolling;
And change OBJC_ASSOCIATION_ASSIGN to OBJC_ASSOCIATION_RETAIN_NONATOMIC in your case objc_setAssociatedObject(self, &strkey, scrolling, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
And use [_scrolling boolValue] for checking.
You can't set the primitive data type as an AssociatedObject, It's an Object. Convert bool to NSNumber when you are saving the data. Convert NSNumber to bool while reading the data.
OBJC_ASSOCIATION_ASSIGN - Specifies a weak reference to the associated object.
OBJC_ASSOCIATION_RETAIN_NONATOMIC - Specifies a strong reference to the associated object, and that the association is not made atomically.
.h File :
#import <UIKit/UIKit.h>
#interface UIScrollView (ScrollViewCategory)
#property (nonatomic, strong)NSNumber *isScrolling;
#end
.m File
#interface UIScrollView ()
#end
#import <objc/runtime.h>
#implementation UIScrollView (ScrollViewCategory)
#dynamic isScrolling;
- (void)setAssociatedObject:(id)object {
objc_setAssociatedObject(self, #selector(associatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)associatedObject {
return objc_getAssociatedObject(self, #selector(associatedObject));
}

No automatic underscore ivar in Xcode 5.1.1

I just noticed that, for some reason, I don't seem to have automatically created underscore iVars in my iOS 7 project, and I wonder why that is. My setup:
MyClass.h
#property (readonly) NSNumber *aNumber;
MyClass.m
#interface MyClass ()
#property (readwrite, strong) NSNumber *aNumber;
#end
#implementation MyClass
(...)
- (NSNumber *)aNumber {
return _aNumber;
}
- (void)setANumber:(NSNumber *)aNumber {
_aNumber = aNumber;
}
#end
This results in Use of undeclared identifier: '_aNumber'.
Why is that so? I thought that underscore iVars are always automatically synthesized? Is it because of the class extension I use? If I put in #synthesize aNumber = _aNumber; it (obviously) works.
There is one exception to the automatic synthesize rule.
If you override both the getter and the setter of a property then you will have to manually synthesize the property.
This has been the case ever since auto synthesis came in.
Just add the #synthesize line and it will be fine.

Objective-C: Passing double between viewControllers?

If I have viewController1 and viewController2 and user enters a double value into a textField in viewController1 this is passed to a variable in viewController2 and that value can be used.
This variable must then be able to be transferred to the UIView as that is what requires the value.
I'm struggling even at the first hurdle and that is to make a double a #property or what it needs to be, A lot of tutorials and examples show NSStrings being transferred but it is important that I transfer a double.
Here's what I have so far..
ViewControllerInputs.h - viewcontroller1
#interface ViewControllerInputs : UIViewController {
IBOutlet UITextField *thicknessField;
IBOutlet UITextField *capWidthField;
double *thicknessValue;
double *capWidthValue;
}
ViewControllerImage.h - viewcontroller2
#interface ViewControllerImage : UIViewController {
double thicknessValue1;
double capWidthValue1;
}
ViewControllerInputs.m - viewcontroller1
-(IBAction)createWeld {
ViewControllerImage *secondViewController = [[ViewControllerImage alloc]
initWithNibName:#"ViewControllerImage" bundle:nil];
ViewControllerImage.thicknessValue1 = thicknessValue;
//Require modal/push to viewControllerImage
}
UIView inside ViewControllerImage - viewcontroller2
This is where the variables thicknessValue1 and CapWidthValue1 will be used.
You have a few problems:
1) Get rid of the asterisk for each of the double ivars
2) Get rid of the double ivars and use properties instead.
#property (nonatomic, assign) double thicknessValue1;
#property (nonatomic, assign) double capWidthField;
As you have it, you are attempting to assign to a non-existent property.
Use
#property (nonatomic) double someDoubleVariable; for example to define the property, in the interface after defining the instance variable,
#synthesize someDoubleVariable; within the implementation to generate the getter and setter functions.
I suggest you read the documentation on Objective-C 2.0 properties — you're lucky to be programming for a platform whose parent company makes the cleanest documentation. Make good use of it.
Good luck.

Why is my ivar not getting set on NSManagedObject Subclass

I have a project using CoreData. I use Mogenerator to generate the subclasses.
When I set the value of a property, this value isn't actually assigned. Each subsequent time I try to set the value, the previous value I set it to was not assigned.
This worked fine as my underlying data framework was Mantle, but since moving to CoreData, this stopped working. I rely on KVO to keep some UIView objects up-to-date with the model.
Again, the ivars of a CoreData NSManagedObject subclass do not seem to take the values I assign them.
Consider the following interface:
#interface Light : _Light{}
/**
Light / Color Properties
*/
#property (nonatomic, assign) CGFloat brightness; // 0...1
#property (nonatomic, assign) CGFloat hue; // 0...1
#property (nonatomic, assign) CGFloat saturation; // 0...1
#property (nonatomic, assign, getter = isEnabled) BOOL enabled;
#property (nonatomic, readonly) UIColor *color; // derived from the above
- (void)setHue:(CGFloat)hue saturation:(CGFloat)saturation; // it often makes sense to set these together to generate fewer KVO on the color property.
#end
and the following .m file:
#interface Light ()
{
CGFloat _hue, _saturation, _brightness;
UIColor *_color;
}
#property (nonatomic, assign) BOOL suppressColorKVO;
#property (nonatomic, readwrite) UIColor *color;
#end
#implementation Light
#synthesize suppressColorKVO = _suppressColorKVO;
- (void)setHue:(CGFloat)hue saturation:(CGFloat)saturation
{
BOOL dirty = NO;
if (saturation != _saturation) {
// clamp its value
[self willChangeValueForKey:#"saturation"];
_saturation = MIN(MAX(saturation, 0.0f), 1.0f);
[self didChangeValueForKey:#"saturation"];
dirty = YES;
}
if (hue != _hue) {
[self willChangeValueForKey:#"hue"];
_hue = MIN(MAX(hue, 0.0f), 1.0f);
[self didChangeValueForKey:#"hue"];
dirty = YES;
}
if (dirty) {
if (!_suppressColorKVO) {
[self setColor: self.color];
}
}
}
// other stuff... the color accessors are also custom. Derived from the h, s, b values.
#end
I assume I'm not playing nice with CoreData, but I have no idea what's wrong. These hue, saturation, brightness are all 'transient' (not in the core data sense) because they are constantly updated by some hardware we are interfacing with so there's no need to save their state.
If hue and saturation are properties in your model then you should be setting their values using setPrimitiveValue:forKey: (or the associated generated primitive methods).
That said, your code all looks custom as model attributes would be NSNumber instances and mogenerator would create value methods for you. So I'm going to guess that these attributes you have aren't backed in the model and that's why they aren't being stored.
So, add the attributes to the model and access the values using the appropriate methods.
In the end it had nothing to do with CoreData. This approach above DOES work with CoreData objects. You can have in a subclass some "transient" properties that exist outside of the CoreData NSManagedObject and you can create your own ivars for them, and your own accessors, and it cooperates.
In relation to this question, I have a complex system that also sends some commands to some hardware, and the hardware returns a response whether it accepted the command with the current status. It turns out I had a bug in that handler which was setting these values back to some unexpected value.
What helped me debug it were using watchpoints in the debugger. Really handy feature! You set a watchpoint and it will break in the debugger whenever the memory address of your variable is set with a new value. (A tiny bit more on that here)

Declaring floats in objective c

I'm new to Objective-C and I'm having trouble with the whole nonatomic, strong, weak, etc. I'm wondering if I will have any issues using Core Data with float values which are defined like so:
#property (nonatomic) float * rating;
#property (nonatomic) float * mRating;
Should I declare the differently?
Yes, you should declare them without asterisks:
#property (nonatomic) float rating;
#property (nonatomic) float mRating;
Asterisks indicate pointers. All Objective C classes are declared with asterisks, because instances are referred to through pointers. Primitives such as floats, ints, etc. are defined as values, i.e. without asterisks. Same goes for typedef-ed types such as CGFloat and NSInteger: scalar fields of these types should be defined without an asterisk.
You should definitely lose the *, unless you are meaning to create a pointer. Outside of that it looks great!

Resources