Why can self be used in implementation methods, or in class methods? What's the internal structure of self? eg:
#import "Person.h"
#implementation Person
+ (void)initialize
{
[super initialize];
[self setupModel];
}
- (instancetype)init
{
self = [super init];
if (self) {
[self test];
}
return self;
}
+ (void)setupModel
{
NSLog(#"setup a person object");
}
- (void)test
{
NSLog(#"this method is called");
}
#end
Code run results:
2018-09-17 09:32:40.615578+0800 Test[662:301213] setup a person object
2018-09-17 09:32:40.615662+0800 Test[662:301213] this method is called
Code run results
For instance methods, self is a reference to the current object.
For class methods, self is a reference to the current class.
If you declare an Objective-C method like this
- (void)doSomethingWithObject:(id)object { ... }
the compiler basically just turns it into a a C function with the following signature:
void _i_classname_doSomethingWithObject_(const id self, const SEL _cmd, id object) { ... }
(where classname is the name of the class the method belongs to. for class methods, the i becomes a c)
When you call the method [foo doSomethingWithObject:bar], the compiler turns that method call into a call to objc_msgSend, which "forwards" the call to the correct implementation:
objc_msgSend(foo, #selector(doSomethingWithObject:), bar);
As you can see, the parameters passed to objc_megSend are the same expected by the C function containing the implementation of the Objective-C method.
These are implementation details, so you shouldn't rely on them:
self is either a tagged pointer or a genuine pointer to an struct that starts with the isa field and then contains all of the object's instance variables.
The isa field is itself either a set of bitfields that contains the instance's retain contain, some flags, and a pointer to the Class of the instance, or (on older and 32-bit platforms) it is just a pointer to the Class of the instance (in which case the retain count is stored in a global hash table).
Related
Working on a legacy hybrid iOS project. Created one new Swift util class in ConsentManager.swift, like below,
import Foundation
public class ConsentManager: NSObject {
#objc static let sharedInstance = ConsentManager()
#objc private override init() {}
#objc public func isDataPermissionConsentRequired() -> Bool
{
…
return value; // based on logic
}
}
Called the method from another objc class, ConsentChecker.m like,
#interface ConsentChecker ()
{
}
#end
#implementation ConsentChecker
-(void)checkConsent {
// GETTING ERROR IN THE FOLLOWING LINE
if (ConsentManager.sharedInstance.isDataPermissionConsentRequired()) {
…
}
}
#end
Getting compiler error:
Called object type 'BOOL' (aka 'bool') is not a function or function pointer
Why and how to resolve it?
The reason you're hitting this is that methods in Objective-C which take no arguments may be called implicitly using dot syntax similar to Swift's, but not exactly like it. A method declared like
// Inside of SomeClass
- (BOOL)someMethod { /* return something */ }
can be called either as
SomeClass *instance = ...
// Traditional Obj-C syntax:
BOOL value = [instance someMethod];
or
// Dot syntax accessor:
BOOL value = instance.someMethod;
Note that the dot syntax version does not use parentheses to denote the call. When you add parentheses like you would in Swift, Obj-C determines that you are trying to call the returned value from the method as if it were a function:
instance.someMethod();
// equivalent to:
BOOL value = [instance someMethod];
value(); // <- Called object type 'BOOL' (aka 'bool') is not a function or function pointer
You cannot call a BOOL like you can a function, hence the error.
#Dávid offers the more traditional Obj-C syntax for calling this method, but alternatively, you can simply drop the parentheses from your call:
if (ConsentManager.sharedInstance.isDataPermissionConsentRequired) {
Objective-C-ism note:
Dot syntax is most idiomatically used for method calls which appear like properties (e.g. boolean accessors like your isDataPermissionConsentRequired), even if the method might need to do a little bit of work to return that value (think: computed properties in Swift).
For methods which perform an action, or which return a value but might require a significant amount of work, traditional method call syntax is typically preferred:
// Prefer:
[instance doTheThing];
NSInteger result = [instance performSomeExpensiveCalculation];
// over:
instance.doTheThing;
NSInteger result = instance.performSomeExpensiveCalculation;
The Obj-C syntax for executing methods is different from Swift's dot syntax.
This is the correct syntax:
if ([ConsentManager.sharedInstance isDataPermissionConsentRequired]) {
If u want to call swift function on obj-c class you use to obj-c syntax
Correct Syntax is:
if ([ConsentManager.sharedInstance isDataPermissionConsentRequired]) {
// Write logic here
}
Hello everyone i am new to objective c. The following code is not mine. I am just trying to understand how it works. I have a ViewController that has this property in the .h file.
#property (nullable, nonatomic, copy) dispatch_block_t logHandler;
Inside the .m file the logHandler is called when a button is pressed with the following code.
- (IBAction)login:(id)sender {
if (nil != self.logHandler) {
self.logHandler();
}
}
Then the logHandler is called which exists in another class NSObject file
inside the .h file
#interface LogFlow : NSObject<TheFlowController>
#end
and in .m file
- (UIViewController *)rootViewController {
LogViewController *viewController = LogViewController.newInstance;
viewController.logHandler = ^{
UIViewController *logController = [self startNewLogFlow];
[self.navigationController pushViewController:logController animated:YES];
};
return viewController;
}
I do not understand why the logHandler exists in another class and why it is called from this specific class, and how is it possible to call this code from another class without any import used? I am trying to understand when to use this kind of implementation and how to use it. Any help appreciated.
The construct that you see in the rootViewController:
^{
UIViewController *logController = [self startNewLogFlow];
[self.navigationController pushViewController:logController animated:YES];
};
This is what's known as a "Block" in Objective-C. You may find other references to it in other languages as an "anonymous function" or a "closure". Those names apply here as well.
This creates an object that is just a function, but the function doesn't have a name. You can also assign the unnamed function to variables and call it from the variable - which is what happens here. The anonymous function, the block, is assigned to the logHandler instance variable of the viewController object. Later some other code can call that function through the variable as you see in your login: example.
Here's a simpler block that is just plain Objective-C:
int squareFunction(int x) {
return x * x;
}
void playWithSquares(void);
void playWithSquares(void) {
int nine = squareFunction(3);
int alsoNine = (^(int x){
return x * x;
})(3);
}
The declaration of squareFunction creates a named function that calculates the square of two integers.
You also see the the expression:
^(int x){
return x * x;
};
This also creates a function that calculates the square of an integer, but it doesn't bind that function to a name. Since it has no name we call the function immediately by wrapping it in parenthesis and then passing it arguments (<anonymous function expression>)(3)
We could store the anonymous function in a variable:
typedef int (^SquaresBlock)(int);
SquaresBlock myBlock = ^(int x){
return x * x;
};
and then call it later using squaresBlock(3)
Blocks are very important in Cocoa's use of Objective-C so you should learn more about them.
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Blocks/Articles/00_Introduction.html
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
...
I have a simple School class which defines a init method:
#implementation School
- (id) init {
self = [super init];
if (self) {
// call class method of MyHelper class
if ([MyHelper isWeekend]) {
[MyHelper doSomething];
}
}
}
#end
(MyHelper is a class contains only class methods, the isWeekend is a class method returns a boolean value)
I use OCMock to unit test this simple init method:
- (void)testInit {
// mock a school instance
id schoolMock = [OCMockObject partialMockForObject:[[School alloc] init]];
// mock class MyHelper
id MyHelperMock = OCMStrictClassMock([MyHelper class]);
// stub class method 'isWeekend()' to return true
OCMExpect([MyHelperMock isWeekend]).andReturn(true);
// run init method
[schoolMock init];
// verify
OCMVerify([MyHelperMock isWeekend]);
}
But when run it, I get error:
OCMockObject(MyHelper): Method isWeekend was not invoked. why?
You've created a mock for the MyHelper class, but this isn't going to be used within the implementation of your School object. You'd only get the mocked response if you wrote [MyHelperMock isWeekend], which you can't do inside the initialiser without rewriting it for tests.
To make your School class more testable you should be passing in any dependencies on initialisation. For example, you could pass in the isWeekend value as part of the initialiser, instead of obtaining it inside the method, or pass in the class object (MyHelper or MyHelperMock).
It's worth noting that finding certain classes or methods difficult to test because of things like this is often a good indicator that your code isn't structured very well.
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.