Best objective-c questions in May 2012

Why can't we use C-strings as SELs?

12 votes

So, I've been messing around with the objc-runtime again (surprise surprise), and I found an interesting block of code here:

const char *sel_getName(SEL sel) {
#if SUPPORT_IGNORED_SELECTOR_CONSTANT
    if ((uintptr_t)sel == kIgnore) return "<ignored selector>";
#endif
    return sel ? (const char *)sel : "<null selector>";
}

So, what this tells me is that a SEL is equivalent to a C-string, in every mannerism. Doing a hex dump of the first 16 bytes of SEL that contains @selector(addObject:) gives the following:

61 64 64 4F 62 6A 65 63 74 3A 00 00 00 00 00 00

Which is equal to the C-string addObject:.

With that said, why does this code crash when I use the C-string as the selector?

SEL normalSEL  = @selector(addObject:);
SEL cStringSEL = (SEL) "addObject:";

NSMutableArray *arr = [NSMutableArray arrayWithObjects:@"1", @"2", nil];

[arr performSelector:normalSEL withObject:@"3"];
[arr performSelector:cStringSEL withObject:@"4"];

NSLog(@"%@", arr);

As far as I can tell, the contents of the selectors are the same, so why the crash on the second one with the following error message?

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayM addObject:]: unrecognized selector sent to instance 0x101918720' ***

Selectors are interned C strings and are compared by their address, not their contents. The string contents is only used for converting to/from an external string representation. Interning is done to improve performance--when the runtime is looking up the method implementation that matches a selector it can compare the selector pointers directly instead of dereferencing each pointer and comparing the characters.

Is there any way other than thumbnail method to take a screenshot of an video in Iphone?

11 votes

Is there any way other than thumbnail method to take a screenshot of an video? If yes then please tell me if not then please tell me how to resize an thumbnail image with a same resolution? i have getting an issue while taking screenshot of video.i have used this:-

How to take a screenshot from an video playing through MPMediaPlayerController in iPhone?

After this i ve used merging of an image by which i'm getting thisResolution problem

here image quality is my big issue.

I want this

Required Output

I want only video and drawing shown in above screenshot with same resultion. Please help Thanks :)

There is a way other than thumbnail method by which i can get screenshot of a video.!!!

The way is this :-

- (UIImage *)imageFromVideoURL 
{
// result 
UIImage *image = nil;

// AVAssetImageGenerator
AVAsset *asset = [[AVURLAsset alloc] initWithURL:appDelegate.videoURL options:nil];; 
AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
imageGenerator.appliesPreferredTrackTransform = YES;

// calc midpoint time of video
Float64 durationSeconds = CMTimeGetSeconds([asset duration]);
CMTime midpoint = CMTimeMakeWithSeconds(durationSeconds/2.0, 600); 

// get the image from 
NSError *error = nil; 
CMTime actualTime;
CGImageRef halfWayImage = [imageGenerator copyCGImageAtTime:midpoint actualTime:&actualTime error:&error];

if (halfWayImage != NULL) 
{
    // cgimage to uiimage
    image = [[UIImage alloc] initWithCGImage:halfWayImage];
    [dic setValue:image forKey:kImage];
    NSLog(@"Values of dictonary==>%@", dic);
    NSLog(@"Videos Are:%@",appDelegate.videoURLArray);
    CGImageRelease(halfWayImage);
}
return image;
}

But still my problem is same i m drawing on video i cant get video's screenshot with drawing overlay,so for that i ve to use merging of images.

Thanks :)

MonoTouch v. Objective-C for new iPhone devs

10 votes

I'm working with a small startup with the goal of creating an iPhone application. The programmers on our team all know both C and Java, but we've got no ObjC/C#/iPhone experience -- we'll be learning something no matter what. All the questions I've seen on this subject have been from the perspective of a C# programmer getting into iOS, so I don't have any information on the relative merits of learning one or the other.

So, what I want to know is the relative merits of C# and Objective-C for new programmers, with respect to these things:

  • Performance
  • Ease of use
  • Ease of learning
  • Reliability (i.e., will a new iOS version break a MonoTouch app?)

Also, would writing in MonoTouch have any benefits for cross-platform development with Android?

If the goal of your startup is to create an iPhone app, then obviously you should learn the language iOS applications are built with, Objective-C. Your team already has experience with C, so it should be very easy to get started. The Big Nerd Ranch books will get you up and running in a week or two (I'm not associated with Big Nerd Ranch, I just think they're awesome).

