For a day now I've been trying to get a piece of code working to help me handle find-and-create for CoreData in a nice way (from this article I found http://emplementation.blogspot.nl/2011/12/importing-data-into-core-data-while.html). I ended up working with code blocks which I've never done before.
Somehow I can't fix the following error which occurs because something is different in my typedef from what I try to define in my function. I think I understand it has something to do with the block being defined as __strong in the typedef but differently in my implementation file.
Error
Incompatible block pointer types initializing '_strong objectOperationBlock' (aka 'void (^_strong)(NSManagedObjectContext *_strong, NSDictionary *_strong, NSManagedObject *_strong)') with an expression of type 'void (^)(NSManagedObject *_strong, NSDictionary *_strong, NSManagedObject *_strong)'
MyViewController.h
typedef void (^objectOperationBlock)(NSManagedObjectContext *context,
NSDictionary *hostObjectData,
NSManagedObject *localManagedObject);
MyViewController.m
objectOperationBlock matchedBlock = ^(NSManagedObject *context, NSDictionary *hostObjectData, NSManagedObject *localManagedObject){
NSLog(#"Dosomething");
};
In all my attempts I've found out that this could will build (but it's not using the typedef)
void (^matchedBlock)(NSManagedObject*, NSDictionary*, NSManagedObject*) = ^(NSManagedObject *context, NSDictionary *hostObjectData, NSManagedObject *localManagedObject){
NSLog(#"Dosomething");
};
Thanks in advance for all your help!
Did I miss that the typedef should read:
typedef void (^objectOperationBlock)(NSManagedObject *managedObject,
NSDictionary *hostObjectData,
NSManagedObject *localManagedObject);
Related
I want to create a wrapper around the NSURLSession and I found some nice code but was written in Swift.https://github.com/daltoniam/SwiftHTTP.But since I still write production code in Objective-C I started borrowing the idea of the above code, though I have hard time to understand the following code and translate to Objective-C (if needed).
I know I could use AFNetworking but this is not feasible due to architecture decisions when building a distributable framework.
The code:
/// encoding for the request.
public var stringEncoding: UInt = NSUTF8StringEncoding
// Somewhere in a method
var charset = CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(self.stringEncoding));
if request.valueForHTTPHeaderField(contentTypeKey) == nil {
request.setValue("application/x-www-form-urlencoded; charset=\(charset)",
forHTTPHeaderField:contentTypeKey)
}
request.HTTPBody = queryString.dataUsingEncoding(self.stringEncoding)
My Objective-C code:
#property (assign, nonatomic) NSUInteger stringEncoding;
// In this line I get a compiler warning and in runtime it crashes with BAD_EXC
CFStringEncoding cfStringEncoding = CFStringConvertIANACharSetNameToEncoding(CFStringConvertNSStringEncodingToEncoding(self.stringEncoding));
if (![mutableRequest valueForHTTPHeaderField:ContentTypeKey])
{
[mutableRequest setValue:[NSString stringWithFormat:#"application/x-www-form-urlencoded; charset=%u", (unsigned int)cfStringEncoding] forHTTPHeaderField:ContentTypeKey];
}
mutableRequest.HTTPBody = [queryString dataUsingEncoding:self.stringEncoding];
Compiler warning:
Incompatible integer to pointer conversion assigning to 'CFStringEncoding' (aka 'unsigned long') from 'CFStringRef' (aka 'const struct __CFString *')
I don't have strong experience working with CFStringEncoding and CFString so I find it hard to translate the documentation.
Do I really need this conversion, and what is it's purpose?
Try using NSString instead:
NSString *charset =
(NSString *)CFStringConvertEncodingToIANACharSetName
(CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding));
This is what typically was used before the Swift version, and as I recall AFNetworking used a similar method (if not the same).
I am trying to update some code for XCode 6.1 / Yosemite. It's a bit weird because it's a macro, but essentially it looks like:
dispatch_block_t blk = ^{ [[self globalEventsHandler] someMethod self]; };
if([NSThread isMainThread]) blk();
else dispatch_async(dispatch_get_main_queue(), blk);
This is causing compilation problems. I have already set OS_OBJECT_USE_OBJC=0 per GCD guide in my preprocessor settings, since right now I'm not interested in modernizing the code.
The first is Implicit conversion of block pointer type 'void (^)(void)' to C pointer type 'dispatch_block_t' (aka 'void *') requires a bridged cast. I can accept the suggest fix for this and get:
dispatch_block_t blk = (__bridge dispatch_block_t)^{ [[self globalEventsHandler] someMethod self]; };
if([NSThread isMainThread]) blk();
else dispatch_async(dispatch_get_main_queue(), blk);
but now I get a new error: Called object type 'dispatch_block_t' (aka 'void *') is not a function or function pointer. And on that I'm stuck.
Questions:
Is there a way to call dispatch_block_t directly now? I have found the original code pattern in a few older blog posts, so I suspect it is (was) common.
Is __bridge the correct way to do this? There seem to be other options related to dispatch_retain and friends that may be appropriate.
I feel like I'm missing a fundamental concept here, which would be very likely since I'm quite inexperienced with OSX development.
For bonus points: how would you get this working without disabling OS_OBJECT_USE_OBJC?
That code snippet is totally ok with Xcode 6.1 with OS X SDK 10.10. However, these compile error messages are odd.
Implicit conversion of block pointer type 'void (^)(void)' to C pointer type 'dispatch_block_t' (aka 'void *') requires a bridged cast
Called object type 'dispatch_block_t' (aka 'void *') is not a function or function pointer.
dispatch_block_t should be the following in dispatch/object.h.
typedef void (^dispatch_block_t)(void);
But these error messages say dispatch_block_t is the same as void *. Did you typedef dispatch_block_t yourself instead of including Foundation/Foundation.h or dispatch/dispatch.h? You'd better search dispatch_block_t typedef in your code.
I'm trying to implement the countByEnumeratingWithState:objects:count: method from the NSFastEnumeration protocol on a custom class.
So far I have it iterating through my objects correctly, but the objects that are returned aren't Objective-C objects but rather the core foundation equivalents.
Here's the part of the code that sets the state->itemsPtr:
MyCustomCollection.m
- (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState *)state
objects: (id __unsafe_unretained *)buffer
count: (NSUInteger)bufferSize {
// ... skip details ...
NSLog(#"Object inside method: %#", someObject);
state->itemsPtr = (__unsafe_unretained id *)(__bridge void *)someObject;
// ... skip details ...
}
Then I call the 'for..in' loop somewhere else on like this
SomeOtherClass.m
MyCustomCollection *myCustomCollection = [MyCustomCollection new];
[myCustomCollection addObject:#"foo"];
for (id object in myCustomCollection) {
NSLog(#"Object in loop: %#", object);
}
The console output is:
Object inside method: foo
Object in loop: __NSCFConstantString
As you can see, inside the NSFastEnumeration protocol method the object prints fine, but as soon as it gets cast to id __unsafe_unretained * I lose the original Objective-C corresponding class.
To be honest I'm not quite sure how the (__unsafe_unretained id *)(__bridge void *) casting works in this case. The (__unsafe_unretained id *) seems to cast to match the right type itemsPtr needs. The (__bridge void *) seems to cast to a pointer of type void with __bridge used to bridge the obj-c world to the CF world. As per the llvm docs, for __bridge:
There is no transfer of ownership, and ARC inserts no retain operations
Is that correct?
From my understanding __NSCFConstantString is just the core foundation equivalent of NSString. I also understand that with ARC you need to bridge from Objective-C objects to CoreFoundation equivalents because ARC doesn't know how to manage the memory of the latter.
How can I get this working so that the objects in my 'for..in' loop are of the original type?
Also note that in this case I'm adding NSStrings to my collection but in theory it should support any object.
UPDATE
Rob's answer is on the right track, but to test that theory I changed the for loop to this:
for (id object in myCustomCollection) {
NSString *stringObject = (NSString *)object;
NSLog(#"String %# length: %d", stringObject, [stringObject length]);
}
In theory that should work since the objects are equivalent but it crashes with this error:
+[__NSCFConstantString length]: unrecognized selector sent to class
It almost looks like the objects returned in the for loop are classes and not instances. Something else might be wrong here... Any thoughts on this?
UPDATE 2 : SOLUTION
It's as simple as this: (thanks to CodaFi
state->itemsPtr = &someObject;
You're incorrectly casting someObject. What you meant is:
state->itemsPtr = (__unsafe_unretained id *)(__bridge void *)&someObject;
(Let's get rid of those awful casts as well)
state->itemsPtr = &someObject;
Without the address-of, your variable is shoved into the first pointer, which is dereferenced in the loop. When it's dereferenced (basically, *id), you get the underlying objc_object's isa class pointer rather than an object. That's why the debugger prints the string's value inside the enumerator call, and the class of the object inside the loop, and why sending a message to the resulting pointer throws an exception.
Your code is fine the way it is. Your debug output is revealing an implementation detail.
NSString is toll-free-bridged with CFString. This means that you can treat any NSString as a CFString, or vice versa, simply by casting the pointer to the other type.
In fact, under the hood, compile-time constant strings are instances of the type __NSCFConstantString, which is what you're seeing.
If you put #"hello" in your source code, the compiler treats it as a NSString * and compiles it into an instance of __NSCFConstantString.
If you put CFSTR("hello") in your source code, the compiler treats it as a CFStringRef and compiles it into an instance of __NSCFConstantString.
At run-time, there is no difference between these objects in memory, even though you used different syntax to create them in your source code.
Consider the following code:
if([obj isKindOfClass:[NSString class]]) {
NSString *s = [(NSString *)obj stringByAppendingString:#"xyzzy"];
}
I'm a bit confused here. The if statement checks whether or not obj is of the NSString class. If it is, it assigns the object and an appended string to NSString *s, do I understand this correctly? If so, why would you still cast it to (NSString *)?
Doesn't the if statement already check for that and doesn't that make the typecasting unnecessary?
Wouldn't it be perfectly fine to just say:
NSString *s = obj stringByAppendingString:#"xyzzy"];
Thanks in advance.
It all depends on how obj is defined. If it is id obj then no casting is needed, but if it was defined as NSObject *obj the cast is necessary to suppress the compiler warning that stringByAppendingString: is not defined on NSObject. The cast is not needed to make the code work at runtime, it only tells the compiler the "correct" type so it can tell whether the method should exist on the object.
The reason why the cast isn't needed for id is because id means "an object of any type", while NSObject * means "an object of type NSObject".
In Objective-C, I can write:
id pString = #"Hello, World.";
and the compiler will instantiate an NSString without me needing to explicitly call a factory method. However, NSString is really just a Foundation class and thus presumably not part of the actual Objective-C language definition.
So when I write #"String", how does the compiler know to build an NSString in particular, and not some other string-like object? In other words, where does the Objective-C language stop and the Foundation library start?
When you write Objective-C code outside of Cocoa or GNUStep environments, #"..." is not linked to NSString.
In this case, gcc provides an option for specifying a class associated to literal strings:
-fconstant-string-class=class-name
Use class-name as the name of the class to instantiate for each
literal string specified with the syntax "#"..."". The default
class name is "NXConstantString".
The #"" directive appears to be built-in to the objective-c compiler.
For instance, if you remove all #imports from your .m source file (& prefix header), the following line will be a syntax error:
NSString *string = #"ABCD"; // (Doesn't know anything about NSString class)
However, if you change the Foundation NSString type to the built-in void type, it will compile just fine:
void *string = #"ABCD";
So, even without Foundation's NSString definition, the compiler knows how to turn #"" into something that can become an NSString instance at runtime (it probably won't instantiate without Foundation, but the compiler doesn't seem to mind); Since it accepts the syntax without needing any external library definitions, the compiler sees #"" as part of the language.
Your code, however, won't be able to make use of any #"" instance without importing Foundation.h, so from the point of view of your program, #"" is part of the library.
Another interesting bit is that an Objective-C string literal (#"" notation) returns the same type as an explicitly instantiated NSString object:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
printf("string literal class: %s\n", object_getClassName(#"a string literal"););
NSString *str = [[NSString alloc] initWithUTF8String:"asdf"];
printf("explicit NSString class: %s", object_getClassName(str));
[pool drain];
return 0;
}
I vaguely remember that in other, older implementations of Objective-C, the string literal actually returned an object of a slightly different class, but that could be used interchangeably with NSString/NSCFString. Not totally sure on that part, though.