Skip to content

Instantly share code, notes, and snippets.

@appleios
Forked from arturlector/ios-questions-interview.md
Last active September 5, 2022 03:52
Show Gist options
  • Save appleios/f97e289afcf69a3428f2 to your computer and use it in GitHub Desktop.
Save appleios/f97e289afcf69a3428f2 to your computer and use it in GitHub Desktop.

Вопросы на собеседование iOS разработчика (дополненное издание):

  • Inheritance is redeclaration of variables and functions in a subscope.
  • Representitives of things do not share relationships of the things they represent.

Links:

Basic Animation:

CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = @"position.x";
animation.fromValue = @77;
animation.toValue = @455;
animation.duration = 1;

[rocket.layer addAnimation:animation forKey:@"basic"];
// strong nonatomic
- (void)setObj:(id)newObj {
  if (newObj != _obj) {
    [newObj retain];
    [_obj release];
    _obj = newObj;
  }
}

// atomic 
- (void)setObj:(id)newObj {
  synchronized(self) {
    ...
  }
}
// problem if we have another setObj2, which is also atomic, and we are colling that setter from here => deadlock
  • Changing the Layer Object Associated with a View. Layer-backed views create an instance of the CALayer class by default, and in most cases you might not need a different type of layer object. However, Core Animation provides different layer classes, each of which provides specialized capabilities that you might find useful. Choosing a different layer class might enable you to improve performance or support a specific type of content in a simple way. For example, the CATiledLayer class is optimized for displaying large images in an efficient manner.

General:

Что такое инкапсуляция? Что такое нарушение инкапсуляции?

Инкапсуляция - скрытие деталей реализации. Но т.к. классы показывают всем наши переменные мы вынуждены использовать hack: private, public, protected.

OO weakened encapsulation. Cuz in C we had perfect encapsulation, and in OO the compiler is telling you that you'r not allowed to do that and that.

Inheritance

We could do inh. in C.

/* An animal --------------------------------------------------------------*/
typedef struct AnimalData {
    int weight;
    const char *name;
} AnimalData;

typedef struct Animal {
    union {
        AnimalData animal;
    } base;
} Animal;

/* This macro perfoms like a typesafe cast */
#define getAnimal(self) (&(self)->base.animal)

/* Accessor macros (syntactic-sugar) */
#define weight(self)    (getAnimal(self)->weight)
#define name(self)      (getAnimal(self)->name)

/* An insect is an animal -------------------------------------------------*/
typedef struct InsectData {
    union {
        AnimalData animal; // superclass data
    } base;
    int exoskeleton;
} InsertData;

typedef struct Insect {
    union {
        InsertData insect;
        AnimalData animal;
    } base;
} Insect;

#define getInsect(self)   (&(self)->base.insect)
#define exoskeleton(self) (getInsect(self)->exoskeleton)

void main(void)
{
    ANIMAL unknown;
    INSECT beetle;

    /* A generic animal */
    weight(&unknown) = 1;
    name(&unknown) = "unknown";

    /* A beetle with a thick exoskeleton */
    weight(&beetle) = 1;
    name(&beetle) = "Herbie";
    exoskeleton(&beetle) = 100;
}
Diamond Problem

The "diamond problem" (sometimes referred to as the "deadly diamond of death") is an ambiguity that arises when two classes B and C inherit from A, and class D inherits from both B and C. If there is a method in A that B and C have overridden, and D does not override it, then which version of the method does D inherit: that of B, or that of C?

Solution:

C++ by default follows each inheritance path separately, so a D object would actually contain two separate A objects, and uses of A's members have to be properly qualified.

  • If the inheritance from A to B and the inheritance from A to C are both marked "virtual" (for example, class B : virtual public A), C++ takes special care to only create one A object, and uses of A's members work correctly.
  • If virtual inheritance and nonvirtual inheritance are mixed, there is a single virtual A and a nonvirtual A for each nonvirtual inheritance path to A. C++ requires stating explicitly which parent class the feature to be used is invoked from i.e. Worker::Human.Age.
  • C++ does not support explicit repeated inheritance since there would be no way to qualify which superclass to use (i.e. having a class appear more than once in a single derivation list class Dog : public Animal, Animal).
  • C++ also allows a single instance of the multiple class to be created via the virtual inheritance mechanism (i.e. Worker::Human and Musician::Human will reference the same object).
Java does not have Multiple inh. but have invented Interface

Interface is just and abstract class. And you only allowed to multily inherit is interface. (its a lazy hack!)

Что такое полиморфизм?

В языках программирования и теории типов полиморфизмом называется способность функции обрабатывать данные разных типов.

Существует несколько разновидностей полиморфизма. Две наиболее различных из них были описаны Кристофером Стрэчи в 1967 году: это ad hoc полиморфизм и параметрический полиморфизм.

  • Параметрический полиморфизм подразумевает исполнение одного и того же кода для всех допустимых типов аргументов (C++ templates/iOS Generics);
  • ad hoc полиморфизм подразумевает исполнение потенциально разного кода для каждого типа или подтипа аргумента: перегрузка, приведение типов. При ad hoc полиморфизме нет единого систематичного способа вывести тип результата из типа аргументов, и хотя возможно построение определённого набора правил для сужения спектра его поиска, но эти правила по своей природе являются спонтанными как по содержанию, так и по контексту применения.

Действительно, ad hoc полиморфизм не является истинным полиморфизмом. Перегрузка функций даёт не ''значение, имеющее множество типов'', а ''символ, имеющий множество типов'', но значения, идентифицируемые этим символом, имеют разные (потенциально не совместимые) типы.

Аналогично, приведение типов не является истинным полиморфизмом: кажется, будто оператор принимает значения множества типов, но значения должны быть преобразованы к некоторому представлению до того, как он сможет их использовать, так что на самом деле оператор работает лишь над одним типом (то есть имеет один тип). Более того, тип возвращаемого значения здесь не зависит от типа входного параметра, как в случае параметрического полиморфизма.

В отличие от перегрузки и приведения типов, полиморфизм подтипов Subtyping является истинным полиморфизмом: объектами подтипа можно манипулировать единообразно, как если бы они они принадлежали к своим супертипам (но сказанное не верно для языков, реализующих ''полиморфизм при наследовании'' посредством приведения типов, как в случае С++).

Наиболее чистым является параметрический полиморфизм: один и тот же объект или функция может единообразно использоваться в разных контекстах типизации без изменений, приведений типов или любых других проверок времени исполнения или преобразований. Однако, для этого требуется некое единообразное представление данных (например, посредством указателей).

  • Ad hoc полиморфизм (рус. специальный полиморфизм) поддерживается во многих языках посредством перегрузки функций и методов, а в слабо типизированных — также посредством приведения типов.
  • В динамически типизируемых языках ситуация может быть более сложной, так как выбор требуемой функции для вызова может быть осуществлён только во время исполнения программы.
  • Перегрузка представляет собой синтаксический механизм, позволяющий по единому идентификатору вызывать разные функции.
  • Приведение типов представляет собой семантический механизм, осуществляемый для преобразования фактического типа аргумента к ожидаемому функцией, при отсутствии которого произошла бы ошибка типизации. То есть, при вызове функции с приведением типа происходит исполнение различного кода для различных типов (предваряющего вызов самой функции).
Polymorphism in C
void main(void) {
  int c;
  while((c=getchar()) != EOF) {
    putchar(c);
  }
}

It works no matter wht file stdin is (a keyboard, a file on disk, etc).

Every IO driver have to implement 5 functions open/close/seek/read/write. OS takes pointers to these functions and puts it into a table.

In Java we don't need pointers to functions, because it has polymorphism from inhertance.

SOLID

Sympyoms of rotting design:

  • Rigidity is the tendency for software to be difficult to change, even in simple ways. Every change causes a cascade of subsequent changes in dependent modules.
  • Fragility. Closely related to rigidity is fragility. Fragility is the tendency of the software to break in many places every time it is changed. Often the breakage occurs in areas that have no conceptual relationship with the area that was changed.
  • Immobility is the inability to reuse software from other projects or from parts of the same project.
  • Viscosity. Viscosity comes in two forms: viscosity of the design, and viscosity of the environment. When faced with a change, engineers usually find more than one way to make the change. Some of the ways preserve the design, others do not (i.e. they are hacks.) When the design preserving methods are harder to employ than the hacks, then the viscosity of the design is high. It is easy to do the wrong thing, but hard to do the right thing. Viscosity of environment comes about when the development environment is slow and inefficient. For example, if compile times are very long, engineers will be tempted to make changes that don’t force large recompiles, even though those changes are not optiimal from a design point of view. If the source code control sys- tem requires hours to check in just a few files, then engineers will be tempted to make changes that require as few check-ins as possible, regardless of whether the design is preserved.

Dependencies:

In module M we do #import "N" in order to use function f:

   -----       -----
  |  M  | --> | N.f | 
   -----       -----

But in OO language we can put f in interface I. And make M to call I.f, and N derives interface I:

   -----       -----       -----
  |  M  | --> | I.f | <|-- |  N  |
   -----       -----       -----

Now compile time dependency is poining agains a flow of controll.

This gives you an absolute controll over your dependency structure. This is what polymorphism gives you.

Single Resposibility Principle

A class should only one reason to change.

Word resposibility means here the reason to change, not a function. You should ask your for each function:

  • how many reasons (sources of) change does it have?
  • if there were a bug, which person will find about it first?
Open Close Principle

We should write our modules so that they can be extended, without requiring them to be modified.

This may sound contradictory, but there are several techniques for achieving the OCP on a large scale. All of these techniques are based upon abstraction.

With dynamic polymorphism:

class Modem {
public:
  virtual void Dial(const string& pno) = 0; virtual void Send(char) = 0;
  virtual char Recv() = 0;
  virtual void Hangup() = 0;
};
void LogOn(Modem& m, string& pno, string& user, string& pw)
{
  m.Dial(pno);
  // you get the idea.
}

Even if the OCP cannot be fully achieved, even partial OCP compliance can make dramatic improvements in the structure of an application. It is always better if changes do not propogate into existing code that already works. If you don’t have to change working code, you aren’t likely to break it.

Liskov Substitution Principle

Subclasses should be substitutable for their base classes.

Derived classes should be substitutable for their base classes. That is, a user of a base class should continue to function properly if a derivative of that base class is passed to it.

Design by Contract. Restating the LSP, we can say that, in order to be substitutable, the contract of the base class must be honored by the derived class.

Restating the LSP once again, this time in terms of the contracts, a derived class is substitutable for its base class if:

  1. Its preconditions are no stronger than the base class method.
  2. Its postconditions are no weaker than the base class method.

Or, in other words, derived methods should expect no more and provide no less.

Dependency Inversion Principle

Depend upon Abstractions. Do not depend upon concretions.

Object Creation. One of the most common places that designs depend upon con- crete classes is when those designs create instances. However, there is an elegant solution to this problem named Abstract Factory.

Interface Segregation Principle

Many client specific interfaces are better than one general purpose interface.

The essence of the principle is quite simple. If you have a class that has several cli- ents, rather than loading the class with all the methods that the clients need, create specific interfaces for each client and multiply inherit them into the class.

Расскажите о паттерне MVC. Чем отличается пассивная модель от активной?

В оригинальной концепции была описана сама идея и роль каждого из элементов: модели, представления и контроллера. Но связи между ними были описаны без конкретизации. Кроме того, различали две основные модификации:

  • Пассивная модель — модель не имеет никаких способов воздействовать на представление или контроллер, и используется ими в качестве источника данных для отображения. Все изменения модели отслеживаются контроллером и он же отвечает за перерисовку представления, если это необходимо. Такая модель чаще используется в структурном программировании, так как в этом случае модель представляет просто структуру данных, без методов их обрабатывающих.
  • Активная модель — модель оповещает представление о том, что в ней произошли изменения, а представления, которые заинтересованы в оповещении, подписываются на эти сообщения. Это позволяет сохранить независимость модели как от контроллера, так и от представления.