I don't understand why you bring up MonoTouch, considering your team doesn't have C#/.NET experience. There are tons of other frameworks like MonoTouch, including Titanium SDK (JavaScript), RubyMotion (Ruby), and so forth. All of these are great, but as I see it are primarily for those with experience with their respective languages. They allow you to write iOS applications using a language you're more familiar with, but have the following drawbacks:

  1. Performance can be just as good, but usually a little (sometimes a lot) worse than an application written in Objective-C.
  2. Resources for learning are not as plentiful as those for iOS SDK/Objective-C. There are tons of people who want to make iOS apps, and of those people some use these frameworks, and they are diluted amongst them. Only a small fraction of the resources available for iOS development will be devoted to any given framework.
  3. These frameworks are based on the iOS SDK/Objective-C, so naturally development is always a step behind. If Apple rolls out radically new APIs in the next version of the iOS SDK, you'll have to wait for these frameworks to adapt (applications written in Objective-C might break, too, but it'll be easier to deal with based on point #2).
  4. Most of these frameworks require a familiarity with the iOS SDK. You will still need to know that an application calls the - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method on launch, but in addition you will need to remember how to translate that into the appropriate method for MonoTouch: public override bool FinishedLaunching (UIApplication app, NSDictionary options). The iOS SDK is massive, and most iOS developers rely on Apple's documentation for insight. So you will be looking at the API docs, written for Objective-C, and translating methods into the framework of your choice.
  5. The Objective-C ecosystem is constantly evolving, with cool new projects like CocoaPods, bwoken, etc. These are primarily developed with Objective-C and Xcode in mind. Configuring them to work with your framework of choice will almost invariably cause you extra time and work.

The above points have generally kept me from delving too much into any of these other frameworks. They're all very cool, but their primary merit in adoption seems to be facilitating development for people with deep skill sets outside of Objective-C, or allowing cross-platform development for small teams lacking the resources to invest in Android and iOS (and Windows Phone) programmers. Hopefully the benefits outweigh the above costs for adopters.

Also, would writing in MonoTouch have any benefits for cross-platform development with Android?

I suppose it would. Indeed, the MonoTouch homepage touts that as a primary advantage of using the framework. I'm not so sure about view/controller classes, since those seem like they would be tied into UIKit, but the logic encapsulated in your models should be fairly easy to port.

Long story short, I think you and your team should stick with Objective-C for iOS development.

How to create video from its frames iPhone

10 votes

I had done R&D and got success in how to get frames in terms of images from video file played in MPMoviePlayerController.

Got all frames from this code, and save all images in one Array.

for(int i= 1; i <= moviePlayerController.duration; i++)
{
    UIImage *img = [moviePlayerController thumbnailImageAtTime:i timeOption:MPMovieTimeOptionNearestKeyFrame];
    [arrImages addObject:img];
}

Now the question is that, After change some image file, like adding emoticons to the images and also adding filters, such as; movie real, black and white, How can we create video again and store the same video in Document directory with the same frame rate and without losing quality of video.

After changing some images I had done following code to save that video again.

- (void) writeImagesAsMovie:(NSString*)path 
{
    NSError *error  = nil;
    UIImage *first = [arrImages objectAtIndex:0];
    CGSize frameSize = first.size;
    AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:
                                  [NSURL fileURLWithPath:path] fileType:AVFileTypeQuickTimeMovie
                                                              error:&error];
    NSParameterAssert(videoWriter);

    NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                                   AVVideoCodecH264, AVVideoCodecKey,
                                   [NSNumber numberWithInt:640], AVVideoWidthKey,
                                   [NSNumber numberWithInt:480], AVVideoHeightKey,
                                   nil];
    AVAssetWriterInput* writerInput = [[AVAssetWriterInput
                                        assetWriterInputWithMediaType:AVMediaTypeVideo
                                        outputSettings:videoSettings] retain];

    AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor
                                                     assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput
                                                     sourcePixelBufferAttributes:nil];

    NSParameterAssert(writerInput);
    NSParameterAssert([videoWriter canAddInput:writerInput]);
    [videoWriter addInput:writerInput];

    [videoWriter startWriting];
    [videoWriter startSessionAtSourceTime:kCMTimeZero];

    int frameCount = 0;
    CVPixelBufferRef buffer = NULL;
    for(UIImage *img in arrImages)
    {
        buffer = [self newPixelBufferFromCGImage:[img CGImage] andFrameSize:frameSize]; 

            if (adaptor.assetWriterInput.readyForMoreMediaData) 
            {
                CMTime frameTime = CMTimeMake(frameCount,(int32_t) kRecordingFPS);
                [adaptor appendPixelBuffer:buffer withPresentationTime:frameTime];

                if(buffer)
                    CVBufferRelease(buffer);
            }
        frameCount++;
    } 

     [writerInput markAsFinished];
     [videoWriter finishWriting];
}


