In the below code I want to declare a private instance variable and initialise it to zero
But when I try to initialise it to zero I receive irrelevant error “; comma expected at the end of the statement”
As an attempt to solve the issue, I tried to find out to which value this variable is initialised:
-(void) startRecursion: (UIView *) uiviews {
if (!uiviews) {
return;
}
NSLog(#"numOfMethodCalls: %d", ++self->numOfMethodCalls);
NSUInteger *countOfSubViews = [uiviews.subviews count];
}
However, the log printed :
2019-11-10 14:08:25.711847+0100 UIEnhancement_00[10065:383388] numOfMethodCalls: 8
Please let me know how to solve this issue
code:
#interface UIEnhancements : NSObject {
NSUInteger *numOfMethodCalls;
}
+(id) initSelf;
Instance variables are automatically initialised to zero in Objective-C.
Your code is printing 8 as you declared your variable as NSUInteger * – a pointer (*) to an NSUInteger. Now on a 64-bit platform an NSUInteger is 8 bytes long.
(Objective-)C defines the operator ++ on a pointer to increment it by the size of whatever type it points at. So numOfMethodCalls was initialised to 0 automatically and then the ++ added 8 to it.
Instance variables are best private, as you state you intended, but you have declared it publicly in the #interface. The best way is to declare it in the implementation:
#implementation UIEnhancements
{
NSUInteger numOfMethodCalls;
}
...
#end
Lastly though you don't show an implementation you declare:
+(id) initSelf;
in your interface. Only methods which are initialisation methods should start with init, they are instance methods, and in modern Objective-C are normally declared to return instancetype (rather than id) which improves compile-time type checks. In summary:
- (instancetype) initSelf;
though if it is your only initialisation method you should call it simply init.
HTH
Related
I'm wondering how Key-Value Coding, to access a property value, works in Objective-C when this property has a custom getter, setter and ivar defined. According to Accessor Search Patterns the runtime will first search for a getter method and fall back to finding an ivar using the reflection string.
According to the search pattern, when neither a getter nor an ivar are found, an exception should be thrown.
However, when I run the following code:
#import <Foundation/Foundation.h>
#interface Class1 : NSObject {
NSInteger prop;
}
#property (getter=customGetter,setter=customSetter:) NSInteger prop;
#end
#implementation Class1
#synthesize prop = customIvar;
#end
int main() {
Class1 *class1;
// Create and give the properties some values with KVC...
class1 = [[Class1 alloc] init];
class1.prop = 9;
NSLog(#"Set value to 9 with direct access");
// Directly access value, should return 9.
NSLog(#"Direct access: %ld", class1.prop);
// Set with setValue:forKey: to 20.
NSLog(#"Set value to 20 with KVC");
[class1 setValue:[NSNumber numberWithInt:20] forKey:#"prop"];
// Directly access value.
NSLog(#"Direct access: %ld", class1.prop);
// Access value using KVC
NSNumber *propVal = [class1 valueForKey:#"prop"];
NSLog(#"ValueForKey access: %d", [propVal intValue]);
}
I get this output:
Set value to 9 with direct access
Direct access: 9
Set value to 20 with KVC
Direct access: 9
ValueForKey access: 20
It seems that I get two different values: values that are set by directly accessing the property are retrieved when reading directly from the property (9). Values that are set using Key-Value Coding are retrieved by using Key-Value Coding (20).
Does anyone know how this works internally? Is this behaviour expected and am I missing something?
Add the following method to your Class1:
- (void) showVars
{
NSLog(#"->prop %ld | ->customIVar %ld", prop1, customIvar);
}
and call it after you alter your property and you will see what is going on.
Read the reference you linked to, what does it say about the search for a getter and setter?
You've found your answer.
Is it well documented? Probably not, Objective-C & Cocoa have no formal semantic description :-(
HTH
When you have one object as a property of another object in Objective-C, does it automatically initialize when you use #synthesize?
does it automatically initialize when you use #synthesize?
Yes, it is initialized to nil (no actual object is allocated, however - this is pointer initialization in the C sense of the word, the init method is not called).
By the way, you don't even have to #synthesize to achieve this behavior - every instance variable, even those which don't have a corresponding #property, are automatically initialized either to nil (in case of objects), NULL (in case of other pointers) or 0 (in case of integers and floating-point numbers) by the Objective-C runtime.
Let's try it:
#interface TypicalObject : NSObject
#property (nonatomic) NSNumber *numberProperty;
#end
#implementation TypicalObject
#synthesize numberProperty;
#end
...
TypicalObject *object = [[TypicalObject alloc] init];
NSLog(#"object.numberProperty = %#", object.numberProperty);
The log statement yields:
object.numberProperty = (null)
So, no, properties do not auto-instantiate. All object instance variables begin as nil, however.
No. The #synthesize does not know how to initialize it. Simple -init?
You can allocate and initialize it in the -init… of the referring object.
You still have to init. Try using lazy initialization:
-(MyPropertyClass*)propertyName {
if(!propertyIvarName) {
_propertyIvarName = [[MyPropertyClass alloc] init];
}
return propertyIvarName;
}
or init the property in viewdidload
I want to have a ivar which is a pointer to the first element of a c array, but I want that ivar to be able to access the other elements in that c array.
#interface myClass : NSObject {
int * _arrayElement;
}
#end
#implementation myClass
-(id)init {
self = [super init];
if (self) {
int A[50];
_arrayElement= &A;
}
return self;
}
#end
Once this method ends the c array goes off the stack and I cant access the other elements by shifting the pointer (_arrayElements + 5 would give me A[5]).
You need to allocate the array on the heap, not on the stack. The arrays and other variables allocated on the stack are reclaimed by the system when they go out of scope, which means that you cannot refer to them outside of the function/block they are declared in, technically this is called undefined behaviour, and in practise usually causes your code to crash. Use malloc. So instead of:
int A[50];
_arrayElement = &A;
simply:
_arrayElement = (int*) malloc(50 * sizeof(int));
Remember to deallocate the memory with free when you are done with it:
free(_arrayElement);
Alternatively you can allocate and deallocate the memory C++ style, using new and delete keywords:
_arrayElement = new int[50];
and, to release the memory:
delete[] _arrayElement;
Note that if you choose to use new and delete you will have to compile you source as Objective-C++ code. To do it with Xcode it should be enough to change the extension of the file from .m to .mm.
Another thing is that if you are OK with using C++ constructs and libs then you can consider using std::vector<int> instead of int *;
I would like to add functions by creating a category for Objective-C Blocks.
__block int (^aBlock)(int) = ^int( int n ){
if( n <= 1 ) return n;
return aBlock( n - 1 ) + aBlock( n - 2 );
};
Instead of just allowing the normal [aBlock copy], [aBlock retain], [aBlock release], [aBlock autorelease]. I could do thing like:
[aBlock mapTo:anArray];
Possible Category
#interface UnknownBlockClass (map)
- (NSArray *)mapTo:(NSArray *)array_;
#end
#pwc is correct in that you can't create a category for a class that you can't see.
However...
WHAT I AM ABOUT TO TELL YOU SHOULD BE USED STRICTLY AS AN EXERCISE IN LEARNING, AND NEVER IN ANY SORT OF PRODUCTION SETTING.
Some runtime introspection reveals some interesting information. There are a number of classes that contain the word "Block". Some of them look promising: __NSStackBlock, __NSMallocBlock, __NSAutoBlock, and NSBlock.
Some more introspection shows that the promising classes inherit from NSBlock
So it looks like any block is going to be some instance or subclass of NSBlock.
You can create a method on an object, like so:
#implementation Foo
- (void) doFoo {
//do something awesome with self, a block
//however, you can't do "self()".
//You'll have to cast it to a block-type variable and use that
}
#end
Then at runtime, you can move that method to the NSBlock class:
Method m = class_getInstanceMethod([Foo class], #selector(doFoo));
IMP doFoo = method_getImplementation(m);
const char *type = method_getTypeEncoding(m);
Class nsblock = NSClassFromString(#"NSBlock");
class_addMethod(nsblock, #selector(doFoo), doFoo, type);
After this, blocks should respond to the doFoo message.
USE AT YOUR OWN RISK, AND ONLY FOR EXPERIMENTING.
A block winds up being an instance of type __NSGlobalBlock__, as seen in the following snippet:
void (^aBlock)(void) = ^(void) {
NSLog(#"Hello world");
};
// prints "type = __NSGlobalBlock__"
NSLog(#"type = %#", [aBlock class]);
In order to create a category of a class, the compiler needs to be able to see the original #interface declaration of the class. I can't find the declaration for __NSGlobalBlock__ and probably for good reason.
This article and this article contain some useful information about the implementation of blocks.
To your original point, why not just make a category of NSArray for your mapTo method? It seems like a better place for that sort of functionality.
Updated
Let's say you can add a category to the Block object. How would you invoke the block from the category's method? To the best of my understanding, the only way to invoke a block is via the () operator (e.g., aBlock()). I don't think there's a way to tell from the Block object the number and types of parameters. So, what arguments would you pass in to the block invocation?
I'm not recommending you do this, but the following works...
#interface NSObject (BlockExtension)
- (void)foo;
#end
#implementation NSObject (BlockExtension)
- (void)foo
{
// not sure how else to determine if self is a Block since neither
// __NSGlobalBlock__ nor any of its superclasses (except NSObject)
// are accessible to the compiler
if ([[[self class] description] isEqual:#"__NSGlobalBlock__"])
{
NSLog(#"foo");
// now what?
// can't call self(), it doesn't compile
// how else can I invoke this block?
}
}
#end
...
void (^aBlock)(void) = ^(void) {
NSLog(#"Hello world");
};
// prints "foo"
[aBlock foo];
Dave DeLong is right, you cannot add a category on a class that you cannot see, but as blocks are subclasses of NSBlock adding:
#interface NSBlock : NSObject
#end
Now you can 'see' NSBlock and add a category on it, e.g.:
#interface NSBlock (map)
- (NSArray *)mapTo:(NSArray *)array;
#end
#implementation NSBlock (map)
- (NSArray *)mapTo:(NSArray *)array
{
...
}
#end
Still probably not the best thing to do in code that is actually used in production...
WRONG: A block winds up being an instance of type __NSGlobalBlock__, as seen in the
following snippet:
int i = 0;
id o = [class self];
void (^aBlock)(void) = ^(void) {
[o setValue:0];
NSLog(#"Hello world %d", i);
};
// prints "type = __NSGlobalBlock__"
// Now it prints __NSStackBlock__
// and when moved into HEAP prints __NSMallocBlock__
NSLog(#"type = %#", [aBlock class]);
It is only OKAY to say that a block winds up being an instance of type "NSGlobalBlock" unless there are no captured variables in the scope, otherwise it will be created in the STACK and when it is copied that will move the block into HEAP and every reference will be retained!
The simple answer is no. A __block variable is a C level object not an Objective C object. You can call [aBlock copy] but this invokes a C function block_copy() not the nsobject copy method. So the __block type is a C type and therefore you can't add categories.
correction:__block is an identifier in the C compiler not a typedef.
I'm not sure if this will achieve what you think it will, infact I'm not even quite sure what it does:
__block int (^aBlock)(int) = ^int( int n ){
if( n <= 1 ) return n;
return fib( n - 1 ) + fib( n - 2 );
};
the __block identifier tells the complier that the variable should be mutable in referencing blocks and should be preserved if any referencing block is copied to the heap. what confuses me about your code is that __block is usually used to wrap a variable, not a block itself.
In my objective-c project, I have a protocol like this:
#protocol MyProtocol
-(id) get:(NSString *) key;
-(void) set:(NSString *) key withValue:(id) value;
-(NSValue *) getSize;
-(void) setSize:(NSValue *) value;
-(NSValue *) getBounds;
-(void) setBounds:(NSValue *) value;
#end
OBJC_EXPORT const NSString *MYPROTOCOL_SIZE;
OBJC_EXPORT const NSString *MYPROTOCOL_BOUNDS;
And basically, those specific methods (getSize, getBounds, setSize, setBounds) are supposed the value that is supposed to be stored in MYPROTOCOL_SIZE and MYPROTOCOL_BOUNDS, respectively.
However, I cannot find an effective way to set those constant strings, by concatenating the results of other methods, because it gives me the error: initializer element is not constant when I try to set them directly. Is there a way I can guarantee that the objects will always be initialized. (e.g. in a classes load method), without having to manually call code when my program runs?
Well first of all, you should learn the naming convention, for accessors you have - (Type); and - (void)set:(Type)value; whereas in your case you did: - (Type)get; and - (void)set:(Type)value;
I advise you to use #property for your size and bounds accessors too.
Now about the "const" in the NSString variable declaration, it doesn't make sense. Const applies to the type on its left and in case it is at the beginning of the line it applies to the token directly on its right. So what you have is a "const NSString" which doesn't make sense because NSString is already immutable, and sending mutating messages to a const object doesn't issue any warning or errors...
What you actually want is "NSString *const" which states that the pointer to your NSString is constant, you can only assign it at initialization and then it doesn't change...
Now about the protocol... Are you sure you want a protocol in your case ? And not an abstract class that would have your 2 NSString as readonly accessors ?