Классической реализацией концепции MVC принято считать версию именно с активной моделью.

Apple MVC: alt tag

Реализация синглтона (Singleton) в ARC и в non-ARC?

Src.

Arc:

+ (id)sharedManager {
    static MyManager *sharedMyManager = nil;
    @synchronized(self) {
        if (sharedMyManager == nil)
            sharedMyManager = [[self alloc] init];
    }
    return sharedMyManager;
}

Non-arc:

#import "MyManager.h"

static MyManager *sharedMyManager = nil;

@implementation MyManager

@synthesize someProperty;

#pragma mark Singleton Methods
+ (id)sharedManager {
  @synchronized(self) {
      if(sharedMyManager == nil)
          sharedMyManager = [[super allocWithZone:NULL] init];
  }
  return sharedMyManager;
}
+ (id)allocWithZone:(NSZone *)zone {
  return [[self sharedManager] retain];
}
- (id)copyWithZone:(NSZone *)zone {
  return self;
}
- (id)retain {
  return self;
}
- (unsigned)retainCount {
  return UINT_MAX; //denotes an object that cannot be released
}
- (oneway void)release {
  // never release
}
- (id)autorelease {
  return self;
}
- (id)init {
  if (self = [super init]) {
      someProperty = [[NSString alloc] initWithString:@"Default Property Value"];
  }
  return self;
}
- (void)dealloc {
  // Should never be called, but just here for clarity really.
  [someProperty release];
  [super dealloc];
}

@end

Какие еще паттерны знаете?

Порождающие, создания объектов (Object Creation)
  • Prototype (NSCopying). Any subclass of NSObject needs to implement the NSCopying protocol and its method, (id)copyWithZone:(NSZone *)zone. NSObject has an instance method called (id)copy. The default copy method calls [self copyWithZone:nil]. For subclasses that adopt the NSCopying protocol, that method needs to be implemented, otherwise it will raise an exception. In iOS, this method retains the newly cloned object before returning it. The invoker of this method is responsible for releasing the returned object.
  • Factory Method. A common example of using this architecture is NSNumber. For example, a message [NSNumber numberWithBool:YES] will get you an instance of NSNumber’s subclass (NSCFBoolean) that contains the Boolean value provided to the class factory method.
  • Abstract Factory. Provides an interface for creating families of related or dependent objects without specifying their concrete classes. Good use: BrandingFactory - for each brand we have different factory that supplies fonts/images/etc.
  • Main difference between Abstract Factory and Abstract Method: in AM we can add support for new type of product without change to a AM Interface; in AF we need to change AF Interface.
  • Builder. Separates the construction of a complex object from its representation so that the same construction process can create different representations.
  • Singleton. Ensures a class has only one instance, and provide a global point of access to it. (UIApplication, UIAccelerometer, NSFileManager)
Interface Adaptation
  • Adapter pattern. Converts the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.
  • Delegation in Cocoa - a concrete class that implements the protocol will be an adapter in that case. The reason I said the Delegation pattern is mainly the Adapter pattern is that the Delegation mechanism can also fulfill the intents of some other design patterns, such as the Decorator pattern (Chapter 16). The implementation of the Delegation pattern in the Cocoa Touch framework can sometimes mix with other design patterns. For example, some framework classes that implement the Delegation pattern are also part of the Template Method pattern (Chapter 18).
  • Bridge pattern. Decouples an abstraction from its implementation so that the two can vary independently.
  • Facade. Provides a unified interface to a set of interfaces in a system. Façade defines a higher-level interface that makes the subsystem easier to use.
Decoupling of Objects
  • Mediator. Defines an object that encapsulates how a set of objects interacts. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.
  • Observer. Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
Abstract Collection
  • Composite. Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.
  • Iterator. Provide a way to access to the elements of an aggregate object sequentially without exposing its underlying representation.
Behavioral Extension
  • Visitor. The Visitor pattern represents an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.
  • Decorator. Attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
  • Chain of Responsibility. To avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. It chains the receiving objects and passes the request along the chain until an object handles it.
Algorithm Encapsulation
  • Template Method. Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.
  • Strategy. Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
  • Command. Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
Performance and Object Access
  • Flyweight. Uses sharing to support large numbers of fine-grained objects efficiently.
  • Proxy. Provides a surrogate or placeholder for another object to control access to it.
State Of Object
  • Memento. Without violating encapsulation, capture and externalize an object’s internal state so that the object can be restored to this state later. (NSKeyedUnarchiver)
In Cocoa
  • Паттерны структурные (Structural): MVC, Decorator(Categories, Delegation), Adapter(Delegation), Facade, Composite
  • Патерны поведения и взаимодействия объектов (Behavioral): Observer(Notification, KVO), Memento(Archiving+UserDefaults), Chain of Recponsibility, Command(Target-Action mechanism)

Что такое responder chain?

When you design your app, it’s likely that you want to respond to events dynamically. For example, a touch can occur in many different objects onscreen, and you have to decide which object you want to respond to a given event and understand how that object receives the event.

When a user-generated event occurs, UIKit creates an event object containing the information needed to process the event. Then it places the event object in the active app’s event queue. For touch events, that object is a set of touches packaged in a UIEvent object. For motion events, the event object varies depending on which framework you use and what type of motion event you are interested in.

An event travels along a specific path until it is delivered to an object that can handle it.

  • First, the singleton UIApplication object takes an event from the top of the queue and dispatches it for handling. Typically, it sends the event to the app’s key window object, which passes the event to an initial object for handling. The initial object depends on the type of event.

  • Touch events. For touch events, the window object first tries to deliver the event to the view where the touch occurred. That view is known as the hit-test view. The process of finding the hit-test view is called hit-testing, which is described in Hit-Testing Returns the View Where a Touch Occurred.

  • Motion and remote control events. With these events, the window object sends the shaking-motion or remote control event to the first responder for handling. The first responder is described in The Responder Chain Is Made Up of Responder Objects.

The ultimate goal of these event paths is to find an object that can handle and respond to an event. Therefore, UIKit first sends the event to the object that is best suited to handle the event. For touch events, that object is the hit-test view, and for other events, that object is the first responder.

Hit-Testing Returns the View Where a Touch Occurred

iOS uses hit-testing to find the view that is under a touch. Hit-testing involves checking whether a touch is within the bounds of any relevant view objects. If it is, it recursively checks all of that view’s subviews. The lowest view in the view hierarchy that contains the touch point becomes the hit-test view. After iOS determines the hit-test view, it passes the touch event to that view for handling.

The hitTest:withEvent: method returns the hit test view for a given CGPoint and UIEvent. The hitTest:withEvent: method begins by calling the pointInside:withEvent: method on itself. If the point passed into hitTest:withEvent: is inside the bounds of the view, pointInside:withEvent: returns YES. Then, the method recursively calls hitTest:withEvent: on every subview that returns YES.

If the point passed into hitTest:withEvent: is not inside the bounds of the view, the first call to the pointInside:withEvent: method returns NO, the point is ignored, and hitTest:withEvent: returns nil. If a subview returns NO, that whole branch of the view hierarchy is ignored, because if the touch did not occur in that subview, it also did not occur in any of that subview’s subviews. This means that any point in a subview that is outside of its superview can’t receive touch events because the touch point has to be within the bounds of the superview and the subview. This can occur if the subview’s clipsToBounds property is set to NO.

Note: A touch object is associated with its hit-test view for its lifetime, even if the touch later moves outside the view.

The hit-test view is given the first opportunity to handle a touch event. If the hit-test view cannot handle an event, the event travels up that view’s chain of responders as described in The Responder Chain Is Made Up of Responder Objects until the system finds an object that can handle it.

The Responder Chain Is Made Up of Responder Objects

Many types of events rely on a responder chain for event delivery. The responder chain is a series of linked responder objects. It starts with the first responder and ends with the application object. If the first responder cannot handle an event, it forwards the event to the next responder in the responder chain.

A responder object is an object that can respond to and handle events. The UIResponder class is the base class for all responder objects, and it defines the programmatic interface not only for event handling but also for common responder behavior. Instances of the UIApplication, UIViewController, and UIView classes are responders, which means that all views and most key controller objects are responders. Note that Core Animation layers are not responders.

The first responder is designated to receive events first.

Note: Make sure that your app has established its object graph before assigning an object to be the first responder. For example, you typically call the becomeFirstResponder method in an override of the viewDidAppear: method. If you try to assign the first responder in viewWillAppear:, your object graph is not yet established, so the becomeFirstResponder method returns NO.

Events are not the only objects that rely on the responder chain. The responder chain is used in all of the following:

  • Touch events. If the hit-test view cannot handle a touch event, the event is passed up a chain of responders that starts with the hit-test view.
  • Motion events. To handle shake-motion events with UIKit, the first responder must implement either the motionBegan:withEvent: or motionEnded:withEvent: method of the UIResponder class, as described in Detecting Shake-Motion Events with UIEvent.
  • Remote control events. To handle remote control events, the first responder must implement the remoteControlReceivedWithEvent: method of the UIResponder class.
  • Action messages. When the user manipulates a control, such as a button or switch, and the target for the action method is nil, the message is sent through a chain of responders starting with the control view.
  • Editing-menu messages. When a user taps the commands of the editing menu, iOS uses a responder chain to find an object that implements the necessary methods (such as cut:, copy:, and paste:). For more information, see Displaying and Managing the Edit Menu and the sample code project, CopyPasteTile.
  • Text editing. When a user taps a text field or a text view, that view automatically becomes the first responder. By default, the virtual keyboard appears and the text field or text view becomes the focus of editing. You can display a custom input view instead of the keyboard if it’s appropriate for your app. You can also add a custom input view to any responder object. For more information, see Custom Views for Data Input.

UIKit automatically sets the text field or text view that a user taps to be the first responder; Apps must explicitly set all other first responder objects with the becomeFirstResponder method.

The Responder Chain Follows a Specific Delivery Path

If the initial object—either the hit-test view or the first responder—doesn’t handle an event, UIKit passes the event to the next responder in the chain. Each responder decides whether it wants to handle the event or pass it along to its own next responder by calling the nextResponder method.This process continues until a responder object either handles the event or there are no more responders.

The responder chain sequence begins when iOS detects an event and passes it to an initial object, which is typically a view. The initial view has the first opportunity to handle an event. Figure 2-2 shows two different event delivery paths for two app configurations. An app’s event delivery path depends on its specific construction, but all event delivery paths adhere to the same heuristics.

Figure 2-2 The responder chain on iOS

alt tag

For the app on the left, the event follows this path:

  • The initial view attempts to handle the event or message. If it can’t handle the event, it passes the event to its superview, because the initial view is not the top most view in its view controller’s view hierarchy.
  • The superview attempts to handle the event. If the superview can’t handle the event, it passes the event to its superview, because it is still not the top most view in the view hierarchy.
  • The topmost view in the view controller’s view hierarchy attempts to handle the event. If the topmost view can’t handle the event, it passes the event to its view controller.
  • The view controller attempts to handle the event, and if it can’t, passes the event to the window.
  • If the window object can’t handle the event, it passes the event to the singleton app object.
  • If the app object can’t handle the event, it discards the event.

The app on the right follows a slightly different path, but all event delivery paths follow these heuristics:

  • A view passes an event up its view controller’s view hierarchy until it reaches the topmost view.
  • The topmost view passes the event to its view controller.
  • The view controller passes the event to its topmost view’s superview.
  • Steps 1-3 repeat until the event reaches the root view controller.
  • The root view controller passes the event to the window object.
  • The window passes the event to the app object.