- (CVPixelBufferRef) newPixelBufferFromCGImage: (CGImageRef) image andFrameSize:(CGSize)frameSize
{
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                             [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
                             [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey,
                             nil];
    CVPixelBufferRef pxbuffer = NULL;
    CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, frameSize.width,
                                          frameSize.height, kCVPixelFormatType_32ARGB, (CFDictionaryRef) options, 
                                          &pxbuffer);
    NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);

    CVPixelBufferLockBaseAddress(pxbuffer, 0);
    void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);
    NSParameterAssert(pxdata != NULL);

    CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(pxdata, frameSize.width,
                                                 frameSize.height, 8, 4*frameSize.width, rgbColorSpace, 
                                                 kCGImageAlphaNoneSkipFirst);
    NSParameterAssert(context);
    CGContextConcatCTM(context, CGAffineTransformMakeRotation(0));
    CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), 
                                           CGImageGetHeight(image)), image);
    CGColorSpaceRelease(rgbColorSpace);
    CGContextRelease(context);

    CVPixelBufferUnlockBaseAddress(pxbuffer, 0);

    return pxbuffer;
}

I am new in this topic so please help me solve this question?

Thanks in advance.

Regards,
Marvin.

You can refer following links hope you get some help :-

  1. http://www.iphonedevsdk.com/forum/iphone-sdk-development-advanced-discussion/77999-make-video-nsarray-uiimages.html

  2. Using FFMPEG library with iPhone SDK for video encoding

  3. Iphone SDK,Create a Video from UIImage

  4. iOS Video Editing - Is it possible to merge (side by side not one after other) two video files into one using iOS 4 AVFoundation classes?

Mac OS X daemon using Objective-C - launchd

9 votes

I'm new in Mac OS X world but I have skills on Windows dev.

I need to develop a daemon (on Windows will be Windows Service) that uploads/downloads files from a Web Service.

My question is: is it possible to create an app written in Objective-C that will be the daemon (to upload/download) and to launch it when the OS starts using launchd? Or there's another way to create a daemon?

Thank you

I don't know if I have understood correctly but I guess you can do it. The next link could be a good start for understand how Daemons work in Objective-C Apple Developer Then here there is a interesting piece of code. It is about GPS but it might be usefull. Also get a look of this github folder.It is a controller for start/stop daemons

NSString by removing the initial zeros?

9 votes

How can I remove leading zeros from an NSString?

e.g. I have:

NSString *myString;

with values such as @"0002060", @"00236" and @"21456".

I want to remove any leading zeros if they occur:

e.g. Convert the previous to @"2060", @"236" and @"21456".

Thanks.

NSString *str = @"000123";      
NSString *clean = [NSString stringWithFormat:@"%d", [str intValue]];

In Objective-C, the ownership is for object, not variable or pointers?

9 votes

In a book, the following is said:

So how do you know when an object is owned, and by whom? Consider the following example:

NSString *str = [[NSString alloc] initWithString:@”Hello”];  
NSString *str2 = str;

In this example, you use the alloc keyword for str, so you own str. Therefore, you need to release it when it’s no longer needed. However, str2 is simply pointing to str, so you do not own str2, meaning you need not release str2 when you are done using it.

I thought ownership is by object, not variable or pointer... so we can't say we "own str" or "own str2"... we own an object, which is pointed to by either str or str2, and if we use [str release] or [str2 release], it is all the same.

The other description is:

For example, consider the example used in the previous section:

NSString *str = [[NSString alloc] initWithString:@”Hello”]; 
NSString *str2 = str;
[str release];
[str2 release]; //---this is not OK as you do not own str2---

Attempting to release str2 will result in a runtime error because you cannot release an object not owned by you.

We can actually use [str2 release] if that is called before [str release]. If we do that, then the line [str release] will cause an error because now str as well as str2 are both dangling pointers, and supposedly when release was sent to the object the first time, the reference count became 0, and dealloc was called immediately, and the memory was freed by the C function free().

Is the above correct, or maybe there is something else to be corrected?

