Objective-C: What does this code mean? - objective-c

In the MyViewController.h file:
#property (nonatomic, copy, nullable, class) void (^saveMetadataSuccess)(MyViewController*const _Nullable myViewController);
In the MyViewController.m file:
void (^saveMetadataSuccess)(MyViewControllerr* const myViewController) = nil;
+ (void)setSaveMetadataSuccess:(void (^)(MyViewController* const))newMetadataSaveSuccess {
saveMetadataSuccess = [newMetadataSaveSuccess copy];
}
+ (void (^)(MyViewController* const))saveMetadataSuccess {
return saveMetadataSuccess;
}
And finally the method which I don't understand:
- (void)success {
dispatch_async(dispatch_get_main_queue(), ^{
MyViewController.saveMetadataSuccess(self);
});
}
From my understanding, saveMetadataSuccess is a getter, but MyViewController.saveMetadataSuccess(self);seems to set something.
Can somebody enlighten me?
Thanks

MyViewController.saveMetadataSuccess is a getter and it returns a block that then being called with a param (self).
So it's like a function that returns other function.
Also you must not just call MyViewController.saveMetadataSuccess(self); because MyViewController.saveMetadataSuccess is nullable and it will crash if MyViewController.saveMetadataSuccess is null.
You have to check MyViewController.saveMetadataSuccess first:
- (void)success {
dispatch_async(dispatch_get_main_queue(), ^{
if (MyViewController.saveMetadataSuccess) {
MyViewController.saveMetadataSuccess(self);
}
});
}

Related

Block mutates a variable that is indirectly in it's scope