Important: If you implement a custom view to handle remote control events, action messages, shake-motion events with UIKit, or editing-menu messages, don’t forward the event or message to nextResponder directly to send it up the responder chain. Instead, invoke the superclass implementation of the current event handling method and let UIKit handle the traversal of the responder chain for you.

Как работают push нотификации?

alt tag

Objective-C, Foundation:

Опишите основные понятия ОО программирования в терминах Objective-C (интерфейс, реализация, свойства, протоколы, и т.д)
Что такое назначеный инициализатор (designated initializer), напишите любой элементарный инициализатор, почему он так выглядит? (имеется ввиду if (self = [super ...]))?

Implementing an Initializer

There are several critical rules to follow when implementing an init... method that serves as a class’s sole initializer or, if there are multiple initializers, its designated initializer (described in Multiple Initializers and the Designated Initializer):

  • Always invoke the superclass (super) initializer first.
  • Check the object returned by the superclass. If it is nil, then initialization cannot proceed; return nil to the receiver.
  • When initializing instance variables that are references to objects, retain or copy the object as necessary (in memory-managed code).
  • After setting instance variables to valid initial values, return self unless:
    • It was necessary to return a substituted object, in which case release the freshly allocated object first (in memory-managed code).
    • A problem prevented initialization from succeeding, in which case return nil.

It isn’t necessary to initialize all instance variables of an object explicitly, just those that are necessary to make the object functional. The default set-to-zero initialization performed on an instance variable during allocation is often sufficient. Make sure that you retain or copy instance variables, as required for memory management.

The requirement to invoke the superclass’s initializer as the first action is important. Recall that an object encapsulates not only the instance variables defined by its class but the instance variables defined by all of its ancestor classes. By invoking the initializer of super first, you help to ensure that the instance variables defined by classes up the inheritance chain are initialized first. The immediate superclass, in its initializer, invokes the initializer of its superclass, which invokes the main init... method of its superclass, and so on (see Figure 6-1). The proper order of initialization is critical because the later initializations of subclasses may depend on superclass-defined instance variables being initialized to reasonable values.

Some subclasses provide convenience initializers that supply default values to an initializer that takes the full complement of initialization parameters. This initializer is usually the designated initializer, the most important initializer of a class. For example, assume there is a Task class and it declares a designated initializer with this signature:

- (id)initWithTitle:(NSString *)aTitle date:(NSDate *)aDate;

The Task class might include secondary, or convenience, initializers that simply invoke the designated initializer, passing it default values for those parameters the secondary initializer doesn’t explicitly request. This example shows a designated initializer and a secondary initializer.

- (id)initWithTitle:(NSString *)aTitle {
    return [self initWithTitle:aTitle date:[NSDate date]];
}
 
- (id)init {
    return [self initWithTitle:@”Task”];
}

The designated initializer plays an important role for a class. It ensures that inherited instance variables are initialized by invoking the designated initializer of the superclass. It is typically the init... method that has the most parameters and that does most of the initialization work, and it is the initializer that secondary initializers of the class invoke with messages to self.

When you define a subclass, you must be able to identify the designated initializer of the superclass and invoke it in your subclass’s designated initializer through a message to super. You must also make sure that inherited initializers are covered in some way. And you may provide as many convenience initializers as you deem necessary. When designing the initializers of your class, keep in mind that designated initializers are chained to each other through messages to super; whereas other initializers are chained to the designated initializer of their class through messages to self.

Суть рантайма (Runtime), отправление сообщения;

Посылка сообщения транслируется в С-функцию с прототипом:

id objc_msgSend(id receiver, SEL method, ...);

Тип SEL, по сути, определен как char const *, но лучше воспринимать его как int, поскольку во время выполнения все селекторы индексируются целыми значениями согласно глобальной таблице селекторов.

alt tag

Пользуясь инвариантом isa объекта receiver (при использовании фреймворка Foundation, базового для Cocoa, все классы должны наследовать класс NSObject, поэтому наличие isa неизбежно), эта функция просматривает локальный список селекторов класса с целью определить, отвечает ли объект данного класса на сообщение method. Если такой селектор находится, то управление передается соответствующему методу класса, которому передается id объекта (указатель на его инварианты) и указанные после селектора параметры функции objc_msgSend(). Значение, возвращенное методом, отдается как результат посылки сообщения. Если у объекта-приемника данный селектор отсутствует, функции objc_msgSend() просматривает список селекторов его базового класса.

alt tag

При такой схеме вызов, например:

[receiver аddObject: otherObject];

Транслируется в:

objc_msgSend(receiver, 12, otherObject);

Так как в глобальной таблице селекторов 12 соответствует строке addObject:. Далее функция objc_msgSend() выполняет поиск по списку селекторов объекта receiver и, найдя его (пусть это объект класса NSArray, который реализовал метод с селектором 12), производит вызов типа:

addObject(receiver, otherObject);

Если искомого метода не оказывается, то система дает возможность для реабелитации.

Для этого нужно реализовать:

// is called first
- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
    NSMethodSignature* signature = [super methodSignatureForSelector:selector];
    if (!signature) {
        NSString *key = NSStringFromSelector(selector);
        id obj = [self valueForKey:key];
        if (obj) {
            signature = [self methodSignatureForSelector:@selector(valueForKey:)];
        }else{
            if ([self __quickTestForSetterMethod:key]) {
                NSArray *matches = [self __matchesOfSetterMethod:key];
                if ([matches count]>0) {
                    signature = [self methodSignatureForSelector:@selector(setValue:forKey:)];
                }
            }
        }
    }
    return signature;
}

// than 
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    NSString *key = NSStringFromSelector([anInvocation selector]);
    id obj = [self valueForKey:key];
    if (obj) {
        [anInvocation setArgument:&key atIndex:2];
        [anInvocation setSelector:@selector(valueForKey:)];
        [anInvocation invokeWithTarget:self];
    } else {
        if ([self __quickTestForSetterMethod:key]) {
            NSArray *matches = [self __matchesOfSetterMethod:key];
            NSString *newKey = [self __newKeyWithMatches:matches fromKey:key];
            if (newKey) {
                [anInvocation setArgument:&newKey atIndex:3];
                [anInvocation setSelector:@selector(setValue:forKey:)];
                [anInvocation invokeWithTarget:self];
            }
        } else {
            [super forwardInvocation:anInvocation];
        }
    }
}

- (BOOL)respondsToSelector:(SEL)aSelector
{
    if ( [super respondsToSelector:aSelector] )
        return YES;
    else {
        /* Here, test whether the aSelector message can     *
         * be forwarded to another object and whether that  *
         * object can respond to it. Return YES if it can.  */
    }
    return NO;
}
Объявление свойств (property), (retain, assign, nonatomic, readonly, copy).
  • С подвохом: вопрос о несуществующем параметре atomic, что он означает?
  • Как правильно реализовать сетер для свойства с параметром retain?
  • Вопрос о циклах в графах владения, и почему свойства delegate (предоставляющие доступ к делегату) обычно задаются как assign?

см. доки.

В чем разница между точечной нотацией и использованием квадратных скобок?
Что происходит когода мы пытаемся вызвать метод у nil указателя? Разница между nil и Nil?
Что такое селектор (selector, IMP)? Как его вызвать? как отложить вызов селектора? Что делать если селектор имеет много параметров? (NSInvocation)
  • Selector (typedef struct objc_selector *SEL): Selectors are used to represent the name of a method at runtime. A method selector is a C string that has been registered (or “mapped”) with the Objective-C runtime. Selectors generated by the compiler are automatically mapped by the runtime when the class is loaded .
  • Method (typedef struct objc_method *Method): An opaque type that represents a method in a class definition.
  • Implementation (typedef id (*IMP)(id, SEL, ...)): This data type is a pointer to the start of the function that implements the method. This function uses standard C calling conventions as implemented for the current CPU architecture. The first argument is a pointer to self (that is, the memory for the particular instance of this class, or, for a class method, a pointer to the metaclass). The second argument is the method selector. The method arguments follow.
Как запустить селектор во второстепенном (фоновом) потоке?
Как запустить поток? Что первым нужно сделать при запуске потока? (NSAutoreleasePool)

Applications that link in Objective-C frameworks typically must create at least one autorelease pool in each of their threads. If an application uses the managed model—where the application handles the retaining and releasing of objects—the autorelease pool catches any objects that are autoreleased from that thread.

If your application uses the managed memory model, creating an autorelease pool should be the first thing you do in your thread entry routine. Similarly, destroying this autorelease pool should be the last thing you do in your thread. This pool ensures that autoreleased objects are caught, although it does not release them until the thread itself exits. Listing below shows the structure of a basic thread entry routine that uses an autorelease pool.

- (void)myThreadMainRoutine
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Top-level pool
 
    // Do thread work here.
 
    [pool release];  // Release the objects in the pool.
}

Because the top-level autorelease pool does not release its objects until the thread exits, long-lived threads should create additional autorelease pools to free objects more frequently. For example, a thread that uses a run loop might create and release an autorelease pool each time through that run loop. Releasing objects more frequently prevents your application’s memory footprint from growing too large, which can lead to performance problems. As with any performance-related behavior though, you should measure the actual performance of your code and tune your use of autorelease pools appropriately.

NSThread* myThread = [[NSThread alloc] initWithTarget:self
                                        selector:@selector(myThreadMainMethod:)
                                        object:nil];
[myThread start];  // Actually create the thread

Note: An alternative to using the initWithTarget:selector:object: method is to subclass NSThread and override its main method. You would use the overridden version of this method to implement your thread’s main entry point.

Что такое runLoop, кодга он используется? (timers, nsurlconnection ...)

Run loops are part of the fundamental infrastructure associated with threads. A run loop is an event processing loop that you use to schedule work and coordinate the receipt of incoming events. The purpose of a run loop is to keep your thread busy when there is work to do and put your thread to sleep when there is none.

Run loop management is not entirely automatic. You must still design your thread’s code to start the run loop at appropriate times and respond to incoming events. Both Cocoa and Core Foundation provide run loop objects to help you configure and manage your thread’s run loop. Your application does not need to create these objects explicitly; each thread, including the application’s main thread, has an associated run loop object. Only secondary threads need to run their run loop explicitly, however. The app frameworks automatically set up and run the run loop on the main thread as part of the application startup process.

The app frameworks start the run loop of your application’s main thread automatically. If you create any secondary threads, you must configure the run loop and start it manually.

Before you run a run loop on a secondary thread, you must add at least one input source or timer to it. If a run loop does not have any sources to monitor, it exits immediately when you try to run it. For examples of how to add sources to a run loop, see Configuring Run Loop Sources.

+ (void)initialize {
    if (self == [ViewController class]) {
        NSThread* myThread = [[NSThread alloc] initWithTarget:self
                                                     selector:@selector(threadMain:)
                                                       object:nil];
        [myThread start];  // Actually create the thread
    
    }
}

