14个重要的iOS面试问题 *

Toptal收集了最好的iOS开发者和工程师可以回答的基本问题. 在我们社区的推动下,我们鼓励专家提交问题并提供反馈.

聘请顶级iOS开发者
Toptal logois an exclusive network of the top freelance software developers, designers, finance experts, product managers, 和世界上的项目经理. 顶级公司雇佣Toptal自由职业者来完成他们最重要的项目.

Interview Questions

1.

考虑以下几点 UITableViewCell constructor:

- (id)initWithStyle:(UITableViewCellStyle)样式reuseIdentifier:(NSString *)reuseIdentifier

的目的是什么 reuseIdentifier? What is the advantage of setting it to a non-nil value?

View answer

The reuseIdentifier is used to group together similar rows in an UITableView; i.e.,这些行仅在内容上不同,但在其他方面具有相似的布局.

A UITableView 通常会分配刚刚好吗 UITableViewCell objects to display the content visible in the table. If reuseIdentifier is set to a non-nil value, then when the table view is scrolled, UITableView will first attempt to reuse an already allocated UITableViewCell with the same reuseIdentifier. If reuseIdentifier 还没定, UITableView 会否强制分配新 UITableViewCell 对象,滚动到视图中,可能导致延迟的动画.

2.

有哪些不同的方法可以指定元素的布局 UIView?

View answer

Here are a few common ways to specify the layout of elements in a UIView:

  • 使用InterfaceBuilder,您可以添加 XIB file to your project, layout elements within it, and then load the XIB 在您的应用程序代码中(根据命名约定自动地或手动地). Also, using InterfaceBuilder you can create a storyboard 对于你的申请.

  • 您可以使用自己的代码 NSLayoutConstraints to have elements in a view arranged by Auto Layout.

  • You can create CGRects describing the exact coordinates for each element and pass them to UIView’s - (id) initWithFrame:(CGRect中)框架 method.

3.

两者的区别是什么 atomic and nonatomic properties? Which is the default for synthesized properties? 什么时候用一个vs. the other?

View answer

指定为 atomic are guaranteed to always return a fully initialized object. 这也是合成属性的默认状态, 这是一个很好的实践 atomic 为了避免混淆,如果您关闭它,您的属性将仍然是 atomic. This guarantee of atomic properties comes at a cost to performance, however. 如果您有一个属性,并且您知道检索未初始化的值不会有风险(例如.g. 如果对该属性的所有访问都已通过其他方式同步),则将其设置为 nonatomic 能提高你的表现吗.

申请加入Toptal的发展网络

并享受可靠、稳定、远程 自由iOS开发者职位

申请成为自由职业者
4.

假设您想要记录应用程序启动的时间, 所以你创建了一个类,在它的头文件中定义了一个全局变量: NSString *startTime;. Then, in the class’ implementation, you set the variable as follows:

+ (void)initialize {
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateStyle:NSDateFormatterNoStyle];
    [formatter setTimeStyle:NSDateFormatterMediumStyle];
    startTime = [formatter stringFromDate:[NSDate date]];
}

If you then added the following line to the 应用:didFinishLaunchingWithOptions: method in your AppDelegate:

NSLog(@"Application was launched at: %@", startTime);

what would you expect to be logged in the debugger console? 如何使其按预期工作?

View answer

调试器控制台将记录日志 应用程序在:(null)启动 because the global startTime 变量尚未设置. The initialize 一个Objective-C类的方法只在第一个消息被发送到那个类之前被调用. 另一方面,任何 load 由Objective-C类定义的方法将在类被添加到Objective-C运行时时被调用.

There are a couple different ways to solve this problem.

解决方法1:改变 initialize to load will yield the desired result (but be cautious about doing too much in load, as it may increase your application’s load time).

Way to Solve #2, 感谢评论者John, 还有另一种方法:在已创建的类上创建另一个类方法. 我们调用这个新方法 getStartTime,它所做的就是返回我们的全局 startTime object.

现在我们将NSLog行更改为:

NSLog(@"应用程序在:%@",[OurCreatedClass getStartTime]);

因为我们现在要向 OurCreatedClass, its initialize 将被调用,设置 startTime. The getStartTime method will then ,并返回开始时间!

5.

考虑下面的代码:

#进口”TTAppDelegate.h"

@interface TTParent: NSObject

@property (atomic) NSMutableArray *children;

@end

@ implementation TTParent
@end

@interface TTChild: NSObject

@property (atomic) TTParent *parent;

@end

