Why NSNumber *a = 0 do not display error - ios

I know the correct way to initial a NSNumber is NSNumber *a = #1;
and when I declare NSNumber *a = 1;, I will got the error
Implicit conversion of int to nsnumber is disallowed with arc
But I don't know why when I declare NSNumber *a = 0; there is no error
In my case, I have write some function in NSNumber category
and then
If the value of NSNumber is #0, I can use the function in category normally
If the value of NSNumber is 0, I can use the function in category, no error happened but when run app, this function will never call

The value 0 is synonymous with nil or NULL, which are valid values for a pointer.
It's a bit of compatibility with C that leads to this inconsistent behavior.
History
In the C language, there is no special symbol to represent an uninitialized pointer. Instead, the value 0 (zero) was chosen to represent such a pointer. To make code more understandable, a preprocessor macro was introduced to represent this value: NULL. Because it is a macro, the C compiler itself never sees the symbol; it only sees a 0 (zero).
This means that 0 (zero) is a special value when assigned to pointers. Even though it is an integer, the compiler accepts the assignment without complaining of a type conversion, implicit or otherwise.
To keep compatibility with C, Objective-C allows assigning a literal 0 to any pointer. It is treated by the compiler as identical to assigning nil.

0 is a null pointer constant. A null pointer constant can be assigned to any pointer variable and sets it to NULL or nil. This was the case in C for the last 45 years at least and is also the case in Objective-C. Same as NSNumber* a = nil.

You can consider 0 as nil or null that can be assign to object but 1 is integer and can't allow to object or non integer.

Objective-C silently ignores method calls on object pointers with value 0 (i.e. nil). That's why nothing happens when you call a method of your NSNumber category pointer which you assigned the value 0.

A nil value is the safest way to initialize an object pointer if you don’t have another value to use, because it’s perfectly acceptable in Objective-C to send a message to nil. If you do send a message to nil, obviously nothing happens.
Note: If you expect a return value from a message sent to nil, the return value will be nil for object return types, 0 for numeric types, and NO for BOOL types. Returned structures have all members initialized to zero.
In the last Apple Doc Working with nil

Related

What has the difference between declaring a variable with var and int do with the Null Safety?

Why does Dart allow this:
void main() {
var A;
A ??= 12;
print(A);
}
The output is 12. But not this:
void main() {
int A;
A ??= 12;
print(A);
}
Here's the error:
lib/project_dart.dart:4:2: Warning: Operand of null-aware operation '??=' has type 'int' which excludes null.
A??= 12;
^
lib/project_dart.dart:4:2: Error: Non-nullable variable 'A' must be assigned before it can be used.
A??= 12;
^
lib/project_dart.dart:5:8: Error: Non-nullable variable 'A' must be assigned before it can be used.
print(A);
^
In this case I have to add the ? after int so it can work but in the previous case it works fine without it the question is WHY?
var tells the compiler "I want you to figure out what type this variable should be" based on the assignment. In this case, there is no assignment to the variable upon declaration, so there is no information for the compiler to use to infer the type. Thus, according to the compiler, the type of A should be dynamic, which could be literally anything, so it's given the default value of null.
int explicitly tells the compiler "I want this variable to be a non-nullable int". Non-nullable variables cannot be given a default value when they are declared without a value, so they must be assigned before they are referenced for the first time, which means you can't do print(A) before A has been definitely given a value.
A ??= 5; is less obvious why it's failing, but think about what it's doing. It checks if A is null, and if it isn't, it assigns it the value of 5. In order to check A, the program needs to reference A, and as we know, A can't be referenced because it's not nullable and hasn't been definitely assigned a value. Additionally, A ??= 5 is redundant because A is non-nullable and therefore can never be null.
Note that a declared variable that hasn't been assigned a value is not going to implicitly contain null. There is no check for whether a variable has been assigned yet, which is why you should be careful when declaring non-nullable variables without immediately assigning them a value. It's easy to find yourself in a race condition where code that will assign the variable may or may not occur before other code that will reference the variable.

int not returning same value from NSString