+ (void)threadMain:(id)obj {
    @autoreleasepool {
        NSRunLoop *myRunLoop = [NSRunLoop currentRunLoop];
        
        NSThread *thread = [NSThread currentThread];
        
        // Create a run loop observer and attach it to the run loop.
        CFRunLoopObserverContext  context = {0, (__bridge void *)(thread), NULL, NULL, NULL};
        CFRunLoopObserverRef    observer = CFRunLoopObserverCreate(kCFAllocatorDefault,
                                                                   kCFRunLoopAllActivities, YES, 0, &myRunLoopObserver, &context);
        
        if (observer)
        {
            CFRunLoopRef    cfLoop = [myRunLoop getCFRunLoop];
            CFRunLoopAddObserver(cfLoop, observer, kCFRunLoopDefaultMode);
        }
        
        // Create and schedule the timer.
        [NSTimer scheduledTimerWithTimeInterval:0.25 target:self
                                       selector:@selector(doFireTimer:) userInfo:nil repeats:YES];
        
        [myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
    }
}

void myRunLoopObserver( CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info ) {
    NSLog(@"runloop observer");
}

+ (void)doFireTimer:(id)timer {
    NSLog(@"fire a timer");
}
Что такое делегат (delegate)? как его создать и использовать?
Как представлены структуры C (CGRect, CGSize, CGPoint) в Objective-C?
Чем объект Objective-c отличается от структуры С, что такое структура в С.
Какие существуют root классы в iOS? Для чего нужны root классы? Корневые классы: NSObject, NSProxy? Как работает proxy?

NSProxy is an abstract superclass defining an API for objects that act as stand-ins for other objects or for objects that don’t exist yet. Typically, a message to a proxy is forwarded to the real object or causes the proxy to load (or transform itself into) the real object. Subclasses of NSProxy can be used to implement transparent distributed messaging (for example, NSDistantObject) or for lazy instantiation of objects that are expensive to create.

NSProxy implements the basic methods required of a root class, including those defined in the NSObject protocol. However, as an abstract class it doesn’t provide an initialization method, and it raises an exception upon receiving any message it doesn’t respond to. A concrete subclass must therefore provide an initialization or creation method and override the forwardInvocation: and methodSignatureForSelector: methods to handle messages that it doesn’t implement itself. A subclass’s implementation of forwardInvocation: should do whatever is needed to process the invocation, such as forwarding the invocation over the network or loading the real object and passing it the invocation. methodSignatureForSelector: is required to provide argument type information for a given message; a subclass’s implementation should be able to determine the argument types for the messages it needs to forward and should construct an NSMethodSignature object accordingly. See the NSDistantObject, NSInvocation, and NSMethodSignature class specifications for more information.

(NSProxy, Как и зачем NSProxy нужен)

Можно ли создать свои рут классы и для чего?

It's actually a "trap" some people migrating from C# or Java style languages fall into. You simply don't specify a superclass when declaring your class i.e.

@interface MyNewRoot {
Class isa;
}
@end
@implement MyNewRoot
+ (void)initialize {}
@end

vs

@interface MyObject : NSObject {
}
@end

However you're now responsible for a number of features for your class that you typically take for granted (even simple things like memory management for allocating or destorying new instances etc). This is what the comment you quoted hints at when it talks about struct objc_object and the isa instance variable etc.

While not strictly a part of the language, the isa pointer is required for an object to work with the Objective-C runtime system. An object needs to be “equivalent” to a struct objc_object (defined in objc/objc.h) in whatever fields the structure defines. However, you rarely, if ever, need to create your own root object, and objects that inherit from NSObject or NSProxy automatically have the isa variable.

для чего?

  • когда хотим создать отдельное дерево классов (не хотим наследоваться от NSObject)
  • когда NSProxy кажется слишком жирным (mikeash: mafeature lightweight proxy, its git).
Как имитировать множественное наследование?

Multiple Inheritance in Objective C is not supported. The reason for not supporting this mechanism might be the fact that it would have been too difficult to include in the language or the authors thought it is a bad programming and design decision. However, in various cases multiple inheritance proves to be helpful. Fortunately objective C does provide some workarounds for achieving multiple inheritance. Following are the options:

Option 1: Message Forwarding

Message Forwarding, as the name suggests, is a mechanism offered by Objective C runtime. When a message is passed to an object and the object does not respond to it, the application crashes. But before crashing the objective c runtime provides a second chance for the program to pass the message to the proper object/class which actually responds to it. After tracing for the message till the top most superclass, the forwardInvocation message is called. By overriding this method, one can actually redirect the message to another class.

Example: If there is a class named Car which has a property named carInfo which provides the car’s make, model and year of manufacture, and the carInfo contains the data in NSString format, it would be very helpful if NSString class methods could be called upon the objects of Car class which actually inherits from NSObject.

- (id)forwardingTargetForSelector:(SEL)sel
{
    if ([self.carInfo respondsToSelector:sel]) return self.carInfo;
    return nil;
}

Source: iOS 4 Developer's cookbook - Erica Sadun

Option 2: Composition

Composition is a cocoa design pattern which involves referencing another object and calling its functionalities whenever required. Composition actually is a technique for a view to build itself based on several other views. So, in Cocoa terminology this is very similar to Subclassing.

@interface ClassA : NSObject {
}

-(void)methodA;

@end

@interface ClassB : NSObject {
}

-(void)methodB;

@end

@interface MyClass : NSObject {
  ClassA *a;
  ClassB *b;
}

-(id)initWithA:(ClassA *)anA b:(ClassB *)aB;

-(void)methodA;
-(void)methodB;

@end

Source: Objective-C multiple inheritance

Option 3: Protocols

Protocols are classes which contains method to be implemented by other classes who implement the protocol. One class can implement as many as protocols and can implement the methods. However, with protocols only methods can be inherited and not the instance variables.

Тип id.
// id
typedef struct objc_object {
    Class isa;
} *id;

// Class
typedef struct objc_class *Class;
struct objc_class {
    Class isa;
    Class super_class;
    /* далее зависит от версии среды исполнения... */
};
Что такое указатель isa? Для чего он нужен?

isa - указатель на класс объекта. Чтоб искать методы.

  • Что случится во время компиляции если мы посылаем сообщение объекту типа id? Произойдет вызов функции id objc_msgSend(id receiver, SEL method, ...);. Которая будет искать данный селектор в списке instance селекторов receiver'а.
  • Что случится во время выполнения если этот метод существует? Вызовется функция вида: method(receiver, ...);
  • Что произойдет здесь (компиляция + время выполнения): NSString *s = [NSNumber numberWithInt:3]; int i = [s intValue];; в рантайме будет exception NSInvalidArgumentException.
  • Что происходит с методом после того, как он не нашелся в объекте класса, которому его вызвали? (forwardInvocation:)
Чем категория отличается от расширения (extension, наименованная категория)? категория vs extension?
Можно ли добавить ivar в категорию?

Да - через associated objects.

@interface NSObject (AssociatedObject)
@property (nonatomic, strong) id myObj;
@end
@implementation NSObject (AssociatedObject)
@dynamic associatedObject;
- (void)setMyObj:(id)myObj {
     objc_setAssociatedObject(self, @selector(myObj), myObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)associatedObject {
    return objc_getAssociatedObject(self, @selector(associatedObject));
}
@end

Method Sizzling

There are two methods that are automatically invoked by the Objective-C runtime for each class. +load is sent when the class is initially loaded, while +initialize is called just before the application calls its first method on that class or an instance of that class. Both are optional, and are executed only if the method is implemented.

Because method swizzling affects global state, it is important to minimize the possibility of race conditions. +load is guaranteed to be loaded during class initialization, which provides a modicum of consistency for changing system-wide behavior. By contrast, +initialize provides no such guarantee of when it will be executed—in fact, it may never be called, if that class is never messaged directly by the app.

Когда лучше использовать категорию, а когда наследование? категория vs наследование?
Что такое notifications (уведомления)? как мы должны их использовать?
Какая разница м/у использование делегатов (delegation) и нотификейшенов (notification)?
В чем разница между NSArray и NSMutableArray?
Чем отличается NSSet от NSArray? Какие операции происходят быстро в NSSet и какие в NSArray?
Формальный и неформальный (informal) протокол? Протоколы (protocols): основные отличия между c#/java интерфейсами и Objective-C протоколами.

Что делать в случае если класс не реализует какой-то метод из протокола?

  • Проверять (respondsToSelector(@selector(...))).
Есть ли приватные и защищенные методы в Objective-C?

Нет. Зная сигнатуру метода и target можно его выполнить.

Что такое быстрое перечисление (fast enumeration)?

Что ж, в заключение ещё одна интересная фича ObjC: циклы for..in. Их поддерживают все дефолтные коллекции, но можем поддержать и мы. Для этого надо поддержать протокол NSFastEnumeration, а точнее — определить метод countByEnumeratingWithState:objects:count:, но не всё так просто! Вот сигнатура этого метода:

- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len

Этот метод будет вызван каждый раз, когда runtime захочет получить от нас новую порцию объектов. Их мы должны записать либо в предоставленный буфер (размер его len), либо выделить свой. Указатель на этот буфер надо поместить в поле state->itemsPtr, а количество объектов в нём вернуть из функции. Так же не забываем, что (в документации этого нет) поле state->mutationsPtr не должно быть пустым. Если этого не сделать, то мы получим неожиданный SEGFAULT. А вот в поле state->state можно записать что угодно, но лучше всего — записать количество уже отданных элементов. Если отдавать больше нечего, нужно вернуть ноль.

Вот мой пример реализации этой функции:

- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len
{
	if (state->state >= _value)
	{
		return 0;
	}
	NSUInteger itemsToGive = MIN(len, _value - state->state);
	for (NSUInteger i = 0; i < itemsToGive; ++i)
	{
		buffer[i] = @(_values[i + state->state]);
	}
	state->itemsPtr = buffer;
	state->mutationsPtr = &state->extra[0];
	state->state += itemsToGive;
	return itemsToGive;
}

Теперь можно использовать:

for (NSNumber *n in obj)
{
	NSLog(@"n = %@", n);
}
Как имитировать множественное наследование?
Что такое KVO? Когда его нужно использовать? Методы для обозревания объектов? Работает ли KVO с instance переменными (полями) объекта?
Что такое KVC (Key-Value Coding)? Когда его нужно использовать?
  • KVC
  • Key-value coding is a mechanism for accessing an object’s properties indirectly, using strings to identify properties, rather than through invocation of an accessor method or accessing them directly through instance variables. In essence, key-value coding defines the patterns and method signatures that your application’s accessor methods implement.
  • A key is a string that identifies a specific property of an object. Typically, a key corresponds to the name of an accessor method or instance variable in the receiving object. Keys must use ASCII encoding, begin with a lowercase letter, and may not contain whitespace.
  • In order for a class to be considered KVC compliant for a specific property, it must implement the methods required for valueForKey: and setValue:forKey: to work for that property.

Attribute and To-One Relationship Compliance

For properties that are an attribute or a to-one relationship, this requires that your class:

  • Implement a method named -<key>, -is<Key>, or have an instance variable <key> or _<key>.
  • Although key names frequently begin with a lowercase letter, KVC also supports key names that begin with an uppercase letter, such as URL.
  • If the property is mutable, then it should also implement -set<Key>:.
  • Your implementation of the -set<Key>: method should not perform validation.
  • Your class should implement -validate<Key>:error: if validation is appropriate for the key.

Indexed To-Many Relationship Compliance

For indexed to-many relationships, KVC compliance requires that your class:

  • Implement a method named -<key> that returns an array.
  • Or have an array instance variable named <key> or _<key>.
  • Or implement the method -countOf<Key> and one or both of -objectIn<Key>AtIndex: or -<key>AtIndexes:.
  • Optionally, you can also implement -get<Key>:range: to improve performance.

For a mutable indexed ordered to-many relationship, KVC compliance requires that your class also:

  • Implement one or both of the methods -insertObject:in<Key>AtIndex: or -insert<Key>:atIndexes:.
  • Implement one or both of the methods -removeObjectFrom<Key>AtIndex: or -remove<Key>AtIndexes:.
  • Optionally, you can also implement -replaceObjectIn<Key>AtIndex:withObject: or -replace<Key>AtIndexes:with<Key>: to improve performance.

Unordered To-Many Relationship Compliance

For unordered to-many relationships, KVC compliance requires that your class:

  • Implement a method named -<key> that returns a set.
  • Or have a set instance variable named <key> or _<key>.
  • Or implement the methods -countOf<Key>, -enumeratorOf<Key>, and -memberOf<Key>:.

For a mutable unordered to-many relationship, KVC compliance requires that your class also:

  • Implement one or both of the methods -add<Key>Object: or -add<Key>:.
  • Implement one or both of the methods -remove<Key>Object: or -remove<Key>:.
  • Optionally, you can also implement -intersect<Key>: and -set<Key>: to improve performance.
Что такое Run Loop?
Как связаны NSRunLoop и NSAutoreleasePool на пальцах?

17 down vote accepted You have to understand the concept of a run-loop. The run loop in iOS waits for some event to happen and then it acts upon it. That event could be the user touching the screen, receiving a call, etc.

For every such event that iOS handles, a new autorelease pool is created at the beginning and drained when the event processing is complete. Theoretically there could be any number of nested autorelease pools created by Cocoa Touch, but the main one you should know about is the event loop.

Maybe this diagram from the Application Life Cycle will help.

alt tag

In pseudo-code, this boils down to,

int UIApplicationMain(...) {
    while (!shouldQuitApplication) {
        Event *someEvent = // wait for next event;
        NSAutoreleasePool *myPool = [[NSAutoreleasePool alloc] init];
        // handle event
        [myPool release];
    }
}

These are the event types in iOS

UIEventTypeTouches,
UIEventTypeMotion,
UIEventTypeRemoteControl,

So after every touch, motion, or remote control event is processed, the pool will be drained.

Why to use @autoreleasepool?
- (void)useALoadOfNumbers {
    for (int j = 0; j < 10000; ++j) {
        @autoreleasepool {
            for (int i = 0; i < 10000; ++i) {
                NSNumber *number = [NSNumber numberWithInt:(i+j)];
                NSLog(@"number = %p", number);
            }
        }
    }
}
Как лучше всего загрузить UIImage c диска (с кеша)?

[UIImage imageFromFile:..], т.к. в этом случае мы управляем его жизненным циклом и можем выгрузить из памяти; также желательно хранить подобные вещи в NSCache.

Какой контент лучше хранить в Documents, а какой в Cache?
Почему нам не следует вызывать instance методы в методе initialize?

т.к. этот метод посылается классу, до того как вызвать певый instance метод класса. И возможно, он делает, что-то необходимое для дальнейшей работы.

see objc-initialize.mm

Протокол NSCopying

The NSCopying protocol declares a method for providing functional copies of an object. The exact meaning of “copy” can vary from class to class, but a copy must be a functionally independent object with values identical to the original at the time the copy was made. A copy produced with NSCopying is implicitly retained by the sender, who is responsible for releasing it.

NSCopying declares one method, copyWithZone:, but copying is commonly invoked with the convenience method copy. The copy method is defined for all objects inheriting from NSObject and simply invokes copyWithZone: with the default zone.

Your options for implementing this protocol are as follows:

  • Implement NSCopying using alloc and init... in classes that don’t inherit copyWithZone:.
  • Implement NSCopying by invoking the superclass’s copyWithZone: when NSCopying behavior is inherited. If the superclass implementation might use the NSCopyObject function, make explicit assignments to pointer instance variables for retained objects.
  • Implement NSCopying by retaining the original instead of creating a new copy when the class and its contents are immutable.

If a subclass inherits NSCopying from its superclass and declares additional instance variables, the subclass has to override copyWithZone: to properly handle its own instance variables, invoking the superclass’s implementation first.

Почему мы не можем просто использовать любой собственный объект в качестве ключа в словарях

(NSDictionary), что нужно сделать чтобы решить эту проблему?

In general, a key can be any object (provided that it conforms to the NSCopying protocol—see below), but note that when using key-value coding the key must be a string. Neither a key nor a value can be nil; if you need to represent a null value in a dictionary, you should use NSNull. (see link)

NSCoding, NSArchiving (shallow copy vs deep copy)
- (id)initWithCoder:(NSCoder *)decoder {
    self = [super init];
    if (!self) {
        return nil;
    }

    self.title = [decoder decodeObjectForKey:@"title"];
    self.author = [decoder decodeObjectForKey:@"author"];
    self.pageCount = [decoder decodeIntegerForKey:@"pageCount"];
    self.categories = [decoder decodeObjectForKey:@"categories"];
    self.available = [decoder decodeBoolForKey:@"available"];

    return self;
}

- (void)encodeWithCoder:(NSCoder *)encoder {
    [encoder encodeObject:self.title forKey:@"title"];
    [encoder encodeObject:self.author forKey:@"author"];
    [encoder encodeInteger:self.pageCount forKey:@"pageCount"];
    [encoder encodeObject:self.categories forKey:@"categories"];
    [encoder encodeBool:[self isAvailable] forKey:@"available"];
}
// archiving
[NSKeyedArchiver archiveRootObject:books toFile:@"/path/to/archive"];

// unarchiving
[NSKeyedUnarchiver unarchiveObjectWithFile:@"/path/to/archive"];

Each app has its own database of user preferences, which can store and retrieve any NSCoding-compatible object or C value.

While it is not advisable to store an entire object graph into NSUserDefaults, it can be useful to encode compound objects in this way, such as “current user” objects

 // archiving
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:books];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"books"];