Don't think of it in terms of managing memory, but in terms of object ownership. You obtain ownership of an object when you allocate it, retain it, or copy it. You are responsible for releasing exactly the objects you own, not others.

In your example the assignment to str2 does not take ownership of the object, but if you really need a second "owning" reference to it, then you ought to do [str2 retain], after which it is not an error to do [str release]; [str2 release];. This is also what would happen automatically using ARC, unless you annotated str2 as a weak reference. (Of course in this simple case the unnecessary retain/release could be optimized away internally by the compiler.)

iOS and ARC : How to retain self during asynchronous operations?

8 votes

It's the first time I'm fiddling around with iOS5 and ARC. So far, so good, it works, but I've run into some kind of a problem.

I have a custom UIStoryboardSegue in which I use Facebook Connect (or other services) to log the user into my app. Simply put, it should do the following :

  1. User clicks on the button
  2. Button fires the Segue
  3. The segue starts asynchronous login but does not immediately push the view controller
  4. If, and only if the login is successful, the segue pushes the view controller

What happens instead, is that the login starts, but the segue is immediately released by ARC before it has any chance to complete.

I thought of a quick'n'dirty hack to prevent this:

@interface BSLoginSegue() {
    __strong BSLoginSegue *_retained_self;
}
@end

// Stuff...
// Other stuff...

- (void) perform {
    login();
    _retained_self = self;
}

- (void) loginServiceDidSucceed:(BSLoginService *)svc {
    ...
    _retained_self = nil;
}

The thing is, it really is a hack, so I was wondering if there were any other, and more elegant way I could do the same?

The idea that a class should need to retain itself suggests that there might be a design problem. When an object retains itself, it becomes unclear who owns it or who might have a reference to it, and the risk of a leak is high.

What class is responsible for presenting the segue? Is it the same class the contains the button? This class should retain the segue, present the segue, and then release the segue when/if the segue completes.

Without further insight into the view controller hierarchy, it's hard to give specific advice. But my first reaction is to suggest that the view controller that is deciding to present the segue or not should have a strong property on the segue. The subclassed segue might define a protocol that the presenting class may conform to to be advised as to when the segue should be nilled/released.

How to send files to other iPhones via Bluetooth

8 votes

I'm doing a little app to allow users to edit and share text fast on the iPhone.

So I know a little of Bluetooth programing for iPhone, but I'm not able to do what I want to do:

  • The text of the app is saved in NSUserDefaults. I want to send this to another iOS device by the key: "Text1", "Text2" or "Text3"( I know that I have to convert the text that will be in a string to NSData, and tren I would like to have it in a NSMutableDictionary with its key)

  • I also want to be looking for new iOS devices arround all the time.

Please help me because I don't know how I can do it and it's so hard to find tutorials of iPhone Bluetooth programming, thank you!

Use GameKit, there are many tutorials.

Use this to find other devices:

GKPeerPickerController *picker = [[GKPeerPickerController alloc] init];
picker.delegate = self;
picker.connectionTypesMask = GKPeerPickerConnectionTypeNearby;
[picker show];

How to transport NSManagedObject subclass between classes?

8 votes

We have made a repository layer for interacting with Core Data that have methods such as allItems(), addItem:(Item*)item where item being the NSManagedObject subclass. When we need to save an item we invoke the method on the repository passing the subclass instance as an argument. However, that does not work because we can't use the initinitializer and the context is hidden inside the repository.

What is the best way to transfer objects when you have an architecture like this? Is making a ItemDTO an passing that around an option? Or are there better ways to solve this such as not using subclassed NSManagedObject at all and just use key/value that works.

I wrotecopy-pasted a sample project that hides the context from model custom classes: branch 10583736.

(it's not final production code, just a quick example, don't expect it to deal with multithreading or weird errors)

Hiding the context to custom classes is just a matter of defining custom methods to deal with every situation where you normally will request the context and use it.

You can define a class for the store layer without exposing the context:

@interface DataStore : NSObject

+ (id)shared;

- (void)saveAll;
- (NSEntityDescription *)entityNamed:(NSString *)name;
/* more custom methods ... */
- (NSManagedObject *)fetchEntity:(NSEntityDescription *)entity withPredicate:(NSPredicate *)predicate;

@end

I suggest to use a common ancestor for all your custom model classes to save some typing. This class can be the only one that interacts with DataStore directly. It doesn't have access to the context.

@interface DataObject : NSManagedObject

