Top Objective-C Interview Questions

author image Hirely
at 10 Jan, 2025

Question: What is the purpose of @synthesize in Objective-C?

Answer:

In Objective-C, @synthesize is a directive used to automatically generate the getter and setter methods for a property, as well as the instance variable (ivar) associated with that property. However, with the introduction of Automatic Reference Counting (ARC) and modern Objective-C, @synthesize has become less necessary in most cases, as the compiler now automatically synthesizes properties for you by default. But understanding its historical usage and functionality is still important.


1. Purpose of @synthesize:

  • Automatic Getter and Setter Generation: When you declare a property in Objective-C using @property, you often want to create a corresponding getter and setter method that handles getting and setting the value of that property. The @synthesize directive tells the compiler to automatically generate these methods for you.

  • Instance Variable Generation: In addition to generating getter and setter methods, @synthesize also automatically generates the backing ivar (instance variable) that holds the actual value of the property. The name of this ivar is usually derived from the name of the property by appending an underscore (_) in front of the property name (e.g., for a property NSString *name, the backing ivar will be _name).

Example:

@interface MyClass : NSObject

@property (nonatomic, strong) NSString *name;

@end

In the past, you would have to explicitly add @synthesize to create the getter and setter for name:

@implementation MyClass

@synthesize name = _name;

@end

Here, the @synthesize directive tells the compiler to create the getter method - (NSString *)name and the setter method - (void)setName:(NSString *)name. The backing ivar is _name.


2. How it Works:

  • Getter and Setter Methods:
    • The getter method returns the value of the backing ivar (e.g., - (NSString *)name).
    • The setter method sets the value of the backing ivar (e.g., - (void)setName:(NSString *)name).
  • Backing Ivar:
    • The ivar is typically created with an underscore prefix (_name), though you can customize it using the @synthesize directive (e.g., @synthesize name = _customName).

Example of manually synthesized property:

@implementation MyClass

@synthesize name = _name; // Generates getter and setter for 'name' with ivar '_name'

@end
  • In this example, the setter - (void)setName:(NSString *)name will set the _name ivar, and the getter - (NSString *)name will return the value of _name.

3. Default Behavior in Modern Objective-C:

Starting with Xcode 4.4 and LLVM 4.0 (2012), the compiler automatically synthesizes properties by default. Therefore, you no longer need to explicitly write @synthesize for each property unless you want to customize the ivar or override the default behavior.

For example:

@interface MyClass : NSObject

@property (nonatomic, strong) NSString *name;

@end

In modern Objective-C, the following will automatically happen behind the scenes without the need for @synthesize:

  • A backing ivar _name will be created.
  • The getter method - (NSString *)name and setter method - (void)setName:(NSString *)name will be automatically generated.

This means you no longer need to write @synthesize unless:

  1. You want to specify a custom name for the backing ivar.
  2. You want to manually synthesize or control the getter and setter methods for advanced cases.

4. When to Use @synthesize:

While not required in most cases today, @synthesize is still useful in certain scenarios:

  • Custom Backing Ivar: If you want to name the ivar something different from the default name (which is _propertyName), you can specify it explicitly with @synthesize.

    Example:

    @interface MyClass : NSObject
    @property (nonatomic, strong) NSString *name;
    @end
    
    @implementation MyClass
    
    // Custom backing ivar '_customName'
    @synthesize name = _customName;
    
    @end

    In this case, the ivar for name is explicitly named _customName, and you can refer to it directly in your methods.

  • Custom Getter or Setter: If you want to implement a custom getter or setter while still using the synthesized ivar, you can override the getter/setter method while keeping the @synthesize directive. This is commonly done if you need additional logic in the getter/setter methods.

    Example:

    @interface MyClass : NSObject
    @property (nonatomic, strong) NSString *name;
    @end
    
    @implementation MyClass
    
    @synthesize name = _name;
    
    // Custom getter
    - (NSString *)name {
        if (!_name) {
            return @"Default Name";  // Fallback value
        }
        return _name;
    }
    
    @end

    Here, the getter is customized to return a fallback value if _name is nil.