//Unarchiving
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:@"books"];
NSArray *books = [NSKeyedUnarchiver unarchiveObjectWithData:data];

XCode & IB:

Storyboard Referrence
StackView
Autolayout
  • Задачки
  • Анимации
Работа с Xib (Nib)

UIViewController LifeCycle

apperance: alt tag

Memory Management:

Как происходит ручное управление памятью - MRC в iOS?

Application memory management is the process of allocating memory during your program’s runtime, using it, and freeing it when you are done with it. A well-written program uses as little memory as possible. In Objective-C, it can also be seen as a way of distributing ownership of limited memory resources among many pieces of data and code. When you have finished working through this guide, you will have the knowledge you need to manage your application’s memory by explicitly managing the life cycle of objects and freeing them when they are no longer needed.

Although memory management is typically considered at the level of an individual object, your goal is actually to manage object graphs. You want to make sure that you have no more objects in memory than you actually need.

alt tag

The memory management model is based on object ownership. Any object may have one or more owners. As long as an object has at least one owner, it continues to exist. If an object has no owners, the runtime system destroys it automatically. To make sure it is clear when you own an object and when you do not, Cocoa sets the following policy:

  • You own any object you create. You create an object using a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy” (for example, alloc, newObject, or mutableCopy).
  • You can take ownership of an object using retain. A received object is normally guaranteed to remain valid within the method it was received in, and that method may also safely return the object to its invoker. You use retain in two situations:
    • (1) In the implementation of an accessor method or an init method, to take ownership of an object you want to store as a property value;
    • and (2) To prevent an object from being invalidated as a side-effect of some other operation (as explained in Avoid Causing Deallocation of Objects You’re Using).
  • When you no longer need it, you must relinquish ownership of an object you own. You relinquish ownership of an object by sending it a release message or an autorelease message. In Cocoa terminology, relinquishing ownership of an object is therefore typically referred to as “releasing” an object.
  • You must not relinquish ownership of an object you do not own. This is just corollary of the previous policy rules, stated explicitly.
Когда нужно использовать метод retainCount (никогда, почему?) Объясните что такое подсчет ссылок (retain count)?
Темы управления памятью, такие как владение retain/release/autorelease.
  • Что случится если вы добавите только что созданный объект в Mutable Array, и пошлете ему сообщение release? Ничего, т.к. array ретейнит свои объекты.
  • Что случится если послать сообщение release массиву? Всем элементам массива будет послано сообщение release.
  • Что случится если вы удалите объект из массива и попытаетесь его использовать? Если есть strong ссылка - то все ок, если массив был еднственным owner-ом объекта - он будет невалиден.

Avoid Causing Deallocation of Objects You’re Using

Cocoa’s ownership policy specifies that received objects should typically remain valid throughout the scope of the calling method. It should also be possible to return a received object from the current scope without fear of it being released. It should not matter to your application that the getter method of an object returns a cached instance variable or a computed value. What matters is that the object remains valid for the time you need it.

There are occasional exceptions to this rule, primarily falling into one of two categories.

When an object is removed from one of the fundamental collection classes.

heisenObject = [array objectAtIndex:n];
[array removeObjectAtIndex:n];
// heisenObject could now be invalid.

When an object is removed from one of the fundamental collection classes, it is sent a release (rather than autorelease) message. If the collection was the only owner of the removed object, the removed object (heisenObject in the example ) is then immediately deallocated. When a “parent object” is deallocated.

id parent = <#create a parent object#>;
// ...
heisenObject = [parent child] ;
[parent release]; // Or, for example: self.parent = nil;
// heisenObject could now be invalid.

In some situations you retrieve an object from another object, and then directly or indirectly release the parent object. If releasing the parent causes it to be deallocated, and the parent was the only owner of the child, then the child (heisenObject in the example) will be deallocated at the same time (assuming that it is sent a release rather than an autorelease message in the parent’s dealloc method). To protect against these situations, you retain heisenObject upon receiving it and you release it when you have finished with it. For example:

heisenObject = [[array objectAtIndex:n] retain];
[array removeObjectAtIndex:n];
// Use heisenObject...
[heisenObject release];
autorelease vs release?

autorelease - puts an object in topmost autorelease pool. release - decrements ref_count.

Что означает ARC?

automatic ref. counting

@interface Counter : NSObject
@property (nonatomic, retain) NSNumber *count;
@end;

// getter is easy
- (NSNumber *)count {
    return _count;
}

// setter is more complicated
- (void)setCount:(NSNumber *)newCount {
    [newCount retain];
    [_count release];
    // Make the new assignment.
    _count = newCount;
}

// Suppose you want to implement a method to reset the counter. You have a couple of choices. The first implementation creates the NSNumber instance with alloc, so you balance that with a release.
- (void)reset {
    NSNumber *zero = [[NSNumber alloc] initWithInteger:0];
    [self setCount:zero];
    [zero release];
}

// or we could use a convenience constructor to create a new NSNumber object. There is therefore no need for retain or release messages
- (void)reset {
    NSNumber *zero = [NSNumber numberWithInteger:0];
    [self setCount:zero];
}

Don’t Use Accessor Methods in Initializer Methods and dealloc

The only places you shouldn’t use accessor methods to set an instance variable are in initializer methods and dealloc. To initialize a counter object with a number object representing zero, you might implement an init method as follows:

- init {
    self = [super init];
    if (self) {
        _count = [[NSNumber alloc] initWithInteger:0];
    }
    return self;
}

To allow a counter to be initialized with a count other than zero, you might implement an initWithCount: method as follows:

- initWithCount:(NSNumber *)startingCount {
    self = [super init];
    if (self) {
        _count = [startingCount copy];
    }
    return self;
}

Since the Counter class has an object instance variable, you must also implement a dealloc method. It should relinquish ownership of any instance variables by sending them a release message, and ultimately it should invoke super’s implementation:

- (void)dealloc {
    [_count release];
    [super dealloc];
}
Ref. Cycle

Retaining an object creates a strong reference to that object. An object cannot be deallocated until all of its strong references are released. A problem, known as a retain cycle, can therefore arise if two objects may have cyclical references—that is, they have a strong reference to each other (either directly, or through a chain of other objects each with a strong reference to the next leading back to the first).