+ (NSString *)entityName;
+ (NSEntityDescription *)entity;
- (void)save;
/* more custom methods ... */

@end

Finally your model custom classes defines any method you need probably taking advantage of whatever is provided by the superclass:

@interface Card : DataObject

@property (nonatomic, retain) NSString * question;
@property (nonatomic, retain) NSString * answer;
@property (nonatomic, retain) Deck *deck;

/* return a new card */
+ (Card *)card; 

/* more custom methods ... */

@end

The master branch has a more usual approach where model classes obtain the context and work with it.

localStorage not persisting in OSX app (Xcode 4.3)

8 votes

From what I have seen, if you are building a OSX desktop HTML5 app and want localStorage to persist in your WebView wrapper, you need to do something like this:

WebPreferences* prefs = [webView preferences];
[prefs _setLocalStorageDatabasePath:@"~/Library/Application Support/MyApp"];
[prefs setLocalStorageEnabled:YES];

Taken from: How do I enable Local Storage in my WebKit-based application?

But this doesn't seem to work for me in Xcode 4.3. Instead I get

"No visible @interface for 'WebPreferences' declares the selector '_setLocalStorageDatabasePath:'
"No visible @interface for 'WebPreferences' declares the selector 'setLocalStorageEnabled:'

I'm very new to Objective C, and are probably doing something silly like not including some header or something.

I've included the WebKit framework and both of these headers:

#import <WebKit/WebKit.h>
#import <WebKit/WebPreferences.h>

And what's weird is that I can access other methods of prefs, i.e. [prefs setDefaultFontSize:10] - but just not the two above that I listed.

Any ideas? Is this something that has been removed in Xcode 4.3?

OK, I have a solution. I looked at the source code to macgap and noticed how they were dealing with this issue.

It turns out the error message I was getting does make a little sense - I needed to declare an interface for WebPreferences first.

@interface WebPreferences (WebPreferencesPrivate)
- (void)_setLocalStorageDatabasePath:(NSString *)path;
- (void) setLocalStorageEnabled: (BOOL) localStorageEnabled;
@end

...

WebPreferences* prefs = [WebPreferences standardPreferences];
[prefs _setLocalStorageDatabasePath:"~/Library/Application Support/MyApp"];
[prefs setLocalStorageEnabled:YES];
[webView setPreferences:prefs];

Like I said, I'm new to Objective-C. I don't really get why the interface is needed in order to call those two methods (i.e. when I can call the other methods without the interface).

Can the messages sent to an object in Objective-C be monitored or printed out?

7 votes

Possible Duplicate:
Intercept method call in Objective-C
How to log all methods used in iOS app

For example, a UIViewController object in iOS receives many messages before its view is shown to a user:

  1. viewWillAppear
  2. viewWillLayoutSubviews
  3. viewDidLayoutSubviews
  4. viewDidAppear
  5. ...

because the framework's source code is not viewable, we have to rely on books or blogs, or is there a way to print out or monitor all the messages sent to this object by (1) Objective-C, or (2) by any tool?

Rather than my comment, the best approach that I used (and still use) is calling:

(void)instrumentObjcMessageSends(YES);

When I need to start logging all messages and then:

(void)instrumentObjcMessageSends(NO);

Don't forget to add #import <objc/runtime.h>.
When I don't need it anymore. The annoying thing is that the log is created under /tmp/msgSends- and this means that you have to open the terminal and use tail to see it in a readable way.

What is printed is something like this:

- CustomTableViewController UIViewController _parentModalViewController
- CustomTableViewController UIViewController isPerformingModalTransition
- CustomTableViewController UIViewController setInAnimatedVCTransition:
- CustomTableViewController UIViewController viewWillMoveToWindow:
- CustomTableViewController UIViewController isPerformingModalTransition
- CustomTableViewController UIViewController parentViewController
- CustomTableViewController UIViewController _popoverController
- CustomTableViewController UIViewController _didSelfOrAncestorBeginAppearanceTransition
- CustomTableViewController UIViewController parentViewController
- CustomTableViewController UIViewController __viewWillDisappear:
- CustomTableViewController UIViewController _setViewAppearState:isAnimating:
- CustomTableViewController UIViewController automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers

Note: It has been a while since I used this approach for the last time and it looks like this approach doesn't log private methods subclassed. So, if you have a DummyClass with -(void)_dummyMethod as private and then a DummySubClass with a -(void)_dummyMethod implementation, the message will not be logged.

For iOS, this works only on Simulator.