Why a pointer to no object can be asked to execute a setter? - objective-c

I creating a pointer does not create a Class object.
But it can be send method to execute in another instance method.
I think just a pointer to the object can be asked to execute a method.
Why the "t" can use setter in the method like this image?

This happens because Xcode only checks if declared type of t variable has visible readwrite property named a. It doesn't care if t is actually object or something else at this point. For example, it cannot know if t is object or nil, or anything else in such situation
- (void) method:(One*) t{
t.a = #"Some string";
}
You can call it like this
[two method:nil];
Xcode won't complain. You can even do this
One *t = (One *)[[NSObject alloc] init]; //warning here
t.a = #"Some t"; //Xcode doesn't care, that t is actually NSObject and doesn't respond to setA:; But you get a runtime error
nil in ObjC responds to all selectors - you can call any visible method on nil object and you won't get a warning nor error. Just silently passes by

Related

Send the message objc_msgSend(class,#selector(dealloc)) to release the object, why is it wrong to access the object pointer?

The code is under ARC. When I delete the code NSObject* objc = (NSObject*)object; the program runs fine, but I didn't have access to the pointer objc. When I keep the code NSObject* objc = (NSObject*)object; I am prompted EXC_BAD_ACCESS (code=1, address=0x20). Is the system accessing the objc pointer after the block function body ends?
-(void)resetDeallocMethodWithInstance:(NSObject*)obj
{
Class targetClass = obj.class;
#synchronized (swizzledClasses()) {
NSString *className = NSStringFromClass(obj.class);
if ([swizzledClasses() containsObject:className]) return;
SEL deallocSel = sel_registerName("dealloc");
__block void (*deallocBlock)(__unsafe_unretained id, SEL) = NULL;
id block = ^(__unsafe_unretained id object){
NSObject* objc = (NSObject*)object;
NSUInteger hash = ((NSObject*)object).hash;
[self removeAllTargetWitSuffixKey:[NSString stringWithFormat:#"%lu",(unsigned long)hash]];
if (deallocBlock == NULL) {
struct objc_super superInfo = {
.receiver = object,
.super_class = class_getSuperclass(targetClass)
};
void (*msgSend)(struct objc_super *, SEL) = (__typeof__(msgSend))objc_msgSendSuper;
msgSend(&superInfo, deallocSel);
} else {
deallocBlock(object, deallocSel);
}
};
IMP blockImp = imp_implementationWithBlock(block);
if (!class_addMethod(obj.class, deallocSel, blockImp, "v#:")) {
Method deallocMethod = class_getInstanceMethod(obj.class, deallocSel);
deallocBlock = (__typeof__(deallocBlock))method_getImplementation(deallocMethod);
deallocBlock = (__typeof__(deallocBlock))method_setImplementation(deallocMethod, blockImp);
}
[swizzledClasses() addObject:className];
}
return;
}
enter image description here
Note: This answer is being directly typed in, your code has not been tested, indeed no code has been tested. Therefore that the issues below are causing your issues is being inferred.
There area number of issues with your design:
Swizzling dealloc is not recommended. The dealloc method is called automatically by the system when it is in the process of destroying an object, as such using the partly destroyed object inappropriately (whatever that might be) could lead to issues - as you have found!
You are using ARC under which "an implementation of dealloc, [should] not invoke the superclass’s implementation". However your block does this.
The variable objc is unused. However by default a local variable has the attribute strong so you are creating a strong reference to an object in the process of destruction. Any strong reference made by the block in this way will be released by ARC when the block has finished, this is almost certainly not good as your error indicates.
You appear to be trying to call your removeAllTargetWithSuffixKey: method when a particular object is destroyed (appear as you swizzle [and can only swizzle] the class but are using the hash of a particular object). A better way to do this avoiding swizzling is to use associated objects.
The runtime function objc_setassociatedobject() allows you to attach an object to a particular instance of another object and have that object be destroyed automatically when its host is destroyed (use an objc_AssociationPolicy of OBJC_ASSOCIATION_RETAIN).
Design a class which has an instance property of your required hash value and a dealloc method which calls your removeAllTargetWithSuffixKey: then rather than swizzle the class simply create and associate an instance of your class with the target object.
HTH
Yes, it's accessing the pointer after the method ends. If this is being compiled under ARC, then the objc is a "strong" reference. However, you are fabricating the implementation of the dealloc method, and so are retaining the object when it's already going to be dealloced, so it's too late to have a strong reference to it. Your implementation is going to call super, which should actually deallocate the object, and then afterwards ARC is going to release the objc value, but it's already gone since it's the receiver, i.e. "self" if you were writing a normal dealloc method.
ARC will never retain self in a regular dealloc method, but that is what you are effectively doing. The "object" value is the same pointer, but is explicitly __unsafe_unretained, so you should just use that directly. You can type the block as NSObject* instead of id if that helps, but it shouldn't matter. Or you can make your objc value also __unsafe_unretained so ARC leaves it alone. You don't want ARC touching the "self" value inside the block in any way, since you are going around ARC's back in this case.
Whatever the case, once you are in an object's dealloc method, don't ever retain/release/autorelease the self pointer -- it will end up with crashes. Calling a method from dealloc and passing a reference to self is a no-no, for example. You need to be very careful about that, and understand exactly what ARC is doing if you are playing these types of runtime games.

Initializing a constant that's value takes a completionBlock argument in its initializer

I have a property, that in Objective-C I created like this:
self.myProperty = [[MyClass alloc] initWithCompletionBlock:^(MyClass *object) {
dispatch_async(dispatch_get_main_queue(), ^{
[self doSomethingAfterInitialization];
});
}];
And it worked great. Initialization of the MyClass object could create an indeterminate amount of time, so I passed a completionHandler in to it. When it finished, doSomethingAfterInitalization: would handle business.
Now in Swift, I'm trying to create the same object and assign it to a property, with problems.
The property never will change, so it makes sense to me to create it as a Swift constant.
So I'm trying it like this:
let myProperty = MyClass(completionBlock:{ (MyClass) -> (Void) in dispatch_async(dispatch_get_main_queue(), doSomethingAfterInitialization())})
To me that seemed like a direct translation... but the Swift compiler tells me that's not correct, via the error
Use of instance member 'doSomethingAfterInitialization' on type 'MyViewController'; did you mean to use a value of type 'MyViewController' instead?
Well that didn't help much. So instead I tried changing the call to the doSomethingAfterInitialization function to self. doSomethingAfterInitialization(), in which case I see
Value of type '(NSObject) -> () -> TodayWidgetTableViewController' has no member 'doSomethingAfterInitialization'
Any idea how I can fix this? Obviously my initializer is a little weird in the first place, so I'm wondering if this is something that doesn't really translate at all to Swift.

How the Obj C object's property name may go through several calls and become a selector name?

I have a small piece of code using MapBox framework:
if (tmpItem.itemTyype > 0 && tmpItem.itemTyype <= 12)
marker = [[RMMarker alloc] initWithUIImage:[UIImage imageNamed:[NSString stringWithFormat:#"PoiSmall%d",tmpItem.itemTyype]]];
else
marker = [[RMMarker alloc] initWithUIImage:[UIImage imageNamed:#"PoiSmall"]];
This code leads to an exception:
-[RMQuadTreeNode itemTyype]: unrecognized selector sent to instance 0xe9aaf00
The RMQuadTreeNode class is a MapBox' class and never be used by myself in my code. Note the unrecognized selector name - this is the name of my tmpItem.itemTyype property!
If I replace this code with the following:
if (tmpItem.itemTyype > 0 && tmpItem.itemTyype <= 12)
image = [UIImage imageNamed:[NSString stringWithFormat:#"PoiSmall%d",tmpItem.itemTyype]];
else
image = [UIImage imageNamed:#"PoiSmall"];
marker = [[RMMarker alloc] initWithUIImage:image];
then error is gone. It's not a problem to replace one fragment of code with another one, but I want to know HOW the 'itemTyype' property became a selector name and later was called somethere inside MapBox framework. The property name went thru NSString call, UIImage call, RMMarker call and do-not-know-how-many another calls to reach RMQuadTreeNode. HOW THIS CAN BE?
This knowledge is not necessary for me to solve this particular problem, but it is necessary to know Objective C better.
EDIT. I have to change the question above. Adjusted question is: I want to know HOW the 'itemTyype' property was passed not only to [NSString ...] call but also somethere inside MapBox framework.
In other words, should we see something like
-[UIImage itemTyype]: unrecognized selector sent to instance 0xe9aaf00
or
-[RMMarker itemTyype]: unrecognized selector sent to instance 0xe9aaf00
?
Answer is NO because the [tmpItem itemTyype] is a parameter of
[NSString stringWithFormat:#"PoiSmall%d",tmpItem.itemTyype]
(ok, just rewriting it as)
[NSString stringWithFormat:#"PoiSmall%d",[tmpItem itemTyype]]
but it's not a parameter of [UIImage ...] nor [RMMarker ...] nor something else like [RMQuadTreeNode ...].
So how it was passed thru the chain of all these calls above and reached [RMQuadTreeNode ...] call? How the parameter of [NSString ...] call magically turned into a parameter of [RMQuadTreeNode ...] call?
In Objective-C dot notation for properties is just a 'shortcut' for writing accessor methods. So when you write
object.property
This is equivalent to writing:
[object property]
And
object.property = ?
is equivalent to:
[object setProperty:?]
So, back to your example,
[[RMMarker alloc] initWithUIImage:… ,tmpItem.itemTyype]]];
is the same as:
[[RMMarker alloc] initWithUIImage:[UIImage imageNamed:…,[tmpItem itemTyype]]]];
This is why you're seeing method itemTyype being called.
So object tmpItem is an instance of RMQuadTreeNode, which doesn't declare method itemTyype (or the equivalent property).
These Apple docs give a fuller explanation.
Simple explanation:
If you declare a property setter and getter methods to access this property are generated automatically (different for older Xcode versions) as well as an instance variable for this property. A setter is used to set the value of this variable, a getter is used to get the value, both so KVO can be used as well.
If you declare a property itemType like for example
#property (strong) NSNumber *itemType;
you also get in your class
- (void)setItemType:(NSNumber*)newValue;
- (NSNumber*)itemType;
as well as a variable
NSNumber *_itemType;
When you use the dot notation of Objective-C 2.0 like self.itemType these getter/setter methods are actually called.
This is a short overview, you should definitely invest time and read up on Objective-C like for example this here

Is my understanding of 'self' correct?

I'll provide a simple method and then explain how I see it, if this is incorrect, please let me know and correct me. I feel like I understand 'self' but still doubt my self.
-(NSString *)giveBack {
NSString *string = [NSString stringWithFormat:#"Hi there!"];
return string;
}
-(IBAction)displayIt {
NSString *object = [self giveBack];
[myView setText:object];
}
the "myView" is a UITextView object.
Now as for the 'self'..
I'm basically saying in my -displayIt method that I'm creating a NSString object called 'object' and storing within it a method that returns a string which says "Hi there".
And this method (named 'giveBack') is performed ON the name of my class (whatever I named the project). Is this correct?
No, you are not creating an object called object and then storing a method within it etc. You are creating a variable which can hold a reference to an object and storing within it a reference to an object obtained by calling a method.
[Note: The following assumes you are using automatic memory management (ARC or garbage collection), no mention will be made of reference counts. If you are using manual memoery there is more to consider...]
Adding line numbers to your sample:
1. -(NSString *)giveBack
{
2. NSString *string = [NSString stringWithFormat:#"Hi there!"];
3. return string;
}
4. -(IBAction)displayIt
{
5. NSString *object = [self giveBack];
6. [myView setText:object];
}
Declares giveBack as an instance method of the class, to be invoked it must be called on a particular instance.
The RHS ([NSString stringWithFormat:#"Hi there!"]) calls a class method which creates an object of type NSString and returns a reference, of type NSString *, to that object. The LHS declares a variable (string) which can hold a reference to an NSString object. The assignment (=) stores the reference returned by the RHS into the variable declared by the LHS.
Return the value in string as the result of the method
Declare an instance method called displayIt
RHS: call an instance method (giveBack) on the object instance self - self is a reference to the current object instance when within an instance method (in this case displayIt). LHS: declare a variable, object of type NSString *. Assignment: store the reference to an NSString returned by the method call on the RHS into the variable declared on the LHS.
Call the instance method setText: on the object instance referenced by the variable myView passing it the reference to an NSString found in variable object.
I think, you are generally correct.
But in below mention:
And this method (named 'giveBack') is performed ON the name of my class (whatever I named the project)
I can't understand your meaning.
A class name is just a symbol (that is text for human readers).
Methods of an Objective-C class are indicated by - notation in the beginning of method declaration.
In other words, all method declarations start with - within #implementation CLASS_NAME ... #end block are instance method of CLASS_NAME class.
When we call another instance methods (within a instance method) we use self keyword. Because all Objective C method call must designate target object and, in this case, we are calling ourselves (current CLASS_NAME instance itself). So we use self keyword.
Sorry for my confusing words.. It's harder to explain I thought :-(
you're storing the string returned by 'giveBack', not the method itself. the method is part of the class. 'self' is the instance of the object that you're calling 'giveBack' (and 'displayIt' for that matter) on.

incompatible pointer type

I have this class:
#interface G2Matrix : NSObject
...
- (id) initWithArray:(float *)val;
...
#end
This line below give me a warning saying that the first argument to the method initWithArray has an incompatible pointer type:
float m[16];
...
G2Matrix* matrix = [[[G2Matrix alloc] initWithArray:m] autorelease];
If I change the method name to something like initWithArray1 the warning disappears. I know that some objects in foundation classes have a method with the same name, but I am deriving from NSObject, which doesn't have this method. What gives?
Additional info - I call the same initWithArray method from other init methods in the G2Matrix class, but I don't see the warning there.
At a guess, this is a type problem:
Inside the other init methods, you call [self initWithArray:...]. self is typed as a G2Matrix*. In this context the compiler can fully resolve which imp (C function pointer) will eventually handle the method call, and detect its signature (argument and return types) correctly.
Out in regular code, [G2Matrix alloc] returns an id. In this context the compiler can only tell the method selector, which will be bound to an imp at runtime. It has to guess which initWithArray: you mean, and as you can see from the warning it guesses wrong, since a foundation class has an initWithArray: method with a different signature. Your code does still work, the compiler just can't be certain.
Picking a unique name for the initMethod (initWithFloats: maybe?) is the recommended way to shut the warning up. Other ways are: break it into two lines; or cast the alloc return value to the right class:
G2Matrix *matrix = [G2Matrix alloc];
matrix = [[matrix initWithArray:pointerToFloats] autorelease];
// or
G2Matrix* matrix = [[(G2Matrix *)[G2Matrix alloc] initWithArray:m] autorelease];
Looks a little odd, but allows you to turn the treat-warnings-as-errors compiler flag back on.
#tathagata thats because initWithArray is method defined in NSArray class so you cannot use it unless you subclass NSArray class.
see the documentation on NSArray
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html
PS.
by use the method, i meant Override the existing method for your purpose which is not a good idea you can find the Subclassing Notes in the document.