The object relationships shown in Figure 1 illustrate a potential retain cycle. The Document object has a Page object for each page in the document. Each Page object has a property that keeps track of which document it is in. If the Document object has a strong reference to the Page object and the Page object has a strong reference to the Document object, neither object can ever be deallocated. The Document’s reference count cannot become zero until the Page object is released, and the Page object won’t be released until the Document object is deallocated.

alt tag

The solution to the problem of retain cycles is to use weak references. A weak reference is a non-owning relationship where the source object does not retain the object to which it has a reference.

Note: You need to be careful about sending messages to objects for which you hold only a weak reference. If you send a message to an object after it has been deallocated, your application will crash. You must have well-defined conditions for when the object is valid. In most cases, the weak-referenced object is aware of the other object’s weak reference to it, as is the case for circular references, and is responsible for notifying the other object when it deallocates. For example, when you register an object with a notification center, the notification center stores a weak reference to the object and sends messages to it when the appropriate notifications are posted. When the object is deallocated, you need to unregister it with the notification center to prevent the notification center from sending any further messages to the object, which no longer exists. Likewise, when a delegate object is deallocated, you need to remove the delegate link by sending a setDelegate: message with a nil argument to the other object. These messages are normally sent from the object’s dealloc method.

Weak vs assign, strong vs copy?
Atomic vs nonatomic. Чем отличаются? Как вручную переопределить atomic/nonatomic сеттер в не ARC коде?

habr

asing = простое присвоение weak = когда объект релизится, превращается в nil

strong = увеличивает счетчик ссылок copy = присвоить _ivar-у результат посылки метода copy в setter-е.

Что такое autorelease pool?
Как можно заимплементировать autorelease pool на с++?
Если я вызову performSelector:withObject:afterDelay: - объекту пошлется сообщение retain?

Да

Как происходит обработка memory warning(предупреждение о малом количестве памяти)? Зависит ли обработка от версии iOS, как мы должны их обрабатывать?

When the system dispatches a low-memory warning to your app, respond immediately. Low-memory warnings are your opportunity to remove references to objects that you do not need. Responding to these warnings is crucial because apps that fail to do so are more likely to be terminated. The system delivers memory warnings to your app using the following APIs:

  • The applicationDidReceiveMemoryWarning: method of your app delegate.
  • The didReceiveMemoryWarning method of your UIViewController classes.
  • The UIApplicationDidReceiveMemoryWarningNotification notification.
  • Dispatch sources of type DISPATCH_SOURCE_TYPE_MEMORYPRESSURE. This technique is the only one that you can use to distinguish the severity of the memory pressure.

Upon receiving any of these warnings, your handler method should respond by immediately freeing up any unneeded memory. Use the warnings to clear out caches and release images. If you have large data structures that are not being used, write those structures to disk and release the in-memory copies of the data.

If your data model includes known purgeable resources, you can have a corresponding manager object register for the UIApplicationDidReceiveMemoryWarningNotification notification and remove strong references to its purgeable resources directly. Handling this notification directly avoids the need to route all memory warning calls through the app delegate.

Напишите простую реализацию NSAutoreleasePoll на Objective-C
С подвохом: сборщик мусора для iPhone.

Его нет и никогда не будет.

Нужно ли ретейнить (посылать сообщение retain) делегаты?
Что делать, если проект написан с использованием ARC, а нужно использовать классы сторонней библиотеки написанной без ARC?
Для чего используется класс NSCoder?

The NSCoder abstract class declares the interface used by concrete subclasses to transfer objects and other values between memory and some other format. This capability provides the basis for archiving (where objects and data items are stored on disk) and distribution (where objects and data items are copied between different processes or threads). The concrete subclasses provided by Foundation for these purposes are NSArchiver, NSUnarchiver, NSKeyedArchiver, NSKeyedUnarchiver, and NSPortCoder. Concrete subclasses of NSCoder are referred to in general as coder classes, and instances of these classes as coder objects (or simply coders). A coder object that can only encode values is referred to as an encoder object, and one that can only decode values as a decoder object.

Sample methods:

- encodeArrayOfObjCType:count:at:
- encodeBool:forKey:
- encodeBycopyObject:
- encodeByrefObject:
- encodeBytes:length:
- encodeBytes:length:forKey:
- encodeConditionalObject:
Опишите правильный способ управленя памятью выделяемой под Outlet'ы?
Реализуйте следующие методы: retain, release, autorelease?
- (void)retain
{
	[_internalLock lock]; //блокировка для синхронизации
	_referenceCounter++; // пусть _referenceCounter – скрытый инвариант счетчика
	[_internalLock unlock];
}
- (void)release
{
	[_internalLock lock];
	_referenceCounter--; //уменьшим счетчик
	if (!_referenceCounter) //если он равен нулю
	{
		[_internalLock unlock];
		[self dealloc]; //скажем себе, что пора умирать (блокировка освободится тут)
	}
	[_internalLock unlock];
}

Networking:

  • Преимущества и недостатки синхронного и асинхронного соединения?
  • Что означает http, tcp?
  • Какие различия между HEAD, GET, POST, PUT?
  • Как загрузить что-то из интернета? В чем разница между синхронными и асинхронными запросами? Небольшое задание. Опишите как загрузить изображение из интернета и отобразить его в ImageView — все это должно происходить после нажатия кнопки.

Multithreading:

Что такое deadlock?

ситуация в многозадачной среде, при которой несколько процессов находятся в состоянии бесконечного ожидания ресурсов, занятых самими этими процессами.

dispatch_queue_t queue = dispatch_queue_create("my.label", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
    dispatch_sync(queue, ^{
        // outer block is waiting for this inner block to complete,
        // inner block won't start before outer block finishes
        // => deadlock
    });

    // this will never be reached
}); 
Что такое livelock?

Livelock: A situation in which two or more processes continuously change their states in response to changes in the other process(es) without doing any useful work:

Suppose that three processes (P1, P2, P3) each require periodic access to resource R. Consider the situation in which P1 is in possession of the resource, and both P2 and P3 are delayed, waiting for that resource. When P1 exits its critical section, either P2 or P3 should be allowed access to R. Assume that the OS grants access to P3 and that P1 again requires access before P3 completes its critical section. If the OS grants access to P1 after P3 has finished, and subsequently alternately grants access to P1 and P3, then P2 may indefinitely be denied access to the resource, even though there is no deadlock situation.

Что такое семафор (semafor)?

Семафор — это объект, с которым можно выполнить три операции.

init(n):
счётчик := n
 
enter():
ждать пока счётчик станет больше 0; после этого уменьшить счётчик на единицу.
 
leave():
увеличить счётчик на единицу.

Предположим, что есть такой участок кода:

semaphore.init(5);
// .....
// .....
void DoSomething()
{
    semaphore.enter();
    // .......
    semaphore.leave();
}

Тогда не более пяти потоков могут одновременно выполнять функцию DoSomething(). В более сложных семафорах может использоваться очередь; при этом потоки, ожидающие освобождения семафора, будут проходить через семафор именно в том порядке, в котором они вызывали enter().

Что такое мьютекс (mutex)?

A mutually exclusive (or mutex) lock acts as a protective barrier around a resource. A mutex is a type of semaphore that grants access to only one thread at a time. If a mutex is in use and another thread tries to acquire it, that thread blocks until the mutex is released by its original holder. If multiple threads compete for the same mutex, only one at a time is allowed access to it.

Асинхронность vs многопоточность. Чем отличаются?

Асинхронная модель

Менее распространенная модель, нежели многопоточная, но имеющая не меньшие возможности. Асинхронная модель построена на очереди событий (event-loop). При возникновении некоторого события (пришел запрос, выполнилось считывание файла, пришел ответ от БД) оно помещается в конец очереди. Поток, который обрабатывает эту очередь, берет событие с начала очереди, и выполняет связанный с этим событием код. Пока очередь не пуста процессор будет занят работой. По такой схеме работает Node.js.

Многопоточная модель

Эта модель известна каждому. Наше приложение создает некоторое количество потоков (пул), передавая каждому из них задачу и данные для обработки. Задачи выполняются параллельно. Если потоки не имеют общих данных, то у нас не будет накладных расходов на синхронизацию, что делает работу достаточно быстрой. После завершения работы поток не убивается, а лежит в пуле, ожидая следующей задачи. Это убирает накладные расходы на создание и удаление потоков.

Какие технологии в iOS возможно использовать для работы с потоками. Преимущества и недостатки.
  • NSOperation
  • GCD
  • NSThread
  • Posix Threads
Чем отличается dispatch_async от dispatch_sync?

dispatch_sync блокирует текущий процесс, пока блок не будет выполнен; dispatch_async - не блокирует.

Для чего при разработке под iOS использовать POSIX-потоки? pthread_create(&thread, NULL, startTimer, (void *)t);

Use POSIX calls if cross-platform portability is required. If you are writing networking code that runs exclusively in OS X and iOS, you should generally avoid POSIX networking calls, because they are harder to work with than higher-level APIs. However, if you are writing networking code that must be shared with other platforms, you can use the POSIX networking APIs so that you can use the same code everywhere.

Read-write locks

A read-write lock is also referred to as a shared-exclusive lock. This type of lock is typically used in larger-scale operations and can significantly improve performance if the protected data structure is read frequently and modified only occasionally. During normal operation, multiple readers can access the data structure simultaneously. When a thread wants to write to the structure, though, it blocks until all readers release the lock, at which point it acquires the lock and can update the structure. While a writing thread is waiting for the lock, new reader threads block until the writing thread is finished. The system supports read-write locks using POSIX threads only. For more information on how to use these locks, see the pthread man page.

UIKit:

Разница между свойствами bounds и frame объекта UIView? Понимание системы координат?

frame - граница во внешней системе координат (в с.к. superview)

This rectangle defines the size and position of the view in its superview’s coordinate system. You use this rectangle during layout operations to size and position the view. Setting this property changes the point specified by the center property and the size in the bounds rectangle accordingly. The coordinates of the frame rectangle are always specified in points.

WARNING If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.

Changing the frame rectangle automatically redisplays the receiver without invoking the drawRect: method. If you want the drawRect: method invoked when the frame rectangle changes, set the contentMode property to UIViewContentModeRedraw.

Changes to this property can be animated. However, if the transform property contains a non-identity transform, the value of the frame property is undefined and should not be modified. In that case, you can reposition the view using the center property and adjust the size using the bounds property instead

bound - внутренняя система координат

On the screen, the bounds rectangle represents the same visible portion of the view as its frame rectangle. By default, the origin of the bounds rectangle is set to (0, 0) but you can change this value to display different portions of the view. The size of the bounds rectangle is coupled to the size of the frame rectangle, so that changes to one affect the other. Changing the bounds size grows or shrinks the view relative to its center point. The coordinates of the bounds rectangle are always specified in points.

Changing the frame rectangle automatically redisplays the receiver without invoking the drawRect: method. If you want the drawRect: method invoked when the frame rectangle changes, set the contentMode property to UIViewContentModeRedraw.

Changes to this property can be animated.

The default bounds origin is (0,0) and the size is the same as the frame rectangle’s size.

Какие бывают состояния у приложения?

alt tag

Most state transitions are accompanied by a corresponding call to the methods of your app delegate object. These methods are your chance to respond to state changes in an appropriate way. These methods are listed below, along with a summary of how you might use them.

  • application:willFinishLaunchingWithOptions:—This method is your app’s first chance to execute code at launch time.
  • application:didFinishLaunchingWithOptions:—This method allows you to perform any final initialization before your app is displayed to the user.
  • applicationDidBecomeActive:—Lets your app know that it is about to become the foreground app. Use this method for any last minute preparation.
  • applicationWillResignActive:—Lets you know that your app is transitioning away from being the foreground app. Use this method to put your app into a quiescent state.
  • applicationDidEnterBackground:—Lets you know that your app is now running in the background and may be suspended at any time.
  • applicationWillEnterForeground:—Lets you know that your app is moving out of the background and back into the foreground, but that it is not yet active.
  • applicationWillTerminate:—Lets you know that your app is being terminated. This method is not called if your app is suspended.