I have saved value in Singletone as NSString. When I want to convert to int, value is some random number. For example, I am calling NSString *numberCoupons = [Manager sharedInstance].userProfile.numberOfCoupons, po numberCoupons returning normal value: 40.
But problem is in next line, when I want to convert string to value: int coupons = (int)numberCoupons; It is returning some random number, etc. 421414.
What could be the problem?
try int coupons = [numberCoupons integerValue];
numerofCoupons is obviously an NSNumber object which is used to store numbers within Objective-C collection classes (NSArray, NSDictionary, etc) as only objects can be stored in them.
To get the wrapped value out of the object use:
NSInteger coupons = [numberOfCoupons integerValue]
I would recommend redeclaring numberOfCoupons as NSInteger, and not NSNumber, as NSNumber objects are difficult and expensive to manipulate compared to the primitive types they wrap.
If the value needs to go into a collection class then wrap it in an NSNumber object when adding it and unwrap it when removing it.
When you write (int)numberOfCoupons you are asking that the value in the variable numberOfCoupons be cast to the type int.
Now the value in a variable of type NSString * is a reference to an object, that is a memory address. When (Objective-)C casts a reference to an integer type you get back the memory address. This is the “random” value you are seeing.
What you need to do is send a message to the object referenced by the value in your variable requesting that it return an integer value equivalent to itself. NSString has a method intValue for this, so [numberOfCoupons intValue] will do what you wish.
There is a whole family of xValue methods to obtain various integer and floating-point values of different precision/size.
Note: if you have a reference to an NSNumber, rather than an NSString, then exactly the same code will work.
Note 2: if you do have an NSNumber then the cast expression you first tried may return a value which has a completely different magnitude than you might expect for a memory address. This is because some integer values are represented by special tagged addresses which don't actually reference a real object. This is an optimisation you normally would not notice, except when you accidentally cast the reference value to an integer...
HTH

How does Objective-C initialize C struct as property?

Consider below struct:
typedef struct _Index {
NSInteger category;
NSInteger item;
} Index;
If I use this struct as a property:
#property (nonatomic, assign) Index aIndex;
When I access it without any initialization right after a view controller alloc init, LLDB print it as:
(lldb) po vc.aIndex
(category = 0, item = 0)
(lldb) po &_aIndex
0x000000014e2bcf70
I am a little confused, the struct already has valid memory address, even before I want to allocate one. Does Objective-C initialize struct automatically? If it is a NSObject, I have to do alloc init to get a valid object, but for C struct, I get a valid struct even before I tried to initialize it.
Could somebody explains, and is it ok like this, not manually initializing it?
To answer the subquestion, why you cannot assign to a structure component returned from a getter:
(As a motivation this is, because I have read this Q several times.)
A. This has nothing to do with Cbjective-C. It is a behavior stated in the C standard. You can check it for simple C code:
NSMakeSize( 1.0, 2.0 ).width = 3.0; // Error
B. No, it is not an improvement of the compiler. If it would be so, a warning would be the result, not an error. A compiler developer does not have the liberty to decide what an error is. (There are some cases, in which they have the liberty, but this are explicitly mentioned.)
C. The reason for this error is quite easy:
An assignment to the expression
NSMakeSize( 1.0, 2.0 ).width
would be legal, if that expression is a l-value. A . operator's result is an l-value, if the structure is an l-value:
A postfix expression followed by the . operator and an identifier designates a member of a structure or union object. The value is that of the named member,82) and is an lvalue if the first expression is an lvalue.
ISO/IEC 9899:TC3, 6.5.2.3
Therefore it would be assignable, if the expression
NSMakeSize( 1.0, 2.0 )
is an l-value. It is not. The reason is a little bit more complex. To understand that you have to know the links between ., -> and &:
In contrast to ., -> always is an l-value.
A postfix expression followed by the -> operator and an identifier designates a member of a structure or union object. The value is that of the named member of the object to which the first expression points, and is an lvalue. 83)
Therefore - that is what footnote 83 explains – ->, &, and . has a link:
If you can calculate the address of a structure S having a component C with the & operator, the expression (&S)->C is equivalent to S.C. This requires that you can calculate the address of S. But you can never do that with a return value, even it is a simple integer …
int f(void)
{
return 1;
}
f()=5; // Error
… or a pointer …
int *f(void)
{
return NULL;
}
f()=NULL; // Error
You always get the same error: It is not assignable. Because it is a r-value. This is obvious, because it is not clear,
a) whether the way the compiler returns a value, esp. whether he does it in address space.
b) when the time the life time of the returned value is over
Going back to the structure that means that the return value is a r-value. Therefore the result of the . operator on that is a r-value. You are not allowed to assign a value to a r-value.
D. The solution
There is a solution to assign to a "returned structure". One might decide, whether it is good or not. Since -> always is an l-value, you can return a pointer to the structure. Dereferencing this pointer with the -> operator has always an l-value as result, so you can assign a value to it:
// obj.aIndex returns a pointer
obj.aIndex->category = 1;
You do not need #public for that. (What really is a bad idea.)
The semantics of the property are to copy the struct, so it doesn't need to be allocated and initialized like an Objective-C object would. It's given its own space like a primitive type is.
You will need to be careful updating it, as this won't work:
obj.aIndex.category = 1;
Instead you will need to do this:
Index index = obj.aIndex;
index.category = 1;
obj.aIndex = index;
This is because the property getter will return a copy of the struct and not a reference to it (the first snippet is like the second snippet, without the last line that assigns the copy back to the object).
So you might be better off making it a first class object, depending on how it will be used.

