I was hoping someone could help me understand the syntax of blocks when used as members of a class. I have some code that's actually working just fine:
#class Structure;
typedef void (^StructureDeleteCallback)(Structure *);
#interface StructureListDelegate : NRFCTableDelegate
{
StructureDeleteCallback _structureDeleteCallback;
}
#property (nonatomic, copy) StructureDeleteCallback structureDeleteCallback;
#end
This works, but I would like to understand the syntax of the typedef statement; and whether or not it's actually required to use typedef.
What I've read says that using typedef in this situation is recommended because it makes the code a lot clearer; but I've been unable to get it to compile at all when trying to do this without typedef. My understanding of typedef was that the syntax was basically:
typedef [actual type] [new name for type];
Such as:
typedef double CLLocationDegrees;
But the syntax of my typedef statement doesn't match this. So my questions are:
How can the syntax of my typedef
statement be so different from other
typedef statements / what does the syntax I'm using actually mean to the compiler?
Is it possible to
have a block as a member of a class
without using typedef?
I myself have asked a question along the lines of yours here: Block references as instance vars in Objective-C
See my answers here and here.
Related
I'm generating bridges from cpp to swift using cpp-objcpp-objc chain and I need to generate NS_OPTIONS.
In ObjC I have it as follows:
#import <Foundation/Foundation.h>
typedef NS_OPTIONS(NSInteger, PhonebookPhoneOptions)
{
PhonebookPhoneOptionsOpt1 = 1 << 0,
PhonebookPhoneOptionsOpt2 = 1 << 1,
PhonebookPhoneOptionsOpt3 = 1 << 2,
PhonebookPhoneOptionsOpt4 = 1 << 3,
};
It is forward-declared in my another header
#import <Foundation/Foundation.h>
typedef NS_OPTIONS(NSInteger, PhonebookPhoneOptions);
//typedef NS_ENUM(NSInteger, PhonebookPhoneOptions);
#protocol PhonebookDataRefreshedCallback
- (void)onEvent:(PhonebookPhoneOptions)param;
When I use NS_OPTIONS forward declaration I'm getting following error:
With NS_ENUM forward it is as folows:
subprojects/controller/service-sbis-phonebook/phonebook/djinni/objc/phonebook/PhonebookDataRefreshedCallback.h:5:53:
error: unnamed enumeration must be a definition typedef
NS_OPTIONS(NSInteger, PhonebookPhoneOptions);
subprojects/controller/service-sbis-phonebook/phonebook/djinni/objc/phonebook/PhonebookDataRefreshedCallback.h:5:9:
error: declaration does not declare anything
[-Werror,-Wmissing-declarations] typedef NS_OPTIONS(NSInteger,
PhonebookPhoneOptions);
With NS_ENUM it is as follows:
subprojects/controller/service-sbis-phonebook/phonebook/djinni/objc/phonebook/PhonebookPhoneOptions.h:6:31:
error: typedef redefinition with different types ('NSInteger' (aka
'long') vs 'enum PhonebookPhoneOptions') typedef NS_OPTIONS(NSInteger,
PhonebookPhoneOptions)
subprojects/controller/service-sbis-phonebook/phonebook/djinni/objc/phonebook/PhonebookDataRefreshedCallback.h:5:28:
note: previous definition is here typedef NS_ENUM(NSInteger,
PhonebookPhoneOptions);
When it was used solely as structure field I got some combination of NS_ENUM\NS_OPTIONS in ObjC\ObjC++ that was working, but when I try to use it as a function argument - it doesn't compile in any of the variants. What is the correct way to do such forward declaration?
I don't think there's a way to forward declare these sensibly. Can't you simply pack the definition into a header file with no other content so it can be #included from just about anywhere? So, have a header file that contains nothing other than your enum, then include/import that from anything that needs it without pulling in any transitive dependencies.
If your problem is that NS_ENUM/NS_OPTIONS isn't available in pure C++, or rather, that #include <Foundation/Foundation.h> does not work there, try CF_ENUM/CF_OPTIONS instead, which are equivalent and defined in <CoreFoundation/CFAvailability.h>, which can be used from any of the C/C++/ObjC/ObjC++.
If that doesn't help you, please update your question to explain in more detail why you are trying to forward declare your enum type. As in, what specific problem arises as a result of not forward declaring the type? (provide minimal repro code)
I want to use a c-style struct in several classes but I am struggling with its (global) access.
I define the struct in the header file of class “Utilities”:
typedef struct
{
int length;
SInt16 *someData;
} NewStruct;
+ (NewStruct *)initStructWithValue:(int)length;
In another class I initialize this struct with the class method of Utitlities:
#property (nonatomic, assign) NewStruct *newStruct;
_newStruct = [Utilities initStructWithValue: 5]
Now I want to use newStruct in other classes. However, when I put
extern NewStruct *newStruct;
at the top of the header-files it doesn’t work, it seems that I don't have access to newStruct.
I get the error: linker command failed with exit code 1 (use -v to see invocation)
What am I doing wrong and what would be the best way to do it? Or might it be better to avoid using this struct as global variable?
The extern keyword means "declare without defining". In other words, it is a way to explicitly declare a variable, or to force a declaration without a definition. But when the linker assembles your object code it needs the variable the be defined somewhere, which means you have to have a source where the variable is there without the extern keyword NewStruct *newStruct;.
With the method above, you are considering this variable as a global singleton. If you need multiple instances, you should just use the #property as you specified above.
I have a simple Blocks and typedef doubt.
Consider the following codes, I've some typedef and two methods, second one is commented out. My doubt is regarding the first one. Should I use this one? Any issues or something that may arise in future?
typedef void (^MySuccessBlock)(MyServiceResponse *response);
typedef void (^MyFailureBlock)(NSError *errorObject);
#property (nonatomic, copy)MySuccessBlock mySuccessBlock;
#property (nonatomic, copy)MyFailureBlock myFailureBlock;
//SHOULD I USE THIS?
- (void)myServiceWithCompletion:mySuccessBlock
failure:myFailureBlock;
//- (void)myServiceWithCompletion:(void (^)(MyServiceResponse *response))completion
// failure:(void (^)(NSError *errorObject))failure;
In Implementation file:
//If 1st one is used:
- (void)myServiceWithCompletion:aSuccessBlock
failure:aFailureBlock{
self.mySuccessBlock = aSuccessBlock;
self.myFailureBlock = aFailureBlock;
....
}
You should use this:
- (void)myServiceWithCompletion:(MySuccessBlock)successBlock
failure:(MyFailureBlock)failureBlock;
Note the capitals at the beginning of MySuccessBlockand MyFailureBlock as in your typedef.
In fact, when you declare:
- (void)myServiceWithCompletion:mySuccessBlock
failure:myFailureBlock;
the problem comes from the fact that you don't specify the type of your parameters 'mySuccessBlock' and 'myFailureBlock'. So they are treated as default type id because:
If a return or parameter type isn’t explicitly declared, it’s assumed
to be the default type for methods and messages—an id.
It is like if you write:
- (void)myServiceWithCompletion:(id)mySuccessBlock
failure:(id)myFailureBlock;
id type is a pointer, and blocks are pointers, so there is no compile problem. But you can have execution problem because of black's parameters and return value!
The typedef declaration provides a way to create an alias that can be used anywhere in place of a (possibly complex) type name. See
You should use the typedef in the following way.
- (void)myServiceWithCompletion:(MySuccessBlock)aSuccessBlock
failure:(MyFailureBlock)aFailureBlock;
This code gives me the error Cannot find interface declaration for 'OGWView':
typedef SKNode OGWView;
#interface OGWView (Category)
#end
Why? Shouldn't the category work just as well with a typedef name?
PS: I know I can fix this with a #define (or by using the original class name) but I'm really more interested in understanding why it isn't possible to create a category on a typedef class.
I believe the answer to this question is that you have 2 different kinds of symbol. I believe the typedef is an object and you are trying to use it as a class symbol.
depending on the order of declaration you get different warnings suggesting as much:
typedef NSObject Foo;
#class Foo;
yields:
Redefinition of forward class 'Foo' of a typedef name of an object type is ignored
#class Foo;
typedef NSObject Foo;
yields:
Redefinition of 'Foo' as different kind of symbol
Replace typedef with #compatibility_alias
#compatibility_alias SKNode OGWView;
I understand that placing the word extern before a variable declaration in a header file declares the existence of a global static variable without initialising it. I also understand that if I import the file containing the extern variables, I can reference them without a class/file name. But where does one define them and their values?
What I am trying to do is create a class of constants with global constants that I want to use throughout an iOS application's code.
Does one put them inside the interface like this?
Example.h
#import <Foundation/Foundation.h>
#interface Constraints : NSObject
{
extern NSString * const PREFS_NAME;
}
Or does one put then outside of the interface like this
Example.h
#import <Foundation/Foundation.h>
extern NSString * const PREFS_NAME;
#interface Constraints : NSObject
{
}
Then in the implementation .m file how would one initialise the extern values?
Inside the implementation area like this?
Example.m
#import "Constraints.h"
#implementation Constraints
/**PRefecences name for the application**/
const NSString * PREFS_NAME = #"MyApp_Prefs";
#end
Or do initialise them outside of the implementation area like this:
Example.m
#import "Constraints.h"
/**PRefecences name for the application**/
const NSString * PREFS_NAME = #"MyApp_Prefs";
#implementation Constraints
#end
Or do I provide them their initial values in a constructor? or some arbitrary a static style method with + in front of it i.e. +(void) setAppConstraints;
I have tried several combinations, but always run into errors, such as "Redefinition of 'xVariable' with a different type". Or a something about "extern does not have an initialise interface" (or something like that, I forget). So I want to know how to declaire and initialise them properly to form the same role as public static final variables in Java.
Also what are the limits of the extern command? I know I can extern an NSInteger or NSString, but what about NSArray?
I am asking this question because there seems to be to much misleading, or incomplete, information regarding the use of extern in Objective-C. Many of the answers seem speculatory. My hope is for this question to be a good resource not only for me, but to limit further similar questions about the basics of extern.
You define it's value in the file inside which it's declared, which in your case is Example.m; You can still re-assign this variable, so the declaration in Example.h would look like this:
extern NSString * PREFS_NAME;
This way every file that imports Example.h has access to this variable. The equivalent of public static final in Objective-C is const. If you also want it to be public you should make it be a class instance variable, but in this case you don't need it because it's already accessible everywhere. So in this case it would be:
// .m file
NSString* const PREFS_NAME = #"MyApp_Prefs";
// .h file
extern NSString* const PREFS_NAME;
Also notice that const NSString* is different from NSString* const. The latter is a const pointer to NSString. The former hasn't sense even if it's a correct syntax. In Objective-C the const qualifier doesn't affect objects, instead there are mutable and immutable classes. It would have sense in C++ meaning that you can use just const methods on the instance.
extern is used to signal the compiler that you will be using a variable or a function that is defined in another compilation unit.
When you say extern const NSString *PREFS_NAME, you're saying "Replace all references in this compilation unit to PREFS_NAME to the variable PREFS_NAME as it is defined in another file." So when you try to assign PREFS_NAME in your .m, all you're doing is trying to assign a variable that, though it has a name, it doesn't exist. Declaring a variable extern is only a declaration of a variable or function, not a definition of that variable or function. It lets the compiler know that the name is in use, and that the linker will take care of what to do with it, but even if you provide a type here, it doesn't actually set aside space for the variable, it's expecting the space to be set aside in the compilation unit that's actually defining the variable.
You compile three or four different source code files together, three of them may declare:
extern int buffer[];
And one may declare
int buffer[BUFSIZE];
In its global scope, and the linker's job is to resolve the three declared references to extern buffer to the fourth's actual definition of the buffer.
extern is to C variables and functions much as #class is to Objective-C classes, it's a forward declaration, a promise to the compiler that you don't have to freak out when you see a name that's undefined here, because the linker will answer whatever lingering questions you may have.