Цикл жизни UIViewController?

Создание

  • init
  • initWithNibName:

Создание view

  • (BOOL)isViewLoaded
  • loadView
  • viewDidLoad
  • (UIView*) initWithFrame:(CGRect)frame
  • (UIView*) initWithCoder:(NSCoder *)coder

Обработка изменения состояния view

  • viewDidLoad
  • viewWillAppear:(BOOL)animated
  • viewDidAppear:(BOOL)animated
  • viewWillDisappear:(BOOL)animated
  • viewDidDisappear:(BOOL)animated
  • viewDidUnload

Обработка memory warning

  • didReceiveMemoryWarning

Уничтожение

  • viewDidUnload
  • dealloc
Что такое View (представление) и что такое window?

The UIView class defines a rectangular area on the screen and the interfaces for managing the content in that area. At runtime, a view object handles the rendering of any content in its area and also handles any interactions with that content. The UIView class itself provides basic behavior for filling its rectangular area with a background color. More sophisticated content can be presented by subclassing UIView and implementing the necessary drawing and event-handling code yourself. The UIKit framework also includes a set of standard subclasses that range from simple buttons to complex tables and can be used as-is. For example, a UILabel object draws a text string and a UIImageView object draws an image.

The UIWindow class defines an object known as a window that manages and coordinates the views an app displays on a device screen. Unless an app can display content on an external device screen, an app has only one window.

The two principal functions of a window are to provide an area for displaying its views and to distribute events to the views. To change the content your app displays, you can change the window’s root view; you don’t create a new window. A window belongs to a level—typically, UIWindowLevelNormal—that represents where it sits on the z-axis relative to other windows. For example, a system alert window appears above normal app windows.

Какого разрешение экранов iphon'ов, и в чем разница между points (точками) и пикселями (pixels)?
Что означают IBOutlet и IBAction, для чего они нужны, и что значат для препроцессора?
Как работает UITableView?
Как многопоточность работает с UIKit?
Что можно сделать если клавиатура при появлении скрывает важную часть интерфейса?
Почему мы должны релизить IBOutlet'ты во viewDidUnload?
Что такое awakeFromNib

awakeFromNib is called for objects (views, controllers, etc.) that are being archived in xib/nib files. This basically means that xib/nib was unarchived, all connections (IBActions/IBOutlets) for all objects are made and you have a working object graph.

I use it when I have a custom view class that I gave to a certain view in my xib.

В чем разница между xib и nib файлами?

As of Interface Builder version 3, a new file format (with extension .xib) has been added, which is functionally identical to .nib, except it is stored in a flat file, making it more suitable for storage in revision control systems and processing by tools such as diff.

Иерархия наследования UIButton.

An instance of the UIButton class implements a button on the touch screen. A button intercepts touch events and sends an action message to a target object when tapped. Methods for setting the target and action are inherited from UIControl. This class provides methods for setting the title, image, and other appearance properties of a button. By using these accessors, you can specify a different appearance for each button state.

NSObject UIResponder UIView UIControl UIButton

The UIResponder class defines an interface for objects that respond to and handle events. It is the superclass of UIApplication, UIView and its subclasses (which include UIWindow). Instances of these classes are sometimes referred to as responder objects or, simply, responders.

There are two general kinds of events: touch events and motion events. The primary event-handling methods for touches are touchesBegan:withEvent:, touchesMoved:withEvent:, touchesEnded:withEvent:, and touchesCancelled:withEvent:. The parameters of these methods associate touches with their events—��especially touches that are new or have changed—��and thus allow responder objects to track and handle the touches as the delivered events progress through the phases of a multi-touch sequence. Any time a finger touches the screen, is dragged on the screen, or lifts from the screen, a UIEvent object is generated. The event object contains UITouch objects for all fingers on the screen or just lifted from it.

iOS 3.0 introduced system capabilities for generating motion events, specifically the motion of shaking the device. The event-handling methods for these kinds of events are motionBegan:withEvent:, motionEnded:withEvent:, and motionCancelled:withEvent:. Additionally for iOS 3.0, the canPerformAction:withSender: method allows responders to validate commands in the user interface while the undoManager property returns the nearest NSUndoManager object in the responder chain.

In iOS 4.0, UIResponder added the remoteControlReceivedWithEvent: method for handling remote-control events.


The UIView class defines a rectangular area on the screen and the interfaces for managing the content in that area. At runtime, a view object handles the rendering of any content in its area and also handles any interactions with that content. The UIView class itself provides basic behavior for filling its rectangular area with a background color. More sophisticated content can be presented by subclassing UIView and implementing the necessary drawing and event-handling code yourself. The UIKit framework also includes a set of standard subclasses that range from simple buttons to complex tables and can be used as-is. For example, a UILabel object draws a text string and a UIImageView object draws an image.


UIControl is the base class for control objects such as buttons and sliders that convey user intent to the application. You cannot use the UIControl class directly to instantiate controls. It instead defines the common interface and behavioral structure for all its subclasses.

The main role of UIControl is to define an interface and base implementation for preparing action messages and initially dispatching them to their targets when certain events occur.

For an overview of the target-action mechanism, see Target-Action in UIKit. For information on the Multi-Touch event model, see Event Handling Guide for iOS.

The UIControl class also includes methods for getting and setting control state—for example, for determining whether a control is enabled or highlighted—and it defines methods for tracking touches within a control. These tracking methods are overridden by UIControl subclasses.

Базы данных, CoreData:

  • Составить SQL запрос на выборку всех проектов на которых сидит девелопер с id ==3. (Developers:id,name; Projects:id,name; Developers&Projects:project_id,developer_id)?
  • Зачем нужно делать двустороннии связи в таблицах?

Что такое Core Data?

нативный ORM для iOS SDK

ManagedObjectModel, PersistentStoreCoordinator, ManagedObjectContext.

A managed object model is a set of objects that together form a blueprint describing the managed objects you use in your application. A model allows Core Data to map from records in a persistent store to managed objects that you use in your application. It is a collection of entity description objects (instances of NSEntityDescription). An entity description describes an entity (which you can think of as a table in a database) in terms of its name, the name of the class used to represent the entity in your application, and what properties (attributes and relationships) it has.

Что такое Persistent store coordinator? Зачем нужен NSPersistentStoreCoordinator?

A persistent store coordinator associates persistent object stores and a managed object model, and presents a facade to managed object contexts such that a group of persistent stores appears as a single aggregate store. A persistent store coordinator is an instance of NSPersistentStoreCoordinator. It has a reference to a managed object model that describes the entities in the store or stores it manages.

The coordinator is the central object in a Core Data stack. In many applications you just have a single store, but in complex applications there may be several, each potentially containing different entities. The persistent store coordinator’s role is to manage these stores and present to its managed object contexts the facade of a single unified store. When you fetch records, Core Data retrieves results from all of them, unless you specify which store you’re interested in.

Что такое контекст (Managed object context)? Как происходят изменения в NSManagedObjectContext?

An instance of NSManagedObjectContext represents a single “object space” or scratch pad in an application. Its primary responsibility is to manage a collection of managed objects. These objects form a group of related model objects that represent an internally consistent view of one or more persistent stores. A single managed object instance exists in one and only one context, but multiple copies of an object can exist in different contexts. Thus object uniquing is scoped to a particular context.

Как использовать СoreData совместно с многопоточностью?
Какие есть нюансы при использовании Core Data в разных потоках?
NSManagedObjectContext* context = [[NSManagedObjectContext alloc]
    initWithConcurrencyType:NSPrivateQueueConcurrencyType];
context.persistentStoreCoordinator = self.persistentStoreCoordinator;
context.undoManager = nil;
[self.context performBlockAndWait:^
{
    [self import];
}];

or

NSArray *jsonArray = ...; //JSON data to be imported into Core Data
NSManagedObjectContext *moc = ...; // Our primary context on the main queue
 
NSManagedObjectContext *private = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[private setParentContext:moc];
 
[private performBlock:^{
    for (NSDictionary *jsonObject in jsonArray) {
        NSManagedObject *mo = ...; //Managed object that matches the incoming JSON structure
        //update MO with data from the dictionary
    }
    NSError *error = nil;
    if (![private save:&error]) {
        NSLog(@"Error saving context: %@\n%@", [error localizedDescription], [error userInfo]);
        abort();
    }
}];
Как синхронизировать данные между потоками(Как синхронизировать контекст)?
[[NSNotificationCenter defaultCenter] 
    addObserverForName:NSManagedObjectContextDidSaveNotification
                object:nil
                 queue:nil
            usingBlock:^(NSNotification* note)
{
    NSManagedObjectContext *moc = self.mainManagedObjectContext;
    if (note.object != moc)
        [moc performBlock:^(){
            [moc mergeChangesFromContextDidSaveNotification:note];
        }];
    }];
}];
Синхронизация разных типов NSManagedObjectContext (получение и изменение данных в child контекстах)?
Что такое NSManagedObjectId? Можем ли мы сохранить его на потом если приложение закроется?

An NSManagedObjectID object is a compact, universal identifier for a managed object. This forms the basis for uniquing in the Core Data Framework. A managed object ID uniquely identifies the same managed object both between managed object contexts in a single application, and in multiple applications (as in distributed systems). Identifiers contain the information needed to exactly describe an object in a persistent store (like the primary key in the database), although the detailed information is not exposed. The framework completely encapsulates the “external” information and presents a clean object oriented interface.