2048 casted to BOOL returns 0

Consider this code
NSInteger q = 2048;
BOOL boolQ = q;
NSLog(#"%hhd",boolQ);
After execution boolQ is equal 0. Could someone explain why is this so?
BOOL probably is implemented as char or uint8_t/int8_t, as "hh" prints half of the half of an integer. which typically is a byte.
Converting to char is taking the lowest 8bit of 2048 (=0x800) and gives you 0.
The proper way to convert any integer to a boolean value is:
NSInteger q = some-value;
BOOL b = !!q;
Casting an integer value to a type too small to represent the value being converted is undefined behaviour in C (C11 standard Annex J.2), and therefore also in the part of Objective-C which deals with C-level matters. Since it's undefined behaviour it can represent the result however it wants, expected value or not.
As per 6.3.1.4, any integer can be used as a boolean value without casting, in which case it will show the expected behaviour (0 is 0, everything else is 1), giving rise to the !! idiom suggested by alk; perhaps counterintuitively, you convert the value by not explicitly converting the value (instead, the conversion is correctly handled by the implicit conversion operation inserted by the ! operator).

Type casting, why is it so verbose? Or am I doing something wrong?

I have a NSDecimalNumber and an NSInteger. I want to multiply the former by the latter, so I'll have to convert the integer. This is what I came up with, after some trial and error.
NSDecimalNumber *factor = (NSDecimalNumber*) [NSDecimalNumber numberWithInteger:myInteger];
It feels like I'm really driving the point home:
Hi there, I want an NSDecimalNumber, let's call it factor.
By the way, just so you know, I want that to be an NSDecimalNumber, if that'd be possible.
So could you give me an NSDecimalNumber? Here, I'll give you an integer to use as a source.
Is it supposed to be this verbose? Or am I messing up somewhere?
The (NSDecimalNumber*) type-cast is "necessary" because +numberWithInteger: is an NSNumber method that returns an NSNumber object. However, this is actually going to cause problems, because it's returning an NSNumber object, not an NSDecimalNumber. (How to use NSDecimalNumber?)
To get your integer into a decimal number, simply do this:
NSDecimalNumber *factor = [NSDecimalNumber decimalNumberWithDecimal: [#(myInteger) decimalValue]];
It's still fairly verbose but two things to bear in mind:
Objective-C is very verbose, by design.
NSDecimalNumber in not intended for basic integer arithmetic, it's intended for use with numbers that are typically represented using scientific notation.
The API you call is a NSNumber convenience constructor which returns an NSNumber -- not necessarily an NSDecimalNumber. A convenience constructor does not need to return a type of the class you message, but an instance of the declared return type. Because NSDecimalNumber is a subclass of NSNumber, an explicit downcast is required when assigning an NSNumber to an NSDecimalNumber.
If a library writer intended to specify the expectation you have in mind (to return an instance of the class you have messaged), instancetype would be used for the return type. Unfortunately, it is a rather new keyword and does not exist in all places it possibly could exist. If instancetype had been used, the cast would not be necessary.
Before instancetype existed, a simple id was the convention. With id, no cast is required and no type checking performed when assigning/initializing a variable of an Objective-C type. For example: NSArray * array = [NSString string]; would not be flagged by the compiler if the return type were id, but it would be flagged if the return type were instancetype. NSMutableString * string = [NSMutableString string]; would be flagged by neither, but it would be flagged if +string's return type were declared + (NSString *)string;.

Resources