5. When @synthesize Is Not Needed:

In most modern Objective-C code, you do not need to explicitly use @synthesize for properties, because the compiler will automatically generate the getter, setter, and backing ivar for you.

Example without @synthesize:

@interface MyClass : NSObject

@property (nonatomic, strong) NSString *name;

@end

@implementation MyClass
// No need for @synthesize, the compiler does it automatically
@end

In this case, the compiler automatically synthesizes:

  • The getter method - (NSString *)name.
  • The setter method - (void)setName:(NSString *)name.
  • The backing ivar _name.

6. Key Points to Remember:

  • @synthesize is used to automatically generate getter, setter, and backing ivar for a property in Objective-C.
  • In modern Objective-C, you rarely need to use @synthesize explicitly, as the compiler automatically synthesizes properties.
  • You can still use @synthesize if you want to customize the ivar name or create custom getter/setter methods.
  • The default ivar name is _propertyName (e.g., _name for a property name), but @synthesize allows you to change this if desired.

Conclusion:

  • @synthesize was historically used to generate getter, setter methods, and a backing ivar for a property.
  • With ARC and modern compilers, explicitly using @synthesize is no longer necessary in most cases unless customization is required.
  • It’s helpful when you need to name the backing ivar differently or write custom getter/setter logic while still using automatic property synthesis.

Question: How do you handle exceptions in Objective-C?

Answer:

In Objective-C, exceptions are used to signal errors or unusual conditions that occur during the execution of a program. Exception handling in Objective-C follows a try-catch mechanism, similar to other object-oriented programming languages like Java or C#. However, the way exceptions are used in Objective-C is quite different from languages like Swift, where error handling is based on error objects and do-catch blocks.

Here’s a detailed explanation of how exceptions work in Objective-C, including the @try, @catch, @finally, and @throw directives.


1. Exception Handling Basics:

In Objective-C, exceptions are raised using the @throw directive, and they are caught and handled using the @try, @catch, and @finally blocks. Here is the general structure:

@try {
    // Code that might raise an exception
} @catch (NSException *exception) {
    // Code that handles the exception
} @finally {
    // Code that is executed whether an exception is thrown or not
}
  • @try: This block contains the code that might raise an exception. You write the code that could potentially fail here (for example, an array access that could go out of bounds, or invalid data being processed).

  • @catch: This block catches the exception and handles it. The exception object is passed as an argument to the @catch block. The most common exception type is NSException, but you can create custom exception classes.

  • @finally: This block is optional. It contains code that will be executed regardless of whether an exception was thrown or not. It’s typically used for cleanup code, such as releasing resources or closing files, that should happen whether an exception occurred or not.


2. Raising an Exception: @throw

To raise an exception, you use the @throw directive. An exception is an instance of the NSException class or its subclasses.

@throw [NSException exceptionWithName:@"MyCustomException" 
                               reason:@"Something went wrong" 
                             userInfo:nil];

In this example:

  • The exception class is NSException.
  • The exception name is a string (@"MyCustomException").
  • The reason is a string describing why the exception occurred.
  • The userInfo is an optional dictionary that can contain additional information (usually nil).

3. Example of Exception Handling:

Here’s an example where we handle an exception by catching it:

- (void)divideNumbers:(NSInteger)numerator denominator:(NSInteger)denominator {
    @try {
        if (denominator == 0) {
            @throw [NSException exceptionWithName:@"DivisionByZeroException"
                                           reason:@"Denominator cannot be zero."
                                         userInfo:nil];
        }
        NSInteger result = numerator / denominator;
        NSLog(@"Result: %ld", (long)result);
    }
    @catch (NSException *exception) {
        NSLog(@"Exception: %@, Reason: %@", exception.name, exception.reason);
    }
    @finally {
        NSLog(@"Finally block executed, regardless of exception.");
    }
}

In this example:

  • If the denominator is zero, an exception is raised with a custom exception name (DivisionByZeroException).
  • If an exception is thrown, it is caught in the @catch block, where we print the exception’s name and reason.
  • The @finally block is executed after the @catch block, regardless of whether an exception was raised or not. This is useful for cleanup tasks.

4. Handling Specific Exception Types:

You can catch different types of exceptions in different @catch blocks. This allows you to handle different exceptions in a more specific way.

@try {
    // Code that may raise exceptions
} @catch (NSException *exception) {
    if ([exception.name isEqualToString:@"DivisionByZeroException"]) {
        NSLog(@"Caught division by zero error: %@", exception.reason);
    } else {
        NSLog(@"Caught an unknown exception: %@", exception.reason);
    }
} @finally {
    NSLog(@"Finally block executed.");
}

In this example, we check if the exception is a specific type (e.g., DivisionByZeroException) and handle it accordingly.


5. NSException Class:

The NSException class is the base class for exceptions in Objective-C. It provides the following key properties:

  • name: The name of the exception (typically a string).
  • reason: A message that describes the reason the exception was raised.
  • userInfo: An optional dictionary that may contain additional information (e.g., custom data) related to the exception.

You can also create custom exceptions by subclassing NSException:

@interface MyCustomException : NSException
@end

@implementation MyCustomException
@end

You can then raise this custom exception in the same way:

@throw [[MyCustomException alloc] initWithName:@"MyCustomException" 
                                        reason:@"A custom error occurred" 
                                      userInfo:nil];

6. Using Exception Handling with ARC:

Exception handling works seamlessly with Automatic Reference Counting (ARC). However, it is important to note that if an exception is raised, any autoreleased objects in the @try block will still be properly released during the exception unwinding process. In other words, ARC ensures that memory is properly managed, even in the event of an exception.


7. Best Practices for Using Exceptions:

  • Exception as an Exceptional Case: Use exceptions for unexpected errors or unrecoverable conditions (e.g., dividing by zero, out-of-bounds access). They should not be used for regular error handling (such as handling invalid user input).

  • Avoid Overusing Exceptions: Exception handling should not be used as a regular control flow mechanism in your program. It is more expensive performance-wise compared to normal control flow mechanisms like if-else conditions or NSError handling.

  • Use @finally for Cleanup: Always use the @finally block to ensure that cleanup code is executed, regardless of whether an exception was thrown or not.


8. Handling Exceptions in Multithreading:

  • When using multithreading (e.g., NSThread, GCD), exceptions raised in a background thread will not be propagated to the main thread. You should be careful to catch exceptions within the background thread itself, or use appropriate mechanisms to pass errors back to the main thread for handling.

9. Exception Handling and iOS:

  • iOS applications do not use exceptions for regular error handling. Instead, NSError objects are used for error reporting. Exceptions are typically reserved for critical failures (e.g., attempting to access a nil object when an object is expected).
  • For user-generated errors or recoverable conditions (e.g., network failure, invalid data), NSError is preferred over exceptions.

Key Points Summary:

  • Use @try to wrap code that may raise exceptions.
  • Use @catch to handle exceptions.
  • Use @finally for cleanup that should run regardless of an exception.
  • Raise exceptions with @throw using instances of NSException or its subclasses.
  • Exceptions are for unexpected, exceptional situations and should not be used for regular error handling in Objective-C.
  • Modern Objective-C prefers NSError for error handling in most cases, while exceptions should be reserved for critical errors.

Conclusion:

In Objective-C, exceptions are handled using the @try, @catch, @finally, and @throw directives. While exception handling is a powerful tool, it is primarily intended for unexpected errors or unrecoverable conditions, and not for general error handling. For everyday error handling, you should use NSError instead.

Read More

If you can’t get enough from this article, Aihirely has plenty more related information, such as Objective-C interview questions, Objective-C interview experiences, and details about various Objective-C job positions. Click here to check it out.

Related Posts

Trace Job opportunities

Hirely, your exclusive interview companion, empowers your competence and facilitates your interviews.

Get Started Now