Object IDs can be transformed into a URI representation which can be archived and recreated later to refer back to a given object (using managedObjectIDForURIRepresentation: (NSPersistentStoreCoordinator) and objectWithID: (NSManagedObjectContext). For example, the last selected group in an application could be stored in the user defaults through the group object’s ID. You can also use object ID URI representations to store “weak” relationships across persistent stores (where no hard join is possible).

The documentation describes the way to get permanent IDs for managed objects from the NSManagedObjectContext:

- (BOOL)obtainPermanentIDsForObjects:(NSArray *)objects error:(NSError **)error
Что таке Fetched Property и особенности работы с ним по сравнению с обычной связью?
Использовали ли NSFetchedResultsController? Почему?
Какие типы хранилищ поддерживает CoreData?

link

  • xml
  • binary
  • sqlite
  • in memory
Что такое ленивая загрузка (lazy loading)? Что ее связывает с CoreData? Опишите ситуация когда она может быть полезной?

Core Data does all the lazy loading for you. Relationship is a fault by default. It’s only when you try to traverse it does the fault fire and the real data will be read from the store into the memory.

Objects related to to-many and to-one relationships are loaded lazily by default. However, If you need to access many of them each time you fetch a Department, then for performance reasons you may ask Core Data to load them simultaneously (this is called pre-fetching). You can do this as follow:

[fetchRequest setReturnsObjectsAsFaults:NO];
[fetchRequest setRelationshipKeyPathsForPrefetching:[NSArray arrayWithObjects:@"employees", nil]];
Что такое Fault и зачем он нужен?

Managed objects typically represent data held in a persistent store. In some situations a managed object may be a “fault”—an object whose property values have not yet been loaded from the external data store—see Faulting and Uniquing for more details. When you access persistent property values, the fault “fires” and the data is retrieved from the store automatically. This can be a comparatively expensive process (potentially requiring a round trip to the persistent store), and you may wish to avoid unnecessarily firing a fault.

You can safely invoke the following methods on a fault without causing it to fire: isEqual:, hash, superclass, class, self, isProxy, isKindOfClass:, isMemberOfClass:, conformsToProtocol:, respondsToSelector:, description, managedObjectContext, entity, objectID, isInserted, isUpdated, isDeleted, faultingState, and isFault. Since isEqual and hash do not cause a fault to fire, managed objects can typically be placed in collections without firing a fault. Note, however, that invoking key-value coding methods on the collection object might in turn result in an invocation of valueForKey: on a managed object, which would fire the fault.

Although the description method does not cause a fault to fire, if you implement a custom description method that accesses the object’s persistent properties, this will cause a fault to fire. You are strongly discouraged from overriding description in this way.

В каких случаях лучше использовать SQLite, а в каких Core Data?

I recently embarked on this journey myself, and ended up trying out all three. Here's what I learned:

Raw sqlite3 Low-level, full access to database. No abstractions. Very verbose - it takes a good deal of code to do very simple things.

Core Data Very high-level, built on abstractions, MUST use Apple's generated database. Useful for iCloud synchronization and simple iOS-only data management. Difficult and dangerous to directly access database, and should not be used for cross-platform databases. Still takes a fair amount of code to do simple things.

FMDB High-level, very abstraction friendly but not forced. Still get full access to database if you need it. Provides an NSDictionary of the result with each item automatically typecasted to the mutable variant of the proper datatype (e.g., text columns are returned as NSMutableString). I ended up building a very simple wrapper class around it to abstract it even more, so I have a helper class with static functions like selectAllFrom:(NSString *)table where:(NSDictionary *)conditions, which returns an NSArray of NSDictionary objects. It's fantastic to be able to do things like NSArray *usersNamedJoe = [DBHelper selectAllFrom:@"user" where:@{@"name": @"Joe"}];. Basically, while Core Data may be useful for simple iOS-only apps, anyone who's interested in using cross-platform databases should stay far, far away from it - Apple has no interest in making that easy, and it shows.

TL;DR:

  • Don't use raw sqlite3 unless you're doing something extremely trivial.
  • Core Data is fine for simple iOS-only data, if you're comfortable with being locked into it.
  • If you want full control over the database and you're not doing something trivial, or you're building your app for multiple platforms, FMDB is definitely the way to go.

CoreAnimation, CoreGraphics:

Чем отличается UIView от CALayer?
CALayers are simply classes representing a rectangle on the screen with visual content. “But wait a darn minute,” you may say, “that’s what UIViews are for!” That’s true, but there’s a trick to that: every UIView contains a root layer that it draws to!
  • Simply speaking,UIView inherit from NSResponder, handle events from users, contains CALayer,which inherit from NSObject,mainly focus on rendering,animation etc.

bigger ans:

On iOS, every UIView is backed by a Core Animation CALayer, so you are dealing with CALayers when using a UIView, even though you may not realize it. Unlike NSViews on the Mac, which evolved before Core Animation existed, UIViews are intended to be lightweight wrappers around these CALayers.

As I describe in the similar question "When to use CALayer on the Mac/iPhone?", working directly with CALayers doesn't give you significant performance advantages over UIViews. One of the reasons you might want to build a user interface element with CALayers instead of UIViews is that it can be very easily ported to the Mac. UIViews are very different from NSViews, but CALayers are almost identical on the two platforms. This is why the Core Plot framework lays out its graphs using CALayers instead of other UI elements.

One thing UIViews provide over CALayers is built-in support for user interaction. They handle hit-testing on touches and other related actions that you would need to build yourself if managing a hierarchy of CALayers. It's not that hard to implement this yourself, but it is extra code you'd need to write when building a CALayer-only interface.

You will often need to access the underlying layers for a UIView when performing more complex animations than the base UIView class allows. UIView's animation capabilities have grown as the iOS SDK has matured, but there are still a few things that are best done by interacting with the underlying CALayer.

Какие типы CALayer есть?
  • another tutorial

  • ref

  • ответ есть тут

  • CAEmitterLayer. Used to implement a Core Animation–based particle emitter system. The emitter layer object controls the generation of the particles and their origin.

  • CAGradientLayer. Used to draw a color gradient that fills the shape of the layer (within the bounds of any rounded corners).

  • CAMetalLayer. Used to set up and vend drawable textures for rendering layer content using Metal.

  • CAEAGLLayer/CAOpenGLLayer. Used to set up the backing store and context for rendering layer content using OpenGL ES (iOS) or OpenGL (OS X).

  • CAReplicatorLayer. Used when you want to make copies of one or more sublayers automatically. The replicator makes the copies for you and uses the properties you specify to alter the appearance or attributes of the copies.

  • CAScrollLayer. Used to manage a large scrollable area composed of multiple sublayers.

  • CAShapeLayer. Used to draw a cubic Bezier spline. Shape layers are advantageous for drawing path-based shapes because they always result in a crisp path, as opposed to a path you draw into a layer’s backing store, which would not look as good when scaled. However, the crisp results do involve rendering the shape on the main thread and caching the results.

  • CATextLayer. Used to render a plain or attributed string of text.

  • CATiledLayer. Used to manage a large image that can be divided into smaller tiles and rendered individually with support for zooming in and out of the content.

  • CATransformLayer. Used to render a true 3D layer hierarchy, rather than the flattened layer hierarchy implemented by other layer classes.

  • QCCompositionLayer. Used to render a Quartz Composer composition. (OS X only)

Чем отличается UIView based Animation от Core Animation?

With Core Animation (Layer based) you can do much advanced things, like 3d animation (embedding your view in 3d space).

UiView animations are quite powerful though, and shouldn't be a performance bottleneck until you have plenty of them. I only see one example in your code, and its duration is extremely short. In fact, you are almost setting the frame property instantly (70 ms is 14 fps). You should think of UIView animation in terms of start, end, and the duration for the whole distance - which is typically 0.2 s to several seconds. It is not designed to to fire one micro animation after another.

Тайминги в CoreAnimation?
Что такое backing store?

Слой - это базовая единица, с которой работает CoreAnimation. Слой - это неких холст, канвас, backing store, который хранит в себе картинку. По факту - это некий графический кэш. Но сам слой не делает ничего, он не занимается отрисовкой. И при изменении свойств слоя - вы просто меняете свойства этого объекта. А когда нужно отрисовать этот слой - эти данные объекта, вместе с закэшированным изображением передаются в GPU и там уже отрисовываются. Поэтому когда вы поменяли параметры слоя (положение, размер), то не происходит скэйлинга картинки или блэндинга дерева слоев. GPU просто перерисовывает кадр, учитывая новые свойства.

Чем отличаются аффинные преобразования от трехмерных?

афинные в 2D (на плоскости), трехменрые в 3D.

Нужно ли ретейнить (посылать сообщение retain) делегат для CAAnimation?

iOS SDK:

UIDocument (and iCloud)
On Demand Resources
BitCode
DynamicType
Self Sizing Cells
intristicContentSize/invalidation
SizeClasses

WWDC:

Links:

Algorithms:

  • Имеется список
@interface Node
@property (nonatomic, strong) id data;
@property (nonatomic, strong) Node *next;
@end

написать функцию, которая разворачивает список:

@implementation Node
- (void)reverse
{

}
@end
  • Имеется дерево
@interface Node
@property (nonatomic, strong) id data;
@property (nonatomic, strong) Node *left;
@property (nonatomic, strong) Node *right;
@end

написать функцию, которая находит длину максимального пути:

@implementation Node
- (NSInteger)depth
{

}
@end
  • Имеется массив строк
a = @[@"abcde", @"abbcd", @"dcbea",@"acbbd"]

необходимо написать такую функцию, которая сгруппирует эти строки по принципу акронима (т.е. слова, состоящие из одних и тех же букв, должны быть в одной группе). Например:

@[
@[@"abcde", @"dcbea"],
@[@"abbcd", @"acbbd"]
]

При этом сложность алгоритма не должна превосходить O(N*log(N))+O(M), N - количество слов, M - длина максимального слова.

Logical:

  • Есть 4 человека, каждый проходит мост за 1, 2, 5, и 10 минут соответственно. Через мост они могут переходить только парами, держась за руку и только с фонариком. Притом один должен вернуться обратно и передать фонарик. Необходимо переправить всех за 17 мин на другую сторону. Задача решаема.

Code Puzzels:

Что не так с этим кодом? Зачем нужны инициализаторы?
[[[SomeClass alloc] init] init];

Calling -init multiple times is undefined, unsupported, and will lead to bugs, crashes, and other unexpected behavior.

Many classes -- NSString, NSArray, and NSDictionary, for example -- don't actually allocate anything when the +alloc method is called. It isn't until one of the various -init* methods are called that the object has enough context to figure out the most efficient means of doing whatever you ask.

Сработает ли таймер? Почему?
void startTimer(void *threadId) 
{
   [NSTimer  scheduleTimerWithTimeInterval:10.0f 
      target:aTarget 
          selector:@selector(tick: ) 
          userInfo:nil
           repeats:NO];
}

pthread_create(&thread, NULL, startTimer, (void *)t);

нет, отсутсвует runloop у процесса

Какой метод вызовется: класса A или класса B? Как надо изменить код, чтобы вызвался метод класса A?
@interface A : NSObject
- (void)someMethod;
@end

@implementation A
- (void)someMethod
{
    NSLog(@"This is class A");
}
@end

@interface B : A
@end

@implementation B
- (void)someMethod 
{
    NSLog(@"This is class B");
}
@end

@interface C : NSObject
@end

@implementation C
- (void)method 
{
    A *a = [B new];
    [a someMethod];
}
В каких случаях лучше использовать strong, а в каких copy для NSString? Почему?
@property (nonatomic, strong) NSString *someString;
@property (nonatomic, copy) NSString *anotherString;

Ответ:

'copy' will cause the setter for that property to create a copy of the object, and is otherwise identical to strong. You would use this to make sure that if someone sets your property to a mutable string, then mutates the string, you still have the original value. If the string isn't mutable, Cocoa will silently optimize out the copy operation, which is nice :)

'strong' will keep the property's value alive until it's set to something else. If you want incoming mutable strings to change out from under you (not impossible, but not all that common, a thing to want), then strong would be the right thing to do. Generally strong is more useful for objects that represent something more complex than a simple "value" (i.e. not NSString, NSNumber, NSValue, etc...).

'assign' is the default (and indeed only) possible setting for an integer. Integers can't be retained or copied like objects.

Что выведется в консоль? Почему?
- (BOOL)objectsCount
{
    NSMutableArray *array = [NSMutableArray new];
    for (NSInteger i = 0; i < 1024; i++) 
    {
        [array addObject:[NSNumber numberWithInt:i]];
    }
    return array.count;
}

- (void)someMethod 
{
    if ([self objectsCount]) 
    {
        NSLog(@"has objects");
    }
    else
    {
        NSLog(@"no objects");
    }
}

has objects

Выведется ли в дебагер «Hello world»? Почему?
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"Hello world");
    });

   /* Another implementation */
   return YES;
}
Что выведется в консоль?
 dispatch_async(dispatch_get_main_queue(), ^
    {
        NSLog(@"A %d", [object retainCount]);
        dispatch_async(dispatch_get_main_queue(), ^
        {
            NSLog(@"B %d", [object retainCount]);
        });
        NSLog(@"C %d", [object retainCount]);
    });
    NSLog(@"D %d", [object retainCount]);

D A C B

Что произойдет при исполнении следующего кода?
Ball *ball = [[[[Ball alloc] init] autorelease] autorelease];

crash

Additional:

  • Анкета в которой просят оценить свои знания по технологиям по 10 бальной шкале.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment