I'm learning how to bind objective c to monotouch and I'm having trouble with a property that is a block
#property (nonatomic, copy) void (^onLongPress)(UIView*, NSInteger);
I have this at the moment
delegate void onLongPress (UIView view, int index);
[Export ("onLongPress")]
void onLongPress() { set; }
The documentation on how to bind blocks is there http://docs.xamarin.com/guides/ios/advanced_topics/binding_objective-c_libraries at 3.10
But your code shows a block property, not a function taking a property.
In your case, I would bind it like this:
//ApiDefinition.cs
delegate void OnLongPress (UIView view, int index)
[Export("onLongPress")]
OnLongPress OnLongPress { set;}
This will probably works, but as I've never encountered that particular case, I'm interested in your results.
Related
I am debugging an old project which has got a old library. I am not sure how they have called the block handler from button action method. They have a Class where they have declared a method
+ (void) method: (NSString *) parameter;
In which I guess, they are adding cancel button and action method for the button. They have declared a block as
typedef void (^simpleBlock)(void);
#property (nonatomic, copy) simpleBlock simpleBlock;
And when it is implemented in ViewController class it is called as
[ClassName method:#"parameter"].simpleBlock = ^{
//Do something
};
How they would have linked the block with button action and the method because I couldn't see as it is library, got just header file. Any help on understanding this implementation would be appreciated. Thanks!
For invoking a block on any action we code like this ,
In a TotalLikes_Comments.h ,
#property (nonatomic, copy) void (^LikeButtonTapAction)(TotalLikes_Comments *aCell);
- (IBAction)openLikesClick:(id)sender;
In TotalLikes_Comments.m Actual Implementation of method is
- (IBAction)openLikesClick:(id)sender {
if (self.openLikesTapAction) {
self.openLikesTapAction(self);
}
}
Where whenever the button clicks this block is initializes with the object of class ,
Whenever we want the action no need to write theaction code in the class file just write your code where you have initialized the class eg.in cellforrow atIndexpath
cell.LikeButtonTapAction = ^(TotalLikes_Comments *aCell){
// Do your stuff.
};
Above is the logic for Invoking Block in IBAction.
But In your scenario this is written in Frameworks. In which the the Ibaction Logic is hidden from all. eg.
if(self.openLikesTapAction) {
self.openLikesTapAction(self);
}
Hope you got an idea from this.
I have a C struct with custom allocation/deallocation functions because the struct has a dynamically-allocated nested array:
struct Cell {
int data, moreData;
};
struct Grid {
int nrows, ncols;
struct Cell* array;
};
struct Grid* AllocGrid (int nrows, int ncols) {
struct Grid* ptr = (struct Grid*) malloc (...);
// ...
ptr->array = (struct Cell*) malloc (...);
return ptr;
}
void FreeGrid (struct Grid* ptr) {
free (ptr->array);
free (ptr);
}
I want to use this struct in the UIViewController of my Objective-C app. The grid's lifespan should be the same as the controller's one.
If it were a C++ object, I would call AllocGrid() in the constructor and match it with a call to FreeGrid() in the destructor. So I tried to put the allocation in the init message and the deallocation in dealloc:
#implementation ViewController
{
struct Grid* theGrid;
}
- (id)init {
self = [super init];
if (self) {
NSLog(#"init()");
theGrid = AllocGrid(10,10);
}
return self;
}
- (void)dealloc {
NSLog(#"dealloc()");
DeallocGrid(theGrid);
theGrid = NULL;
}
#end
But the allocation is never executed and I cannot see the "dealloc" log message when running the app in the iOS simulator. I guess I could do the allocation in viewDidLoad but I feel it's not the right thing to do. Hence my question:
Question: How can I wrap the C struct in a #property and force it to use my custom AllocGrid() and DeallocGrid() functions?
Or: Is there an equivalent of a scoped_ptr in Objective-C? Or should I roll out my own?
I think putting the allocation in the viewDidLoad() is correct. In fact, there is a discussion regarding to why init() is not being called in ViewController, iPhone UIViewController init method not being called. But, it depends on your context, if you want to initialize your structure "before" the view appear, you should put your initialization in viewWillAppear. There's another interesting thread talking about the invoking order in ViewController, Order of UIViewController initialization and loading. Finally, I want to point out Objective-C is an extension of C, so the "basic" allocation/free behavior should be the same.
What is the equivalent of the following Objective-C code in Swift?
#property (nonatomic, assign, getter = isOpen) BOOL open;
Specifically, how does one declare a variable in Swift to synthesize the getter with a custom name?
Furthermore, how can you subsequently override the implementation of the getter and setter?
Your assumption was close, but a few things could be changed. I will try to help you get as close as possible to the Objective-C version.
First of all, the nonatomic and assign are irrelevant in swift. That leaves us with
#property (getter = isOpen) BOOL open;
Since properties in swift are just instance variables, the swift translation would be as follows.
var open:Bool
Although this has the same basic functionality as the Objective-C version, it is lacking the named getter (isOpen). Unfortunately, there is no direct translation to swift for this (yet). You could use a custom getter and setter.
var open:Bool {
get {
// custom getter
}
set {
// custom setter
}
}
A rather crude work around would be to make another function literally called isOpen that would act as a getter.
func isOpen() -> Bool { return self.open }
In conclusion, what you are asking is only slightly possible, but hopefully in later releases of swift can become a reality.
var open: Bool {
#objc(isOpen)
get {
// custom getter
}
set {
// custom setter
}
}
Leads to this generated header:
SWIFT_CLASS("_TtC11SwiftToObjC9TestClass")
#interface TestClass : NSObject
#property (nonatomic, getter=isOpen) BOOL open;
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
#end
As a remarq, for the setter you need to repeat the #objc directive:
#objc( setOpen:) set { self.open = newValue }
Don't forguet the semi-column.
A particular thing is that, doing this, self.open will call the setter/getter itself and produce an infinite loop.
In Obj-C you fix it using self->open. How do this if swift?
I have an ill-understanding of block-based callbacks. There seems to be two approaches that I'm aware of and I don't know when I should be using one over the other so could someone please explain to me the differences between the two, correct me and give me some tips if I need any.
Some code I found off stackoverflow as well as a library from elsewhere so thanks to those who wrote this code.
typedef void (^MyClickedIndexBlock)(NSInteger index);
#interface YourInterface : YourSuperClass
#property (nonatomic, strong) MyClickedIndexBlock clickedIndexBlock
.m
//where you have to call the block
if (self.clickedIndexBlock != nil) {self.clickedIndexBlock(buttonIndex)};
// where you want to receive the callback
alert.clickedIndexBlock = ^(NSInteger index){NSLog(#"%d", index);};
my understanding with the above is that:
MyClickedIndexBlock is typedef to a NSInteger. Property created with the name "clickedIndexBlock" which is of type MyClickedIndexBlock (meaning that clickedIndexBlock can be a number).
Blocks can also be used as methods which is why I can call self.clickedIndexBlock(buttonIndex);
BUT something tells me that this approach as a #property only really supports one parameter
eg. NSInteger.
WHEREAS the following approach allows for more than one parameter.
bluetoothMe.h
typedef void (^hardwareStatusBlock)(CBPeripheral *peripheral, BLUETOOTH_STATUS status, NSError *error);
- (void)hardwareResponse:(hardwareStatusBlock)block;
bluetoothMe.m
- (void)hardwareResponse:(hardwareStatusBlock)block {
privateBlock = [block copy];
}
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(#"Did connect to peripheral: %#", peripheral);
privateBlock(peripheral, BLUETOOTH_STATUS_CONNECTED, nil);
NSLog(#"Connected");
[peripheral setDelegate:self];
[peripheral discoverServices:nil];
}
My understanding that creating a property which is strong and doing a [block copy] will retain the block around until the app terminates. So [block copy] and strong both retain. [block copy] is applied to the block to retain otherwise the block would have vanished when the method goes out of scope.
ViewController.m
[instance hardwareResponse:^(CBPeripheral *peripheral, BLUETOOTH_STATUS status, NSError *error) {
if (status == BLUETOOTH_STATUS_CONNECTED)
{
NSLog(#"connected!");
}
else if (status == BLUETOOTH_STATUS_FAIL_TO_CONNECT)
{
NSLog(#"fail to connect!");
}
else
{
NSLog(#"disconnected!");
}
NSLog(#"CBUUID: %#, ERROR: %#", (NSString *)peripheral.UUID, error.localizedDescription);
}];
So lets see what my questions were:
1) When would I choose the first approach over the second approach and vice versa?
2) First example, the block was a typedef to a property. Second example, the block was declared a method. Why couldn't the first example be declared a method and why couldn'tt the second example be typedef to a property?
3) Would I need to create a typedef for every type of delegate method that I want a block-based callback for?
4) At of date, ive only seen one delegate method supported. Could you show me an example on how one would implement each approach if I was to create block-based callbacks on multiple delegate methods which are not similar.
Appreciate your feedback. This is hard at times. Need as much help as I can get.
Thanks,
Ben
The questions
Whether to typedef a block or not,
whether to use a property for a block or not,
whether a block has a single or multiple arguments,
are completely independent (or orthogonal). All combinations are
possible and allowed.
void (^myClickedIndexBlock)(NSInteger index);
declares a block variable myClickedIndexBlock taking an integer argument
and returning void. You can use typedef if the same block type occurs
repeatedly in your program:
// Define MyClickedIndexBlock as *type* of a block taking an integer argument and returning void:
typedef void (^MyClickedIndexBlock)(NSInteger index);
// Declare myClickedIndexBlock as a *variable* of that type:
MyClickedIndexBlock myClickedIndexBlock;
With multiple arguments:
void (^privateBlock)(CBPeripheral *peripheral, BLUETOOTH_STATUS status, NSError *error);
or
typedef void (^hardwareStatusBlock)(CBPeripheral *peripheral, BLUETOOTH_STATUS status, NSError *error);
hardwareStatusBlock privateBlock;
Instead of (instance) variables, you can use properties. In the first example:
#property (nonatomic, copy) void (^myClickedIndexBlock)(NSInteger index);
declares myClickedIndexBlock as a block property, and is equivalent to
typedef void (^MyClickedIndexBlock)(NSInteger index);
#property (nonatomic, copy) MyClickedIndexBlock clickedIndexBlock;
Contrary to your assumption, block properties are not restricted to blocks
with a single argument. You can use a property also in the second example,
with or without typedef:
#property (nonatomic, copy) void (^privateBlock)(CBPeripheral *peripheral, BLUETOOTH_STATUS status, NSError *error);
or
typedef void (^hardwareStatusBlock)(CBPeripheral *peripheral, BLUETOOTH_STATUS status, NSError *error);
#property (nonatomic, copy) privateBlock;
It is your choice whether to use instance variables or properties for blocks.
I would use properties (with the "copy" attribute).
Whether to typedef or not is purely a matter of taste. It helps to avoid
errors if the same block type occurs repeatedly in your program. On the other
hand, the Xcode autocompletion seems to work better without typedef (in my
experience).
I strongly suggest you read the Blocks Programming Guide.
Blocks are not methods. I'm not going to paraphrase what's said in the Conceptual Overview, but just quote some parts:
Blocks represent typically small, self-contained pieces of code. [...]
They allow you to write code at the point of invocation that is executed later in the context of the method implementation.
It seems you're confused by the syntax.
typedef void (^MyClickedIndexBlock)(NSInteger index);
It's basically just defining a type named MyClickedIndexBlock representing a block that takes a single parameter of type NSInteger and returns nothing (void).
It's not a typedef to a NSInteger.
#property (nonatomic, strong) MyClickedIndexBlock clickedIndexBlock
is a declaration of a property that will contain a MyClickedIndexBlock.
It's not required to typedef blocks, it would be perfectly valid to write
#property (nonatomic, strong) void(^clickedIndexBlock)(NSInteger index);
But for the sake of clarity (or reuse), you may choose to typedef them. Notice that the property name is what follows the ^.
You're stating that blocks can be used as methods because it's possible to call self.clickedIndexBlock(buttonIndex) in your example. But in fact, it's because you've declared a property named clickedIndexBlock that you can call it like that.
There's a lot in your question, but a large part is due to confusion and misunderstanding. The 2 approaches you mention aren't really different. Blocks are objects and can be manipulated as parameters, local variables or ivars / properties just as you would do with NSString or other kinds of objects.
1) The block isn't typedef'd to an integer. It's typedef'd to return void and has an integer parameter. There is no advantage to ethos 1 or method 2; they both can have multiple parameters if declared.
2) No reason why that format has been chosen for either case. They both achieve the same result, but the first one is arguably better semantically.
3) No. You can declare blocks inline to a method. Look at the header for [NSArray enumerateObjectsUsingBlock:] for an example of inline block declaration.
4) You can just create multiple properties and call each distinct block when necessary.
I have the following protocol to a native library for which I'm attempting to write a MonoTouch binding for (it also has an interface part called Brsp).
#protocol BrspDelegate <NSObject>
#required
- (void)brsp:(Brsp*)brsp OpenStatusChanged:(BOOL)isOpen;
- (void)brsp:(Brsp*)brsp SendingStatusChanged:(BOOL)isSending;
#optional
- (void)brspDataReceived:(Brsp*)brsp;
- (void)brsp:(Brsp*)brsp ErrorReceived:(NSError*)error;
- (void)brspModeChanged:(Brsp*)brsp BRSPMode:(BrspMode)mode;
#end
MonoTouch Binding:
[BaseType(typeof(NSObject))]
[Model]
interface BrspDelegate
{
[Abstract]
[Export("brsp:OpenStatusChanged:"), EventArgs("OpenStatus")]
void OpenStatusChanged(Brsp brsp, bool isOpen);
[Abstract]
[Export("brsp:SendingStatusChanged:"), EventArgs("SendingStatus")]
void SendingStatusChanged(Brsp brsp, bool isSending);
[Export("brspDataReceived:")]
void DataReceived(Brsp brsp);
[Export("brsp:ErrorReceived:"), EventArgs("Error")]
void ErrorReceived(Brsp brsp, NSError error);
[Export("brspModeChanged:"), EventArgs("Mode")]
void ModeChanged(Brsp brsp, BrspMode mode);
}
The open status, sending status and error received methods are being called, but the mode changed does not work. So the Delegate is referenced right in the Brsp class, so that part should work. At one point I got the modechanged also working, so I do know that is called when I call "changeMode" on the Brsp class. But then I messed it up and now it does not work anymore...
Also, right now I'm using the Delegate private class, instead of the event based approach. Because when I try to hook up the generated events for one the above methods, the application crashes without any error or warning from MonoTouch.
I suspect something is not 100% right with the binding, so the methods are not glued together right, but I have tried many, many different things and AFAIK this should work...
Any ideas?
Your binding for
- (void)brspModeChanged:(Brsp*)brsp BRSPMode:(BrspMode)mode;
is missing a parameter. Try:
[Export("brspModeChanged:BRSPMode:"), EventArgs("Mode")]
void ModeChanged(Brsp brsp, BrspMode mode);