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.
Related
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.
I have seen several examples of Objective-C code, where a delegate needs to be defined. For example, when using MapKit, I see statements such as:
[self.mapView.delegate self];
I also sometimes see the following:
[self.mapView setDelegate:self];
And still I find some examples that do the following:
self.mapView.delegate = self;
I understand how the second and third are equivalent, however I do not understand how the first is able to run, let alone compile. What I mean is: how is self a valid selector in this context? How does this code translate to an assignment statement for the delegate property?
self in [self.mapView.delegate self]; and [self.mapView setDelegate:self]; are different — yet related things. while the latter self represents the object in its scope it is used, [object self] is a method -(id)self defined in the NSObject protocol.
from the doc:
self
Returns the receiver. (required)
- (id)self
Return Value The receiver.
Availability Available in OS X v10.0 and later.
As the NSObject class implements the NSObject protocol, nearly any object we use in our codes will understand this method.
A clue, what it useful for, gives us the GNUStep documentation:
self
- (id) self;
Availability: OpenStep
Returns the receiver. In a proxy, this may (but is not required to) return the proxied object.
We can use it for proxies.
Also in KVC it can be useful that there is a method called self, as the operator needs a right key path, but actually the object itself is what we need:
NSArray *numbers = #[#1, #1, #2 ,#3, #5]
NSNumber* sum = [numbers valueForKeyPath: #"#sum.self"];
sum will be 12.
[self.mapView setDelegate:self]; and self.mapView.delegate = self; are equivalent and self sends for the object it is used in. Basically each Objective-C message translates to a C function, that takes at least two parameters. -setDelegate: would be translation in runtime to
void setDelegate(id self, SEL _cmd, id delegate)
{
// implementation ....
}
As you can see here, self is just the default name of the object passed in as the first parameter by the runtime and refers to the object of the class the method is defined on.
Although it is often referred as a keyword, self isn't. it is just a convention. As it is possible to construct Objective-C methods by using C functions, the Implementation IMP type and selector SEL type, you could decide to call the first object differently, like this if you would like to have C++ naming.
I want to create a subclass of NSMutableArray and need to override the -initWithObjects: method.
But How to call [super xxx];?
- (id) initWithObjects:(id)firstObj, ... {
[super initWithObjects:firstObj]; // Error: Missing sentinel in method dispatch
// Error: The result of a delegate init call must be immediately returned or assigned to "self"
}
Thanks.
Then "missing sentinel" message refers to the missing nil termination. In fact, according to font-of-all-knowledge-Wikipedia:
The name of the nil that terminates a variable length list of parameters in Objective-C
also: Sentinel node, an object to represent the end of a data structure
also: Sentinel value, a value used to terminate a loop
also: In network protocols such as Bisync, sentinel values indicate where frames start and end
You can't. As discussed in the documentation for NSArray:
You might want to implement an initializer for your subclass that is
suited to the backing store that the subclass is managing. The NSArray
class does not have a designated initializer, so your initializer need
only invoke the init method of super. The NSArray class adopts the
NSCopying, NSMutableCopying, and NSCoding protocols; if you want
instances of your own custom subclass created from copying or coding,
override the methods in these protocols.
So you can assign self = [super init]; and add the objects from your initialiser to the resulting object. Indeed, because of the way that NSArray is implemented, calling any -initWith… method is likely to return an instance of a different NSArray subclass.
Notice that the documentation also discusses alternatives to subclassing NSArray that may be easier, more reliable or better in some other way.
Subclassing NSArray/NSMutableArray doesn't work like subclassing most classes. NSArray is a class cluster, please see subclassing notes from the NSArray documentation.
Now, for your specific question, subclassing va_list methods is a bit tricky, there are a number of ways to handle this. The 'safest' would be to process your va_list into an NSArray and pass that into another method that dealt with whatever you wanted. The other, slightly less portable, slightly hackier way is to create a new va_list list on the stack to pass through.
id __unsafe_unretained * stack = (typeof(stack))calloc(numOfObjects, sizeof(id));
//filloutStack
[super initWithObjects:*stack, nil];
free(stack);
Subclassing Apples Collection classes isn't that difficult — if you use a tiny trick (see also: cocoawithlove).
A subclass is a "is-a" relationship in object-orientated Design. But there are also "has-a" relationships, i.e. wrappers.
If you would try to create a subclass of NSArray by using a pure is-a relationship, I guess, it would be kind of hard, as you would have to do C-level memory management.
But if you add a has-a relationship — or: create a wrapper — at the same time, you can the subcalssing quite easily: Just make your custom array class have a member of a regular NSArray. Now override its method by forwarding the calls to the member object. I showed this in this post, where I just add objects, that pass a certain test.
But you will see, that I didn't implement the method you talked about correctly, but I raise a error. The reason is: that method is a variadic methods, that has a variable number of objects you can pass in — and to handle this, you have to to a bit of work. cocoawithlove has an great article about it.
For you — if using that has-a trick — it could look like
- (id) initWithObjects:(id)firstObj, ... {
if (self = [super init]) {
_realArray = [[NSMutableArray alloc] initWithCapacity:1];
}
va_list args;
va_start(args, firstObj);
for (id obj = firstObj; obj != nil; obj = va_arg(args, id))
{
[self.realArray addObject:obj];
}
va_end(args);
return self;
}
Try
self = [super initWithObjects:firstObj,nil];
I have code similar to this.
MySubclassOfUIView *view = [aUIPickerView viewForRow:4 forComponent:0];
The viewForRow:forComponent method of UIPickerView returns a UIView. MySubclassOfUIView is exactly that: a subclass of UIView.
The UIPickerView delegate (not mentioned here) uses an array of MySubclassOfUIView objects to populate the rows of the UIPickerView components. Thus, I know the viewForRow:forComponent method is really going to be returning a pointer to an object of type MySubclassOfUIView.
Xcode gives me this warning.
Incompatible pointer types initializing 'MySubclassOfUIView*' with an expression of type 'UIView*'.
So I figure that I'll typecast it to fix the warning, which gives me this code.
MySubclassOfUIView *view = (MySubclassOfUIView*)[aUIPickerView viewForRow:4 forComponent:0];
And the warning goes away.
Please forgive my shaky C and Objective-C skills, but am I doing the right thing (as far as the context given so far)? Is there some other better way to handle this situation?
If you are absolutely sure that it will return a MySubclassOfUIView, then it is OK to do this. If there is any chance that it could return something else (such as you made a mistake and added the wrong thing to the array), then you should check the type and use a temporary variable.
UIView *temp = [aUIPickerView viewForRow:4 forComponent:0];
NSAssert([temp isMemberOfClass:[MySubclassOfUIView class]],[NSString stringWIthFormat:#"aUIPickerView returned the wrong class (%#)",[temp class]]);
MySubclassOfUIView *theView = (MySubclassOfUIView*)temp;
What you can do is:
MySubclass* subFoo = [[MySubclass alloc] init];
MySuperclass* superFoo = subFoo;
What you shouldn't do is:
MySuperclass* superFoo = [[MySuperclass alloc] init];
MySubclass* subFoo = superFoo;
This is, because your Subclass will have all properties, selectors, etc from the Superclass. But the Superclass won't have all (..) of the Subclass.
For the rest, see ughoavgfhw's answer.
I came across a strange problem today. I created a subclass of UIView and added only 1 method to the template code provided by xcode.
#interface FloatView : UIView {
}
- (void)floatTest:(CGFloat)x;
#end
- (void)floatTest:(CGFloat)x {
NSLog(#"float was %f", x);
}
Then in my appDelegate I had code like this:
UIView *floatView = [[FloatView alloc] init];
[floatView floatTest:10.0f];
Pretty simple, right? What should this print out? I thought it would something like "10.0000", but no, it prints out "0.000000".
I wrestled with this for hours, trying to figure out what I was doing wrong, and then I changed the code in my appDelegate to
FloatView *floatView = [[FloatView alloc] init];
[floatView floatTest:10.0f];
Only then, did it print out the expected "10.0000". Why is this so? I've declared FloatView as a subclass of UIView, shouldn't I be able to assign a FloatView object to a UIView pointer without problems?
Even though floatView was declared a pointer to a UIView, it's really a floatView and it should be able to handle the floatTest message? Am I totally off base here?
Actually, polymorphism is working as expected. If it didn't work, nothing would have been printed (in your example, 0.0000 is being printed). The thing is, while your instance actually responds to testFloat:10.0f message, since the compiler can't statically see the method declaration (as UIView class doesn't declare such a method), it assumes that your method takes ... as argument and returns id.
When CGFloat is passed to a method that expects variable number of arguments (...), it's promoted to double. Thus, the receiving method is passed a double argument and thinks it's a float and it doesn't get printed correctly.
You can verify this behavior by changing NSLog line to:
NSLog(#"%f", *(double*)&x);
When the compiler sends the message to FloatView* rather than a UIView*, it can find the exact signature of the method. It can see it really expects CGFloat and doesn't promote the argument to double. As a result, it works correctly.
Additionally, if UIView* contained the method declaration that took a CGFloat, the compiler would call the method appropriately. To summarize, this is not a polymorphism issue; it's a missing method signature issue.