Objective-C Where to define initial Class Properties - objective-c

just wanted to ask where I define initial class properties?
From other languages I am used to define some standard properties in the head before the content of the class starts.
For example paths to files. Settings and so on.
Where I fill these initial properties with values in Objective-C?
Thanks

Generally it's something like:
MyClass.h:
extern NSString * const staticValue1;
extern NSString * const staticValue2;
#interface MyClass : NSObject
{
NSString *_strval;
int _intval;
float _fltval;
}
#property (retain, nonatomic, readwrite) NSString *strval;
#property (assign, nonatomic, readwrite) int intval;
#property (assign, nonatomic, readwrite) float fltval;
#end
MyClass.m:
NSString * const staticValue1 = #"Something";
NSString * const staticValue2 = #"Something else";
#interface MyClass
#synthesize strval = _strval;
#synthesize intval = _intval;
#synthesize fltval = _fltval;
- (id)init
{
self = [super init];
if (self != nil)
{
[self setStrval:[NSString stringWithFormat:#"This is a %#", #"string"]];
[self setIntval:10];
[self setFltval:123.45f];
}
return self;
}
- (void)dealloc
{
[self setStrval:nil];
[super dealloc];
}
#end
This demonstrates the use of synthesized properties which are being used here to manage the memory of the instance variable _strval, which requires retaining/releasing to avoid memory leaks. Note that [self setStrval] is initialised with an autoreleased object (from [NSString stringWithFormat) and will be retained by the setter method. Alternatively these methods can be called using the following syntax, if you prefer:
self.strval = [NSString stringWithFormat:#"This is a %#", #"string"];
self.intval = 10;
self.fltval = 123.45f;

Maybe some of what you are after can be implemented with class methods.
Class methods are coded with a + (instead of the instance methods' -), and can't refer to instance variables, as they are not associated with any specific instance of the class.
This is a class method to return a default string:
+ (NSString *)myDefaultString
{
return #"Some default value";
}
You call it by simply calling it with the class name at the receiver's place. Imagine you have defined the method in a class called MyClass, the you call it like this:
NSString *str = [MyClass myDefaultString];
You'll notice that there is no alloc/init calls in this.

Public property needs to be define in .h file.
#interface MyClass {
}
#property(nonatomic, reatin) NSString *a;//Define as per needs, then synthesise in .m file
#end
For private property you need define inline category in .m file-
#interface MyClass ()
#property(nonatomic, reatin) NSString *b;//Define as per needs, then synthesise in .m file
#end
#implementation MyClass
#synthesize a = _a;
#synthesize b = _b;
- (void)viewDidLoad {
//You can initialise property here or in init method
self.a = #"Demo1";
self.b = #"Demo2";
}
//Now you can have other code for this class.
#end

Related

Cannot access Objective-C singleton's array from Swift code

I made an array in a singleton to write objects into it from multiple parts of my code. Here's how:
// in singleton.h
#import <UIKit/UIKit.h>
// make globally accessible array
#interface MyManager : NSObject {
NSMutableArray *imgArray;
}
#property (nonatomic, retain) NSMutableArray *imgArray;
+ (id)sharedManager;
#end
// in singleton.m
#import "singleton.h"
For my .m file :
#implementation MyManager
#synthesize imgArray;
#pragma mark Singleton Methods
+ (id)sharedManager {
static MyManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (id) init {
if (self = [super init]) {
self.imgArray = [NSMutableArray new];
}
NSLog(#"initialized");
return self;
}
#end
I can access my array called imgArray it from my objective C code. However, In swift I get an error when I do this:
let array = MyManager.sharedManager()
array.imgArray.add("hello world") . (!!!) Value of type 'Any?' has no member 'imgArray'
I can access MyManager.sharedManager(), but Why can't I access imgArray the same way as in objective C?
You should declare it as instancetype or MyManager *. E.g.
+ (MyManager *)sharedManager;
or
+ (instancetype)sharedManager;
A couple of suggestions:
The Swift convention for singleton’s is to use a class property name of shared, not a class method of sharedManager(). When you declare it in Objective-C, you might want to explicitly say that it’s a class property:
#property (class, readonly, strong) MyManager *sharedManager NS_SWIFT_NAME(shared);
This won’t change any of the Objective-C behavior, but in Swift, you can just do:
let manager = MyManager.shared
manager.images.add(image)
This results in more concise and idiomatic Swift code.
I’d suggest that you audit your Objective-C for nullability. I.e., confirm what can be nil and what can’t. Since both imgArray (which I might just call images) and sharedManager can never be nil, I would just use the NS_ASSUME_NONNULL_BEGIN/END macros which tells the compiler “unless I tell you otherwise, assuming this property cannot be nil”:
// MyManager.h
#import UIKit;
NS_ASSUME_NONNULL_BEGIN
#interface MyManager : NSObject
#property (nonatomic, strong) NSMutableArray <UIImage *> *images;
#property (class, readonly, strong) MyManager *sharedManager NS_SWIFT_NAME(shared);
#end
NS_ASSUME_NONNULL_END
By telling the compiler that these two cannot be nil, that means that you’ll have to do less unnecessary unwrapping of optionals in your Swift code.
As an aside, notice that I didn't declare an instance variable. (And if you did need one, I wouldn’t advise declaring it in the public interface.) Objective-C will now synthesize the ivars backing our properties automatically for us. (So my property images will have an ivar called _images that will be synthesized for me.) And you don’t need/want the #synthesize line, either:
// MyManager.m
#import "MyManager.h"
#implementation MyManager
+ (instancetype)sharedManager {
static MyManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (instancetype)init {
if (self = [super init]) {
self.images = [NSMutableArray new];
}
NSLog(#"initialized");
return self;
}
#end
Change + (id)sharedManager; to + (MyManager *)sharedManager;. Otherwise Swift doesn't know what kind of object sharedManager is and it will assume it's Any.

Objective-c readonly copy properties and ivars

I'm try to grok properties declared as both copy and readonly in objective-c, and specifically, whether I have to do the copy myself. In my init methods. Evidence suggests I do:
#interface A : NSObject
#property(nonatomic, copy, readonly) NSData *test;
- (instancetype)initWithData:(NSData *)data;
#end
#implementation A
- (instancetype)initWithData:(NSData *)data {
if ((self = [super init]) != nil) {
_test = data;
}
return self;
}
#end
int main (void) {
NSData *d1 = [NSMutableData dataWithBytes:"1234" length:5];
A *a = [[A alloc] initWithData:d1];
NSLog(#"%lx", (unsigned long)d1);
NSLog(#"%lx", (unsigned long)a.test);
return 0;
}
I had thought I could do self.test = data in my init method, but that is not permitted because it's readonly (not unexpectedly). Of course, self.test = [data copy] ensures two different objects.
So: Is there a way to create a readonly property in objective-c that copies the incoming value, or is it sufficiently an edge case that the combination is pointless and I have to do any copying myself manually anyway?
A #property declaration is merely shorthand for some accessor/mutator method declarations, and (in some cases) synthesized implementations for said accessor/mutator methods.
In your case, the #property(nonatomic, copy, readonly) NSData *test declaration expands to this equivalent code:
#interface A : NSObject
{
NSData* _test;
}
- (NSData*)test;
#end
#implementation A
- (NSData*)test
{
return _test;
}
#end
There is no setTest: mutator method because the property is declared as readonly, so the copy attribute has no effect.
You can implement your own mutator method:
- (void)setTest:(NSData*)newValue
{
_test = [newValue copy];
}
Or, you can have the compiler synthesize a mutator method for you by declaring a read/write property in a private class extension in your implementation file:
// A.m:
#interface A()
#property (nonatomic, copy) NSData* test;
#end
Both cases would allow you to use the test mutator method to copy a value to the _test instance variable:
- (instancetype)initWithData:(NSData *)data {
if ((self = [super init]) != nil) {
self.test = data;
}
return self;
}
The end result is:
#interface A : NSObject
#property(nonatomic, copy, readonly) NSData* test;
- (instancetype)initWithData:(NSData*)data;
#end
#interface A()
#property (nonatomic, copy) NSData* test;
#end
#implementation A
- (instancetype)initWithData:(NSData*)data {
if ((self = [super init]) != nil) {
self.test = data;
}
return self;
}
#end
In addition to what Darren said, the copy attribute describes what semantics the properties setter has. In your initializer, you're not using the setter, you're directly assigning to the instance variable.
It's maybe a bit hard to grok, but the instance variable is not the same thing as the property. It is used to implement the property in this case. But, assigning to the instance variable is not the same as setting the property.
If you want your initializer to also have the semantics that it copies the passed-in data, that's a separate design decision (although a good idea to go with the property's semantics). You could implement that by using a private setter as Darren suggests, but you could also just do:
_test = [data copy];
in the initializer.

objective c class property variables not populated

I have the following simple class definition:
//mycommon.h
#interface CurrentPath : NSObject
#property (nonatomic, strong) NSString* PathString;
#property (nonatomic, strong) NSMutableArray* PathArr;
- (void) addAddressToPath:(NSString*) address;
#end
//mycommon.m
#implementation CurrentPath : NSObject
#synthesize PathString;
#synthesize PathArr;
- (void) addAddressToPath:(NSString*) address{
NSLog(#"addAddressToPath...");
// Add to string
self.PathString = [self.PathString stringByAppendingString:address];
// Add to Arr
[self.PathArr addObject:address];
}
#end
In another class I do #import<mycommon.h> and declare the variable like this:
#interface myDetailViewController :
{
CurrentPath* currentPath;
}
- (void) mymethod;
#end
and in
#implementation myDetailViewController
- void mymethod{
self->currentPath = [[CurrentPath alloc] init];
NSString* stateSelected = #"simple";
[self->currentPath addAddressToPath:stateSelected];
}
#end
Problem is that the PathString and PathArr properties of self->currentPath are empty after this method call which I think should have "simple" in them. Please help!
You have to make sure that your NSString and NSMutableArray properties are initialized when your CurrentPath object is created. Otherwise, the call to stringByAppendingString will result in nil because it is sent to a nil object.
One feasible way would perhaps be
self.currentPath = [NSString string];
// or
self.currentPath = #"";
[self.currentPath addAddressToPath:#"simple"];
More elegant and robust would be to check for nil property in the addAddressToPath method.
if (!self.pathString) self.pathString = [NSString string];
if (!self.pathArr) self.pathArr = [NSMutableArray array];
Notice that am following the objective-c convention and use property names that start with lower case letters.

Strange assignment in synthesize

I have seen a lot of code like this
header.h
#interface Foo : NSObject
{
NSString *str;
}
#property(nonatomic, retain) NSString *str;
#end
and then in implementation
#implementation Foo
#synthesize str = _str;
#end
I can't understand what is the benefit of using such assignment ?
#synthesize str = _str;
It is just a common naming convention.
It is so that in your implementation, you can distinguish accessing a variable directly against accessing via the property accessor.
If you try and access str in your code, like [str length], the code won't compile. You either need to do [self.str length] or [_str length].
... and as it's an NSString immutable property, use copy, not retain.
#synthesize str = _str; will mean that the instance variable that is synthesised for the str property is called _str. In your code you therefore have a mismatch between it and the declared instance variable. So you'll actually end up with 2 instance variables, one called str and one called _str.
You want to do this:
#interface Foo : NSObject
#property(nonatomic, retain) NSString *str;
#end
#implementation Foo
#synthesize str = _str;
#end
Or this:
#interface Foo : NSObject {
NSString *str;
}
#property(nonatomic, retain) NSString *str;
#end
#implementation Foo
#synthesize str;
#end
Or obviously rename the declared instance variable, _str.
There's lots of questions on SO already about whether or not to prefix with _ such as - Prefixing property names with an underscore in Objective C .
Here's a quick example of how it can be useful to use the name change:
#interface MyClass : NSObject {
NSString *myString;
}
#property (nonatomic, retain) NSString *myString;
- (NSString *)stringTreatment:(NSString *)str;
#end
#implementation MyClass
#synthesize str = _str;
- (id)init {
self = [super init];
if (self) {
self.str = [NSString string];
}
return self;
}
- (NSString *)stringTreatment:(NSString *)str {
return [str uppercaseString];
}
#end
If you wouldn't have synthesized str as _str, you would get a warning in that stringTreatment method saying that the local declaration of str hides the instance variable.
Also, in your code, you could be assigning a value to _str and have an external class call [MyClass str] and it would return it.
So, to make a long story short, "str" remains the name of your property and "_str" is the internal reference to that property. For example, you won't be able to use [MyClass _str], that won't work. Makes sense?
Hope this helps.

In Objective-C, how do I pass a property as an argument for a function and then call the setter/getter methods?

The code is probably the best way to see what I am trying to do:
AcInfo.h:
#interface AcInfo : NSManagedObject {
#private
}
#property (nonatomic, retain) NSString *registrationNumber;
#end
AcInfo.m:
#implementation AcInfo
#dynamic registrationNumber;
#end
AnotherClass.h:
#interface AnotherClass : NSObject {
}
#property (nonatomic, retain) AcInfo *detailItem;
#property (nonatomic, retain) IBOutlet UITextField *registrationNumberTextField;
- (void)setDetailItemValueFromUIElement:(id *)uiElement forAcInfoTarget:(id *)acInfoTarget;
#end
AnotherClass.m:
#import "AcInfo.h"
#implementation AnotherClass
#synthesize detailItem, registrationNumberTextField;
- (void)viewDidLoad
{
[super viewDidLoad];
registrationNumberTextField.text = #"Test";
// I expect this to set detailItem.registrationNumber to the value of
// registrationNumberTextField.text (Test) but it doesn't change anything!
setDetailItemValueFromUIElement:registrationNumberTextField forAcInfoTarget:detailItem.registrationNumber;
}
- (void)setDetailItemValueFromUIElement:(id *)uiElement forAcInfoTarget:(id *)acInfoTarget
{
if ([(id)uiElement isKindOfClass:[UITextField class]]) {
// This doesn't do anything when it returns!
(NSString *)acInfoTarget = (UITextField *)uiElement.text
return;
}
}
#end
In short, I want acInfoTarget to call the getter [detailObject registrationNumber] and the setter [detailObject setRegistrationNumber] in the setDetailItemValueFromUIElement: function...
You can set or read properties by name using
// setter
NSString *propertyName = #"myProperty";
[object setValue:newValue forKey:propertyName];
// getter
id value = [object valueForKey:propertyName];
This is slower than using the normal dot notation, though, and it's frequently (though not always) a sign of poorly-designed code.
Also note that id is a pointer type, so you probably don't actually mean "(id*)".
Your code wants to look something like this, I think:
- (void)setDetailItemValueFromUIElement:(id)uiElement forAcInfoTarget:(NSString*)acInfoTarget {
if ([(id)uiElement isKindOfClass:[UITextField class]]) {
NSString *newValue = ((UITextField*)uiElement).text;
[self.detailItem setValue:newValue forKey:acInfoTarget];
}
}
Properties are just syntax sugar for a couple of accessor methods. They are not, in essence, variables so you shouldn't treat them as such. If you want to affect a property, then what you wanting to do is call a method. So you should pass a id and selector parameter and not pointer to a variable type.