Swift Inheritance Issue - Cannot access inherited properties - objective-c

I'm running into a strange issue that involves simple inheritance in Swift. I might be doing something totally stupid so if anyone has any advice.. thanks in advance!
I am using the latest XCode 6 GM version 6A313.
Here are the 2 Swift classes that's made Objective-C backward compatible.
#objc
public class ObjectA : NSObject {
var testProp: String!
init(testProp: String) {
self.testProp = testProp
}
}
#objc
public class ObjectB : ObjectA {
var testPropB: String!
init(testProp: String, testPropB: String) {
self.testPropB = testPropB
super.init(testProp: testProp)
}
}
I then initialize and use the object in Objective-C code.
ObjectB *objectB = [[ObjectB alloc] initWithTestProp: #"TestProp" testPropB: #"TestPropB"];
// This executes correctly and has the correct value
NSLog(#"%#", objectB.testPropB);
// I then pass this newly constructed object to another class that's written in Swift
AnotherClass *anotherClass = [[AnotherClass alloc] init];
[anotherClass someMethod:objectB];
Here is the Swift class where when I attempt to access the inherited property, I get an EXC_BAD_ACCESS code 1.
#objc
public class AnotherClass : NSObject {
public func someMethod(objectB: ObjectB) {
// This executes and assigns correctly
let prop = objectB.testProp
// This errors out with EXC_BAD_ACCESS error code 1
// In the debugger, objectB.testPropB actually seem to have the correct value
let propB = objectB.testPropB
}
}

var testProp: String!
There is no reason for this to be an implicitly unwrapped optional. You always initialize it in init. If possible, it should be:
let testProp: String
If it must be mutable after assignment, then it should be:
var testProp: String
Fixing this will probably cause the compiler to shout at you where you're doing something wrong (possibly not in the code you've posted here). With a !, it is legal to fail to initialize this value (you'll just crash later). Without a !, the compiler will better verify that you do initialize this value.
This line:
ObjectB *objectB = [[ObjectB alloc] initWithTestProp: #"TestProp", testPropB: #"TestPropB"];
is not correct ObjC (there's an extra comma). I'll assume that it's a typo. I don't think that can result in a legal C comma-operator that would cause the wrong initializer to run. But maybe... it's at least worth checking if you actually do have a comma in there. (I'm pretty sure testPropB:#"..." isn't a legal expression, so this is a little far-fetched, but it would match your symptom).

So, I ended up creating a dummy project to test my theory in a more controlled environment. The fix is basically to ensure that ObjectA and ObjectB are in 2 separate Swift files. When they are in the same Swift file, the error occurs.
Here is the sample code for anyone interested.
https://github.com/MystKnight/swift-inheritance-test
I'm not sure why this is but for everyone out there, I guess limit your files to one class if you are using inheritance...

Related

Store Objective-C classes in array and use them

Say I have two classes, BulbDevice and FanDevice, both are subclasses of Device and has a method signature like this:
+ (BOOL)isMyId:(NSInteger)someId;
If I wanted to create a class I could test it out:
if ([BulbDevice isMyId:someId]) {
Device *dev = [BulbDevice alloc] initWithId:someId];
}
But what I really want is to create a factory method inside a factory class, with minimum fuss when new device are added:
+ (Device)createDevice:(NSInteger)someId {
// say I have an array registered
NSArray *arr = #[[BulbDevice class], [FanDevice class]];
// Loop through it.
Device *device;
for (Class *c in arr) {
// The idea is kind of like this but I'm not sure how to make it work
if ([c isMyId]) {
device = [[c alloc] init];
}
}
}
The idea is that I only need to update arr in the factory method. So I think it is good to have something like this. But I am not sure how to make it work.
EDIT:
I took out the asterisk, but it won't work:
for (Class c in arr) {
// Now I want to access the isMyId which is a static method,
// but I how do I cast to that class? I mean not an object of the class, but to that class itself.
if ([(Device)c isMyId:]) {
}
}
But I still need a way to access that class method. Error says Used type 'Device' where arithmetic or pointer type is required, and even if it works, I want to access class method, not sending message to an object.
Or shall I store NSString in the array instead? But it is hard to find way to access the class method as well.
If I understand correctly what you are trying to achieve, then your approach seems to be correct.
There is only one thing that needs to be fixed:
for (Class c in arr)
c variable is not a pointer - the asterisk should be removed. Your code works.
The Class type is not an NSObject type, and although it is a bit special it is object-like or object-equivalent, so you are able to send it messages and store it in collections like you're doing.
You don't use the asterisk as #MaxPevsner says, because Class isn't used as a normal pointer-to-object. Think of Class as a special type like id which also doesn't get the * when you use it to reference an object.

Convert Swift 2 closure to Objective-C block

I'm trying to build an Objective-C block in Swift 2 in order to add it to an NSArray like so :
typealias CompletionBlock = () -> Void
let aBlock:CompletionBlock = {
print("Hello world!")
}
let nsArray = NSMutableArray()
nsArray.addObject(aBlock) // Error
I know it will work just fine with a Swift array, but I need an NSArray here for compatibility with existing Objective-C code. And if I use a swift array the compiler will refuse to cast it to an NSArray because it won't be a [AnyObject] (it will be a [Any]).
The problem here is that a swift closure is not an object contrary to Objective-C blocks which are objects behind the scene (they are instances of NSBlock which is a subclass of NSObject)
So my question is : How do a create an Objective-C block in swift ? I've tried using #convention (block) in the typealias but it doesn't work.
EDIT : As of Swift 3, this is completely unnecessary (and doesn't even work). Adding closures to Objective-C arrays works out of the box in Swift 3. The answer below is valid for Swift 2 only.
I know this is a duplicate but I will still post a refactored answer from swift-closure-as-anyobject and cast-closures-blocks in case anyone lands on this one first.
The solution is to use the unsafeBitCast function to convert the Swift closure to an Objective-C compatible object before adding it to an NSArray and back before using it in Swift.
// The `#convention(block)` is important here in order to get
// Objective-C like memory management
typealias CompletionBlock = #convention(block) () -> Void
let aBlock:CompletionBlock = {
print("Hello world!")
}
let nsArray = NSMutableArray()
let blockObject = unsafeBitCast(aBlock, AnyObject.self)
nsArray.addObject(blockObject)
let closureObject = nsArray[0]
let closure = unsafeBitCast(closureObject, CompletionBlock.self)
closure()

Syntax to return a on Objective-C class reference from a method?

I did try to google this, but actually found nothing. Coming from a strong Smalltalk background, I thought the following would be fine:
#import "ValveTargetState.h"
- (id) targetStateClass {
return ValveTargetState;
}
- (void) targetIsActive:(BOOL)isActive {
self.targetState = [[[self targetStateClass] alloc] initValve: self isActive: isActive];
[self.targetState push];
}
Basically, I've added a method, so that subclasses can tune what the targetStateClass is used. It was my understanding, that like Smalltalk, classes are objects too in ObjC. But Xcode tells me
Unexpected interface name 'ValveTargetState': expected expression
(for the return ValveTargetState; expression)
I think I'm missing something obvious, but what is it?
Try this:
- (Class)targetStateClass
{
return [ValveTargetState class];
}
Assuming that ValveTargetState is a class that inherits ultimately from NSObject, either
[ValveTargetState class]
or
[ValveTargetState self]
will give you the pointer to the class object for ValveTargetState.
It would be much better to use ValveTargetState directly, but unfortunately the name of a class is not a valid expression in Objective-C.

Does the self keyword have any special meaning for a C struct?

Trying to make sense of MTAudioProcessingTap sample code from WWDC 2012.
This structure is used by the Tap to pass data around.
typedef struct AVAudioTapProcessorContext {
...
void *self; //Note use of word self
} AVAudioTapProcessorContext;
The word self here is used apparently as a standard variable name even though Xcode highlights it in pink. Is this just a code parsing oversight in Xcode?
Later, the Tap management class passes a reference to itself to this variable.
//Setting up Tap callbacks:
callbacks.clientInfo = (__bridge void *)self,
//And then storing it in the above struct when it's passed by the callback:
static void tap_InitCallback(MTAudioProcessingTapRef tap, void *clientInfo, void **tapStorageOut)
{
AVAudioTapProcessorContext *context = calloc(1, sizeof(AVAudioTapProcessorContext));
...
context->self = clientInfo;
*tapStorageOut = context;
}
And the confusion starts again in a later function (another Tap callback). Once again, the word self is used to refer to the Tap management class reference. Note that this occurs in a function defined after #end of the Tap management class implementation.
AudioFFTTapProcessor *self = ((__bridge AudioFFTTapProcessor *)context->self);
self has a special meaning only in the context of an Objective-C method. In all other contexts, it's just an ordinary identifier. You can think of Objective-C methods as being rewritten to take a hidden parameter with the name self (and a second hidden parameter named _cmd that's not relevant for this discussion), e.g.:
#implementation MyClass
- (void)doSomething:(int)param1 withThis:(NSString *)param2
{
// 'self' refers to the MyClass instance in this function
...
}
#end
could be imagined as if it were rewritten like this:
void MyClass_doSomething(id self, SEL _cmd, int param1, NSString *param2)
{
// 'self' refers to the MyClass instance in this function
...
}
Outside of Objective-C methods, you can use self as any other kind of identifier, whether it be a local variable, a struct member, a global function, or type name, etc., though the latter uses should be highly discouraged due to high potential for confusion.
So yes, XCode highlighting the self instance in pink in the struct definition is just a parsing anomaly.
self is not a keyword in C. It's a keyword for Objective-C, which is probably why it's getting highlighted.
self does not mean anything special as a C struct field name. Objective-C uses it as the equivalent of most other OO languages' this, which is why Xcode highlights it specially.

Using objc_msgSendSuper to invoke a class method

I was going through and replacing #synthesized(self) locks w/ this method
void _ThreadsafeInit(Class theClassToInit, void *volatile *theVariableItLivesIn, void(^InitBlock)(void))
{
//this is what super does :X
struct objc_super mySuper = {
.receiver = (id)theClassToInit,
.super_class = class_getSuperclass(theClassToInit)
};
id (*objc_superAllocTyped)(struct objc_super *, SEL, NSZone *) = (void *)&objc_msgSendSuper;
// id (*objc_superAllocTyped)(id objc_super, SEL, NSZone *) = (void *)&objc_msgSend;
do {
id temp = [(*objc_superAllocTyped)(&mySuper /*theClassToInit*/, #selector(allocWithZone:), NULL) init];//get superclass in case alloc is blocked in this class;
if(OSAtomicCompareAndSwapPtrBarrier(0x0, temp, theVariableItLivesIn)) { //atomic operation forces synchronization
if( InitBlock != NULL ) {
InitBlock(); //only the thread that succesfully set sharedInstance pointer gets here
}
break;
}
else
{
[temp release]; //any thread that fails to set sharedInstance needs to clean up after itself
}
} while (*theVariableItLivesIn == NULL);
}
which while a bit more verbose exhibits significantly better performance in non-contested cases
along with this little macro (excuse poor formatting, it's very simple). To allow the block to be declared after the initial nil check, looks to help LLVM keep the "already initialized" path extremely fast. That's the only one I care about.
#define ThreadsafeFastInit(theClassToInit, theVariableToStoreItIn, aVoidBlockToRunAfterInit) if( theVariableToStoreItIn == nil) { _ThreadsafeInitWithBlock(theClassToInit, (void *)&theVariableToStoreItIn, aVoidBlockToRunAfterInit); }
So initially implemented it using the commented out sections for objc_superAllocTyped (actually first using [theClassToInit allocWithZone:NULL], which was definitely the best approach :) ), which worked great until I realized that most of the singletons in the project had overridden allocWithZone to return the singleton method... infinite loop. So I figured using objc_msgSendSuper should sort it out quickly, but I get this error.
[51431:17c03] +[DataUtils allocWithZone:]: unrecognized selector sent to class 0x4f9584
The error doesn't seem to be related to the actual problem, as...
(lldb) po 0x4f9584
$1 = 5215620 DataUtils
(lldb) print (BOOL)[$1 respondsToSelector:#selector(allocWithZone:)]
(BOOL) $2 = YES
So I'm definitely missing something... I compared to assembly generated by a [super allocWithZone:NULL] method in an empty class... almost identical except for the functions called have different names (maybe just using different symbols, no idea, can't read it that well).
Any ideas? I can use class_getClassMethod on the superclass and call the IMP directly, but I'm trying to be reasonable in my abuse of the runtime :)
Alright, this wasn't actually that tricky once I recalled that the meta class contains all of the method information for a Class instance obtained via -[self class] or +[self] -> thanks http://www.cocoawithlove.com/2010/01/what-is-meta-class-in-objective-c.html
This error occurred because I was asking the runtime to look up the method in NSObject's set of instance methods, which obviously doesn't contain allocWithZone: . The mistake in the error log presumably originated because the receiver was a metaclass instance, and Apple has their interns implement error logs.
so while with a normal instance method call via objc_msgSendSuper, you would pass a metaclass instance as objc_super.super_class, to invoke a class method, the metaclass itself is needed (everything is one level up).
Example, and a diagram that helped me understand this - (http://www.sealiesoftware.com/blog/archive/2009/04/14/objc_explain_Classes_and_metaclasses.html)
struct objc_super mySuper;
mySuper.receiver = theClassToInit; //this is our receiver, no doubt about it
//either grab the super class and get its metaclass
mySuper.super_class = object_getClass( class_getSuperclass( theClassToInit ) );
//or grab the metaclass, and get its super class, this is the exact same object
mySuper.super_class = class_getSuperclass( object_getClass( theClassToInit ) );
Then the message can be resolved correctly. Makes perfect sense now that I started paying attention :P
Anyways, now that I found my mistake I feel like I've leveled up my Objc runtime understanding. I was also able to fix an architectural mistake made two years ago by someone I never met without having to modifying and re-test dozens of classes across 3 projects and 2 static libraries (God I love Objective-C). Replacing the #synchronized construct with a simple function call also halved the compiled code size of those methods. As a bonus, all our singleton accessors are now (more) threadsafe, because the performance cost for doing so is now negligible. Methods which naively re-fetched the singleton object multiple times (or in loops) have seen a huge speedup now that they don't have to acquire and release a mutex multiple times per invocation. All in all I'm very happy it all worked as I'd hoped.
I made a "normal" Objective-C method for this on a category of NSObject, which will work for both instance and Class objects to allow you to invoke a superclass's implementation of a message externally. Warning: This is only for fun, or unit tests, or swizzled methods, or maybe a really cool game.
#implementation NSObject (Convenience)
-(id)performSelector:(SEL)selector asClass:(Class)class
{
struct objc_super mySuper = {
.receiver = self,
.super_class = class_isMetaClass(object_getClass(self)) //check if we are an instance or Class
? object_getClass(class) //if we are a Class, we need to send our metaclass (our Class's Class)
: class //if we are an instance, we need to send our Class (which we already have)
};
id (*objc_superAllocTyped)(struct objc_super *, SEL) = (void *)&objc_msgSendSuper; //cast our pointer so the compiler can sort out the ABI
return (*objc_superAllocTyped)(&mySuper, selector);
}
so
[self performSelector:#selector(dealloc) asClass:[self superclass]];
would be equivalent to
[super dealloc];
Carry on runtime explorers! Don't let the naysayers drag you into their land of handwaving and black magik boxes, it's hard to make uncompromisingly awesome programs there*.
*Please enjoy the Objective-C runtime responsibly. Consult with your QA team for any bugs lasting more than four hours.