@ implementation TTChild
@end

@ implementation TTAppDelegate

- (BOOL)application:(UIApplication *)application
  didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    TTParent *parent = [[TTParent alloc] init];
    parent.children = [[NSMutableArray alloc] init];
    for (int i = 0; i < 10; i++) {
        TTChild *child = [[TTChild alloc] init];
        child.parent = parent;
        [parent.孩子addObject:孩子);
    }
    return YES;
}
@end

What is the bug in this code and what is its consequence? How could you fix it?

View answer

This is a classic example of a retain cycle. 父节点将保留 children 数组,并且数组将保留每个 TTChild object added to it. 创建的每个子对象将 also retain its parent, so that even after the last external reference to parent 是否清除,保留计数 parent will still be greater than zero and it will not be removed.

为了解决这个问题,需要将子对象对父对象的引用声明为 weak 参考资料如下:

@interface TTChild: NSObject

@property (weak, atomic) TTParent *parent;

@end

弱引用不会增加目标的保留计数,并将设置为 nil 当目标最终被摧毁时.

Note:

For a more complicated variation on this question, 您可以考虑在数组中保持对彼此引用的两个对等点. 在这种情况下,您需要进行替换 NSArray/NSMutableArray with an NSPointerArray declared as:

NSPointerArray *weakRefArray = [[NSPointerArray alloc] initWithOptions: NSPointerFunctionsWeakMemory];

since NSArray normally stores a strong reference to its members.

6.

识别以下代码中的bug:

@interface TTWaitController : UIViewController

@property (strong, nonatomic) UILabel *alert;

@end

@ implementation TTWaitController

- (void)viewDidLoad
{
    CGRect frame = CGRectMake(20, 200, 200, 20);
    self.alert = [[UILabel alloc] initWithFrame:frame];
    self.alert.text = @“请等10秒...";
    self.alert.textColor = [UIColor whiteColor];
    [self.视图addSubview:自我.alert];

    NSOperationQueue *waitQueue = [[NSOperationQueue alloc] init];
    [waitQueue addOperationWithBlock: ^ {
        [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];
        self.alert.text = @"Thanks!";
    }];
}

@end

@ implementation TTAppDelegate

- (BOOL)application:(UIApplication *)application
  didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[uisscreen mainScreen] bounds]];
    self.window.rootViewController = [[TTWaitController alloc] init];
    [self.窗口makeKeyAndVisible];
    return YES;
}

如何解决这个问题?

View answer

当上面的代码使用分派工作时 NSOperationQueue’s method addOperationWithBlock,不能保证正在排队的块将在主线程上执行. 的内容 UILabel is being updated within the body of the block. 未在主线程上执行的UI更新可能导致未定义的行为. 在出现任何问题之前,此代码可能会正常工作很长一段时间, 但UI更新应该 always 发生在主线程上.

修复潜在问题的最简单方法是更改块体,以便使用主线程队列重新排队,如下所示:

[waitQueue addOperationWithBlock: ^ {
    [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        self.alert.text = @"Thanks!";
    }];
}];
7.

“app ID”和“bundle ID”有什么区别?它们各自的用途是什么?

View answer

An App ID 由两部分组成的字符串是否用于识别来自单个开发团队的一个或多个应用程序. 该字符串由一个Team ID和一个bundle ID搜索字符串组成,其中包含一个句点(.)将两部分分开. 团队ID由苹果提供,是特定开发团队的唯一ID, 而bundle ID搜索字符串是由开发者提供的,用来匹配单个应用的bundle ID或者一组应用的bundle ID.

因为大多数人认为App ID是一个字符串,他们认为它可以与Bundle ID互换. 之所以会出现这种情况,是因为一旦在Member Center中创建了App ID, 你只使用与应用Bundle的Bundle ID匹配的App ID前缀.

bundle ID唯一地定义了每个App. 它是在Xcode中指定的. 一个Xcode项目可以有多个目标,因此可以输出多个应用. 一个常见的用例是,一个应用同时拥有精简版/免费版和专业版/完整版,或者以多种方式打上品牌.

8.

什么是“强”和“弱”引用? 为什么它们很重要?如何使用它们来帮助控制内存管理和避免内存泄漏?

View answer

