How to handle static frameworks dependencies - ios

I am trying to build Objective-C static frameworks but ran into hierarchy/organization problems.
For the question, assume I have 3 classes, classA, classB, and classC. classB and classC are children classes of classA. classB and classC each needs special resource files.
#import "classB.h"
#import "classC.h"
#implement classA
+ (classA)factoryMethodCreateType:(NSString *)type {
classA a;
if ([type isEqualToString:#"classB"]) {
a = [[classB alloc] init];
} else if ([type isEqualToString:#"classC"]) {
a = [classC alloc] init];
}
return a;
}
#end
I'd like to statically package the frameworks, classB.framework and classC.framework, such that when a user wants to use classB, they don't need to include classC and its resource files. Additionally, the user can use classA as the entry point to use either of the frameworks.
I assume if I just create classB.framework (includes classA and classB) and classC.framework (includes classA and classC), when the user wants to use both types and include both frameworks, the user will face duplicate symbols?
What is the best way to handle this situation? Is the following approach the best way to do it?
Change classA's implementation to dynamic creation of classB or classC and not include their header files.
Build 3 frameworks instead of 2: classA.framework, classB.framework, classC.framework. When the user wants to use classB, the the user should include both classA.framework and classB.framework.
#implement classA
+ (classA)factoryMethodCreateType:(NSString *)type {
classA a;
id obj = [NSClassFromString(type) alloc];
a = objc_msgSend(obj, #selector(init));
return a;
}
#end

Related

Use of undeclared identifier error in my case

My code invokes a C library function:
#implementation Store
...
-(void) doWork {
// this is a C function from a library
int data = getData();
...
}
end
I am unit testing the above function, I want to mock the C function getData() in my test, here is my test case:
#interface StoreTests : XCTestCase {
int mData;
Store *store;
}
#end
#implementation StoreTests
-(void) setUp {
[super setUp];
mData = 0;
store = [[Store alloc] init];
}
-(void) testDoWork {
// this call will use the mocked getData(), no problem here.
[store doWork];
}
// mocked getData()
int getData() {
mData = 10; // Use of undeclared identifier 'mData', why?
return mData;
}
...
#end
Why I get complier error:
Use of undeclared identifier 'mData' inside mocked getData() function?
You are misunderstanding how instance methods and variables work.
Every instance method has a variable self which references the current instance (or "current object") and a use of an instance variable, such as mData, is shorthand for accessing that variable using self, e.g self->mData, where -> is the (Objective-)C operator for field access. So your setup method written "long hand" is:
-(void) setUp {
[super setUp];
self->mData = 0;
self->store = [[Store alloc] init];
}
But where does self, the reference to the instance, itself come from? Well it's not magical, just hidden, it is passed to an instance method automatically as a hidden extra argument. At this point which switch to pseudo-code to show this. Your setup method is effectively compiled as:
-(void) setUp withSelf:(StoreTest *)self {
[super setUp];
self->mData = 0;
self->store = [[Store alloc] init];
}
and a call such as:
StoreTests *myStoreTests = ...
[myStoreTests setup];
is effectively compiled as something like:
[myStoreTests setup withSelf:myStoreTests];
automatically adding the extra self argument.
Now all the above only applies to methods, and enables them to access instance variables and methods, it does not apply to plain C functions - they have no hidden self argument and cannot access instance variables.
The solution you mention in the answer you added of declaring mData outside of the interface:
int mData;
#interface StoreTests : XCTestCase {
Store *store;
}
#end
changes mData into a global variable, instead of being an instance variable. C functions can access global variables. However this does mean that every instance of the class shares the same mData, there is only one mData in this case rather than one for every instance.
Making an instance variable into a global is therefore not a general solution to to issues like this, however as it is unlikely that you will have more than one instance of your StoreTests class it is a suitable solution in this case.
You should however make one change: you can only have one global variable with a given name with a program, so your mData must be unique and is accessible by any code within your program, not just the code of StoreTests. You can mitigate this my declaring the variable as static:
static int mData;
this keeps the variable as global but only makes it visible to code within the same file as the declaration, which is probably just the code of StoreTests.
HTH
I found one solution for my question, that is declare mData above #interface StoreTests : XCTestCase, something like this:
int mData;
#interface StoreTests : XCTestCase {
Store *store;
}
#end
...

Without using property how can we access a variable in another class in iOS?

If I want to access a variable that is declared in Class A and I
want to use that variable in Class B .
I don't want to use Property.
How Can I do ?
It is bad practice to declare a public variable without making it a property.
You may also use KVC to access(both read and write) ivar, even if it is readonly.
[instanceOfMyClass valueForKey:#"myIvar"];
I hope someone finds my first stackOverflow answer helpful :)
#interface Class1
{
#public
int var; // if you not declare it public by default it'll be protected
}
// methods...
#end
// Inside a Class2 method:
Class1 *obj = ...;
obj->var = 3;
But property approach is far better.
Well, you can declare variable as public and access it with selector operator, but this is not recommended:
#interface A:NSObject{
#public
int x;
}
#end
...
//Somewhere inside class B
A *a = [[A alloc] init];
a->x;
However it's hard to imagine why it can be better that to use a property.
The 4 possibles cases that I can think to access a variable are:
1 Declare the variable in class A as public
#public
BOOL _test;
Totally not recommended. If you need a public variable you use a property.
2 Use a property.
#property (readonly, getter = myMehtodName) id myVariable;
3 Use a custom method. In practice it acts the same as a property with readonly attribute. Also you can access it with the dot notation.
4 Use KVC to access the variable.
This can be useful when you don't know the name of the property / variable in compilation time.
A little example:
NSString *myKey = [self obtainKey];
id myVariable = [self valueForKey:myKey];
if ([myVariable isKindOfClass:[NSString class]]) {
//Do something
}else {
//Do another thing
}
Note that key can be either a method name or a variable.