This is the most minimal example I could think of:
#import <Foundation/Foundation.h>
#interface ObjCClass:NSObject
#property void (^aBlockInner) ();
-(void)method;
-initWithBlock:(void (^)())aBlock;
#end
#implementation ObjCClass
- (void)method{
self->_aBlockInner();
}
-(instancetype)initWithBlock:(void (^)())aBlock
{
self->_aBlockInner = aBlock;
return self;
}
#end
struct cppClass{
cppClass(){
objCClassInstance = [[ObjCClass alloc] initWithBlock:^{
modifyY();
}];
[objCClassInstance method];
}
void modifyY() {
y++;
}
int y = 0;
ObjCClass* objCClassInstance;
};
int main()
{
cppClass a{};
NSLog(#"Y is:%d",a.y);
}
The member variable y is supposed to stay untouched as blocks are supposed to copy their “captures”. Though, the final print outputs:
Y is:1
Have I misunderstood Objective-C blocks?
To compile on a macOS do: clang++ main.mm -framework Foundation
Much more minimal example would be as follows:
struct MClass {
int i{};
MClass() {
void(^block)() = ^{
++i;
};
block();
}
};
int main() {
MClass var;
NSLog(#"%d", var.i);
}
This snippet has exactly the same "problem", and i member variable gets changed within a block. This happens, because when you refer to member variables from inside member functions, this implicitly adds this pointer to the expression, so it's not actually i member variable that gets captured, but this pointer which encompasses it. The block above can be equivalently rewritten like this:
void(^block)() = ^{
++this->i;
};
The block makes a copy of the pointer to the same object, not object itself. However, if you had the owning object referred to by value, and not a pointer, you would not be able in fact, alter the object, because the copies a block makes are constant:
struct MClass {
int i{};
};
int main() {
MClass var;
void(^block)() = ^{
// Compile-time error: Read-only variable is not assignable
++var.i;
};
block();
NSLog(#"%d", var.i);
}
And this can only be done with use of the __block modifier:
__block MClass var;
void(^block)() = ^{
++var.i;
};

Arc Semantic Issue: No visible #interface for 'CSRGaia' declares the selector 'getUserEQ:'

I'm working on a react-native project that requires some native modules. One of them is a Bluetooth module that allows me to access some CSRGaia methods. Ultimately, I want to be able to read the eq values on the PS-key so that I can set my equalizer to the corresponding values. I know almost nothing about Objective-C
Currently there is a method that looks like this:
RCT_EXPORT_METHOD(setEQValues:(NSArray *)values callback:(RCTResponseSenderBlock)callback)
{
CSRPeripheral *connectedPeripheral = [CSRConnectionManager sharedInstance].connectedPeripheral;
if( connectedPeripheral == nil )
{
callback(#[DISCONNECTED]);
return;
}
[[CSRGaia sharedInstance] setEQValues:values];
}
This works with no issues. However, when I tried to write my own
RCT_EXPORT_METHOD(getUserEQ: (NSArray *)values callback:(RCTResponseSenderBlock)callback)
{
CSRPeripheral *connectedPeripheral = [CSRConnectionManager sharedInstance].connectedPeripheral;
if( connectedPeripheral == nil)
{
callback(#[DISCONNECTED]);
return;
}
[[CSRGaia sharedInstance] getUserEQ: values];
}
I get the following error:
No visible #interface for 'CSRGaia' declares the selector 'getUserEQ:'
I double checked the CSRGaia.m file to verify that both methods exist.
- (void)setEQValues:(NSArray *)values {
NSMutableData *payload = [[NSMutableData alloc] init];
for( NSNumber *value in values ) {
uint8_t hex = [value unsignedCharValue];
[payload appendBytes:&hex length:1];
}
[self sendCommand:GaiaCommand_SET_HEP_EQ_PSKEY
vendor:CSR_GAIA_VENDOR_ID
data:payload];
}
- (void)getUserEQ {
[self sendCommand:GaiaCommand_GetUserEQControl
vendor:CSR_GAIA_VENDOR_ID
data:nil];
}
you are calling this method:
'getUserEQ:'
notice the 2 dots colon
it's different from method
'getUser'
with no colon
and in your .m file there is only
- (void)getUserEQ {}
i guess you wanted to use the setter method, instead
- (void)setEQValues:(NSArray *)values{}
like this:
[[CSRGaia sharedInstance] setEQValues: values];
add anyway both
- (void)getUserEQ;
- (void)setEQValues:(NSArray *)values;
in CSRGaia.h file
between
#interface OSRGaia
and
#end

Swift call Objective-C wrapper function containing blocks

I have an Objective-C wrapper (ObjCWrapper.h and ObjCWrapper.m) with prototype
+ (void) login:(NSString *)username andPassword:(NSString *)password andErrorBlock:(SuccessBlock)errorBlock andSuccessBlock:(SuccessBlock)successBlock;
With typedef
typedef void (^SuccessBlock)(NSString *);
and implementation
+ (void)login:(NSString *)username andPassword:(NSString *)password andErrorBlock:(SuccessBlock)errorBlock andSuccessBlock:(SuccessBlock)successBlock
{
// do stuff like
successBlock(#"test");
}
From my swift view controller (ViewController.swift), I call the login function:
ObjCWrapper.login("abc", andPassword: "abc",
andErrorBlock:
{
(error:String) -> Void in
println();
},
andSuccessBlock:
{
(map:String) -> Void in
println();
}
)
But I get error:
Cannot invoke 'login' with an argument list of type '(String, andPassword: String, andErrorBlock:(String)->void, andSuccessBlock:(String)->void)'
Searching in google says that I am passing some invalid types in the arguments, but I can't find anything wrong in the code. Removing the blocks from the function makes the code work, so I guess it is something related on the way of calling a block function.
Thanks for the help!
It might be worth adding nullability specifiers to your completion block:
typedef void (^SuccessBlock)( NSString * _Nonnull );
And to the method itself:
+ (void) login:(nonnull NSString *)username andPassword:(nonnull NSString *)password andErrorBlock:(nullable SuccessBlock)errorBlock andSuccessBlock:(nullable SuccessBlock)successBlock;
Then you should be able to call your method in Swift:
ObjCWrapper.login("login", andPassword: "pass", andErrorBlock: { (error:String) -> Void in
//error handling
}) { (map:String) -> Void in
//other stuff
}
Obj-C NSString != Swift String
With other word, you pass a String where a NSString is expected.
Casting it down should solve that.
This is what I ended up doing
let username = "username" //usernameField.text
let password = "password" //passwordField.text
ObjCWrapper.login(username, andPassword: password,
andErrorBlock:
{
(map) -> Void in
// stuff
})
{
(map) -> Void in
// stuff
}

Objective-C passing parameters in void method

How would I go about passing parameters when calling a void method? I understand that you can do something like this:
-(void)viewDidLoad {
[self callMethod];
}
-(void)callMethod {
//stuff here
}
But how would I pass a parameter, such as an NSString, to the callMethod method?
Here is an example with an integer parameter.
-(void)viewDidLoad {
[self callMethodWithCount:10];
}
-(void)callMethodWithCount:(NSInteger)count {
//stuff here
}
In objective-c the parameters are included within the method name. You can add multiple parameters like this:
-(void)viewDidLoad {
[self callMethodWithCount:10 animated:YES];
}
-(void)callMethodWithCount:(NSInteger)count animated:(BOOL)animate{
//stuff here
}
It seems you may be misunderstanding what the void in the beginning of the method means. It's the return value. For a void method, nothing is returned from calling the method. If you wanted to return a value from your method you would do it like this:
-(void)viewDidLoad {
int myInt = [self callMethodWithCount:10 animated:YES];
}
-(int)callMethodWithCount:(NSInteger)count animated:(BOOL)animate{
return 10;
}
You define your method to return an int (in this example it always returns 10.) Then you can set an integer to the value returned by calling the method.
- (void)callMethod:(NSString *)string
{
}
Where string is your parameter so you would call
NSString *myString = #"your string here......";
[self callMethod:myString];

EXC_BAD_ACCESS with NSDate in a C++ file, why?

So, I have to add a functionality to an old .cpp file.It's huge. So rewriting it in Objective C is not an option. Instead, I have added the necessary functionality using Objective-C (because I need a lot of the NSDate/NSDateFormatter functions). It worked fine. BUT, when calling the getter (on my view controller) I get this error: EXC_BAD_ACCESS.
Here is a fragment of the code:
//.h file -----------------
// C/C++ headers
#import <Foundation/NSDate.h>
#import <Foundation/NSDateFormatter.h>
namespace MySpace {
class Session {
private:
// C++ stuff
NSDate * startTime;
public:
// C++ stuff
NSDate * getStartTime();
Session(NSDate * startTime );
};
}
// .cpp file -----------------
using namespace MySpace;
Session:Session (NSDate * startTime) {
// unrelated code
if (startTime == nil ){
startTime = [NSDate date];
}
setStartTime( startTime);
// unrelated code
}
void Session::setStartTime( NSDate * startTime){
this->startTime = [startTime copy];
}
NSDate * Session::getStartTime() {
return this->startTime; // here I get the EXC_BAD_ACCESS
}
The entire project is compiled as Objective-C++ and ARC enabled. I believe this issue is caused because the member 'startTime' is released by ARC, and, when I call the getter, it points to nil?
How may I solve this problem?
Thanks.
Try that:
NSDate * Session::getStartTime() {
if (this == NULL) return nil;
return this->startTime; // here I get the EXC_BAD_ACCESS
}
The change makes getStartTime immune to a NULL this pointer.
Does that helps? If so, somewhere, you are using a dangling Session* pointer.
Step 2
Not that. That then:
#interface MyNSDate: NSDate
#end
#implementation MyNSDate
- (id) init
{
self = [super init];
if ( self == nil ) return nil;
NSLog( #"MyNSDate %# initialized", self );
return self;
}
- (void) dealloc
{
// ARC: no super call
NSLog( #"MyNSDate %# deallocated", self );
}
#end
And replace the NSDate* in your class with MyNSDate. Check the messages, breakpoint in dealloc... You should be able to find out when the date is deallocated, appropriate or not, or rules out that hypothesis.
The other idea that crossed my mind is the missing copy construcoperators. If you are copying your Session between ARC and non-ARC compilation units, it may break. You shouldn't do that, but hey, it happens.
Session::Session( const Session& rhs )
{
this->startTime = [rhs.startTime copy];
}
Session& Session::operator=( const Session& rhs )
{
if ( this->startTime != rhs.startTime )
{
this->startTime = [rhs.startTime copy];
}
return *this;
}