By default, 任何指向另一个对象的变量都是通过所谓的“强”引用来实现的. A retain cycle occurs when two or more objects have reciprocal strong references (i.e.,彼此之间的强引用). 任何这样的物体 never be destroyed by ARC (iOS的自动引用计数). 即使应用程序中的所有其他对象都释放了这些对象的所有权, these objects (and, in turn, 任何引用它们的对象都将凭借这些相互强引用而继续存在. 因此,这将导致内存泄漏.

因此,应该尽可能避免对象之间的相互强引用. 但是,当它们是必要的时候,可以使用一种方法来避免这种类型的内存泄漏 weak references. 声明两个引用中的一个为 weak will break the retain cycle and thereby avoid the memory leak.

To decide which of the two references should be weak, 可以把保留循环中的对象看作是父子关系. 在这种关系中,父节点应该保持一个强引用.e.(所有权)的子对象,但是子对象不应该保持一个强引用(例如.e.(母公司的所有权).

例如,如果你有 Employer and Employee objects, 它们相互引用, you would most likely want to maintain a strong reference from the Employer to the Employee 对象的弱引用 Employee to thr Employer.

9.

Describe 管理对象上下文 以及它提供的功能.

View answer

A 管理对象上下文 的实例 NSManagedObjectContext)基本上是应用程序中用于(大概)相关对象集合的临时“记事本”. 这些对象共同表示一个或多个持久存储的内部一致视图. A single managed object instance exists in one and only one context, but multiple copies of an object can exist in different contexts.

您可以将托管对象上下文看作一个智能的记事本. When you fetch objects from a persistent store, 您将临时副本带到草稿簿上,在那里它们形成对象图(或对象图集合)。. You can then modify those objects however you like. 但是,除非实际保存这些更改,否则持久存储将保持不变.

提供的关键功能 管理对象上下文 includes:

  • 生命周期管理. 上下文提供验证、反向关系处理和撤销/重做. 通过上下文,您可以从持久存储中检索或“获取”对象, 对这些对象进行更改, 然后要么放弃更改,要么将它们提交回持久存储. 上下文负责监视其对象中的更改,并维护一个撤消管理器,以便您可以对撤消和重做进行细粒度控制. You can insert new objects and delete ones you have fetched, and commit these modifications to the persistent store.
  • Notifications. 上下文在不同的点发布通知,这些通知可以在应用程序的其他地方监控.
  • Concurrency. Core Data使用线程(或序列化队列)限制来保护托管对象和托管对象上下文. In OS X v10.7及以上版本和iOS v5.0 and later, 创建上下文时,您可以指定使用上下文的并发模式 initWithConcurrencyType:.

For more information, see the iOS Developer Library Core Data Basics or the NSManagedObjectContext参考.

10.

比较和对比在OS X和iOS中实现并发的不同方式.

View answer

There are basically three ways of achieving concurrency in iOS:

  • threads
  • dispatch queues
  • operation queues

The 线程的缺点 它们是否将创建可伸缩解决方案的负担转嫁给了开发人员. 您必须决定要创建多少线程,并根据条件的变化动态地调整这个数字. Also, 应用程序承担了与创建和维护它所使用的线程相关的大部分成本.

因此,OS X和iOS更倾向于采用异步设计方法来解决并发问题,而不是依赖于线程.

One of the technologies for starting tasks asynchronously is 中央调度(GCD) that relegates thread management down to the system level. 开发人员所要做的就是定义要执行的任务,并将它们添加到适当的 dispatch queue. GCD负责创建所需的线程并调度在这些线程上运行的任务.

All dispatch queues are first-in, 先出(FIFO)数据结构, so tasks are always started in the same order that they are added.

An operation queue 可可粉是否相当于 并发调度队列 并由 NSOperationQueue class. 与调度队列不同, 操作队列并不局限于以FIFO顺序执行任务,并且支持为任务创建复杂的执行顺序图.

11.

Will the code below log “areEqual” or “areNotEqual”? Explain your answer.

NSString *firstUserName = @"nick";
NSString *secondUserName = @"nick";

if (firstUserName == secondUserName)
{
  NSLog(@"areEqual");
}
else
{
  NSLog(@“areNotEqual”);
}
View answer

代码将输出“areEqual”.

While one might think this is obvious, it’s not. Here’s why:

比较指针值相当于检查它们是否指向同一个对象. 指针将具有相同的值 if and only if 它们实际上指向完全相同的对象(而指向不同对象的指针不会有相同的值), even if the objects they point to have the same value).

在上面的代码片段中, firstUserName and secondUserName 每个指针都指向字符串对象吗. One could easily assume that they are pointing to different 字符串对象,尽管它们指向的对象都具有相同的值. 但是,iOS编译器会优化对具有相同值(i.e., 它重用它们,而不是冗余地分配相同的字符串对象), 因此,两个指针实际上都指向同一个地址,因此条件的计算结果为真.