Structure objective-c classes

What is the best way to structure a class that has a to many relationship in it?
I come from a C# background. So let me show you how I would do it in C#.
class User {
public List<string> Items { get; set; }
}
Code to access Items:
User u = new User();
u.Items = new List<string>();
u.Items.Add( "foo" );
u.Items.Add( "bar" );
foreach( string s in u.Items ) {
Console.WriteLine( s );
}
How do I do this in Objective-C?
You need to learn ObjC in detail:
Class in ObjC: right click -> create new file and select ObjC class
Each class have property in .h file for you to access outside. Ex list here is NSMultableArray
//USer.h
#interface User: NSObject
{}
#property(nonatomic, strong) NSMutableArray *items;
#end
//User.m
#implementation User
-(id)init{
if (self = [super init]){
self.items = [NSMutableArray array];
}
return self;
}
#end
//In other class
#import "User.h"
User *u = [[User alloc] init];
[u.items addObject:#"foo"];
[u.items addObject:#"bar"];
for (NSString *aStr in u.items) {
NSLog (#"%#",aStr)
}
Objective-C is just a language, it does not contain any framework per se. As C# doesn't have List which comes from .NET framework.
First you need to learn the language by reading one of many guides around, from example the official one from Apple here. Then you can read some reference to Foundation, which is the framework with most basic library support (for lists or other generic elements). You can start from here. Take a look at NSString, NSMutableArray and so on.

Getters and setters for constants?

Is is possible to create getters and setters for constants? I want to refer to a constant directly, and have it instantiate itself if it's value is nil. A constant declared like this:
// Prefs.h
extern MyClass * const kThing;
// Prefs.m
MyClass * const kThing = nil;
and the getter/setter would look like:
// getter
+ (MyClass *)kThing
{
_kThing = _kThing ? : [MyClass new];
return _kThing;
}
// setter
+ (void)setKThing:(MyClass *)myClass
{
_kThing = myClass
}
And then I could use it like:
[kThing doSomething];
Is this possible?
edit edited the methods to class methods
What you describe are not constants, they are global variables. You cannot define getters and setters for them, but you can use their values to back class methods, which is precisely what you have done.
However, when you send message like this
[kThing doSomething];
the global variable is used directly, bypassing your getter. If you want to go through a getter, you can write
[[MyClass kThing] doSomething];
or inside methods of MyClass you can write
[[[self class] kThing] doSomething];
Another note is that when yo implement accessor methods like that, you should make the backing variables static, rather than extern. This will ensure that other modules cannot access these variables bypassing your getters.
Global variable declaration in other file is very dangerous in objective C. Ideally we use sharedInstance. Try like this:
In MyGame.h
#interface MyGame : NSObject
{
int mScore;
}
#property(nonatomic,assign) int score;
+(MyGame*)sharedObject;
-(void)someFunction;
#end
In MyGame.m
static MyGame *gGame = nil;
#implementation MyGame
#synthesize score = mScore;
+(MyGame*)sharedObject
{
if(!gGame)
{
gGame = [[MyGame alloc] init];
}
return gGame;
}
-(void)someFunction
{
}
#end
To access anywhere in project:
#import "MyGame.h"
[MyGame sharedObject].score;
[MyGame sharedObject] someFunction];
The short answer is that this is not possible.
MyClass * const kThing = nil;
means that kThing is a constant pointer, which means that the address in memory that it points to cannot be changed. So once it's set to nil, it can't later be set to a new object.

Clean implementation of architecture, Business Objec, iOS

I have a class in objective C which represents a generic business object ( let's say for my question sake its a bankaccount). I have a java C# background (FYI ). Now this bankAccount has an enum property defining what type it is, plus an NSURl property
//this is in the bankAccount.h file
typedef enum {Checking, Savings, Investor} AccountType;
-(NSURL *)url;
Now users can create a new bankaccount and set the enum to the relevant type. after allocating their new bankaccount object they might need to access the url property, so I have to implement a getter for that property which will initialize it properly. My question here is that how can I know what type of a bankaccount the calling class has created of my bankaccount in order to initialize the url property correctly?
like right now this is how I am implementing url in the bankaccount.m file :
-(NSURL *)url {
if (url != NULL) {
return url;
}
NSString *filePath;
// figure out the file path base on account type
switch (AccountType) {
}
return url;
}
keep in mind that this is in the Bankaccount.m file, which really doesn't know in the calling class what is the instance being created is. Maybe I am confused and maybe its a simple answer but I can't wrap my head around this concept.
Appreciate the help guys.
I think youre forgetting that you can't exactly save information in an enum. Save the value of the enum in some variable.
You do not necessarily have to setup your code like this, but maybe this is more what you are looking for.
// BankAccount.h
#import <Foundation/Foundation.h>
typedef enum {
Checking = 1,
Savings = 2,
Investor = 3
} AccountType;
#interface BankAccount : NSObject
-(void)setAccountType:(AccountType)theType; //Setter
-(NSURL *)url;
#end
// BankAccount.m
#import "BankAccount.h"
#implementation BankAccount
-(void)setAccountType:(AccountType)theType {
self.bankAccountType = theType;
}
-(NSURL *)url {
NSURL *someUrl;
switch (self.bankAccountType) {
case Checking:
someUrl = [NSURL URLWithString:#"http://xzyChecking"];
break;
case Savings:
someUrl = [NSURL URLWithString:#"http://xzySavings"];
break;
case Investor:
someUrl = [NSURL URLWithString:#"http://xzyInvestor"];
break;
default:
someUrl = [NSURL URLWithString:#"http://xzyError"];
break;
}
return someUrl;
}
#end

Resources