12.

List and explain the different types of iOS Application States.

View answer

iOS应用的状态如下:

  • Not running 状态:应用程序尚未启动或正在运行,但被系统终止.
  • Inactive state:应用程序在前台运行,但目前没有接收事件. (它可能正在执行其他代码.)一个应用程序通常只在过渡到另一个状态时短暂地停留在这个状态. 只有当用户锁定屏幕或系统提示用户响应某些事件(例如来电或短信)时,它才会处于非活动状态。.
  • Active state: The app is running in the foreground and is receiving events. This is the normal mode for foreground apps.
  • Background state: The app is in the background and executing code. Most apps enter this state briefly on their way to being suspended. 但是,请求额外执行时间的应用程序可能会在一段时间内保持这种状态. In addition, 一个应用程序被直接启动到后台进入这个状态,而不是非活动状态.
  • Suspended state:当挂起时,应用程序保留在内存中,但不执行任何代码. 当出现低内存状态时, 系统可能会在没有通知的情况下清除挂起的应用,为前台应用腾出更多空间.
13.

考虑以下两种方法:

  1. 应用范围:willFinishLaunchingWithOptions
  2. 应用范围:didFinishLaunchingWithOptions

这些方法的用途是什么?它们之间的区别是什么?

View answer

Both methods are present in the AppDelegate.swift file. 它们用于在应用启动时为应用添加功能.

The difference between these two methods are as follows:

  1. 应用范围:willFinishLaunchingWithOptions这个方法是你的应用程序在启动时第一次执行代码的机会.
  2. 应用范围:didFinishLaunchingWithOptions这个方法允许你在你的应用程序显示给用户之前执行任何最后的初始化.
14.

渲染选项的作用是什么 JSONSerialization?

View answer
  1. 可变容器:数组和字典是作为可变对象创建的,而不是常量.
  2. mutable叶子:JSON对象图中的叶子字符串是作为变量字符串的实例创建的.
  3. allowFragments:解析器应该允许不是数组或字典实例的顶级对象.

There is more to interviewing than tricky technical questions, 所以这些只是作为一个指南. 并不是每一个值得雇佣的“A”候选人都能回答所有的问题, nor does answering them all guarantee an “A” candidate. 一天结束的时候, hiring remains an art, a science — and a lot of work.

Why Toptal

厌倦了面试候选人? 不知道该问什么才能让你得到一份好工作?

让Toptal为你找到最合适的人.

聘请顶级iOS开发者

我们的独家iOS开发者网络

希望找到一份iOS开发者的工作?

让Toptal为你找到合适的工作.

申请成为iOS开发者

工作机会从我们的网络

提出面试问题

Submitted questions and answers are subject to review and editing, 并可能会或可能不会选择张贴, 由Toptal全权决定, LLC.

*所有字段均为必填项

寻找iOS开发者?

Looking for iOS Developers? 看看Toptal的iOS开发者.

Vladimir Mitrovic

自由iOS开发者
SerbiaToptal的自由iOS开发者 Since November 1, 2015

Vladimir has been a professional developer for eleven years. 最近,他一直专注于增强现实、计算设计和计算机图形学. 他设计过复杂的软件系统,做过用户体验设计,领导过团队,还在会议上发表过演讲. 他重视简单和效率,喜欢开发互动产品,不管平台是什么.

Show More

Christina Lin

自由iOS开发者
CanadaToptal的自由iOS开发者 Since March 6, 2018

Christina is a senior QA engineer with expertise in test automation, test management, and manual testing for web and mobile applications. 她在几个项目中使用Selenium WebDriver从头创建了测试自动化框架,并使用REST-assured和Postman自动化了API测试. With experience in both private and public sectors, 克里斯蒂娜曾与大型, medium-size, 小型创业公司.

Show More

Dan Napierski

自由iOS开发者
United StatesToptal的自由iOS开发者 Since April 28, 2016

Dan是一名专注于区块链技术应用的软件架构师和技术专家. 他拥有多年的专业咨询服务经验,为从初创公司到跨国公司的客户提供服务. 他擅长将严格的测试和防弹代码引入棘手的工程挑战. He has deep expertise in many aspects of artificial intelligence, blockchain, machine learning, and automation.

Show More

Toptal Connects the Top 3% 世界各地的自由职业人才.

加入Toptal社区.

Learn more