Skip to content

Commit

Permalink
General updates 3
Browse files Browse the repository at this point in the history
  • Loading branch information
sashakid committed Oct 10, 2017
1 parent a5e1067 commit 51b02fe
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 4 deletions.
32 changes: 31 additions & 1 deletion Main/11_runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- [Чем объект Objective-c отличается от структуры С, что такое структура в C?](#объект)
- [Вопрос о методах isKindOfClass, isMemberOfClass](#вisKindOfClass)
- [Тип id](#тип-id)
- [Dynamic method resolution](#dynamic-method-resolution)

<a name="runtime"></a>
# Runtime
Expand Down Expand Up @@ -36,7 +37,7 @@ typedef struct objc_object {

_Форвардинг_

If you send a message to an object that does not handle that message, before announcing an error the runtime sends the object a forwardInvocation: message with an NSInvocation object as its sole argumentthe NSInvocation object encapsulates the original message and the arguments that were passed with it. You can implement a forwardInvocation: method to give a default response to the message, or to avoid the error in some other way. As its name implies, forwardInvocation: is commonly used to forward the message to another object.
If you send a message to an object that does not handle that message, before announcing an error the runtime sends the object a forwardInvocation: message with an `NSInvocation` object as its sole argumentthe `NSInvocation` object encapsulates the original message and the arguments that were passed with it. You can implement a forwardInvocation: method to give a default response to the message, or to avoid the error in some other way. As its name implies, `forwardInvocation:` is commonly used to forward the message to another object.

<a name="классы"></a>
## Что такое классы в Objective-C, структура классов?
Expand Down Expand Up @@ -214,3 +215,32 @@ And to get the value back out:
MyStruct struct;
[value getValue:&struct];
```

<a name="dynamic-method-resolution"></a>
## Dynamic method resolution

There are situations where you might want to provide an implementation of a method dynamically. For example, the Objective-C declared properties feature includes the `@dynamic` directive, which tells the compiler that the methods associated with the property will be provided dynamically. You can implement the methods `resolveInstanceMethod:` and `resolveClassMethod:` to dynamically provide an implementation for a given selector for an instance and class method respectively.

An Objective-C method is simply a C function that take at least two arguments — `self` and `_cmd`. You can add a function to a class as a method using the function `class_addMethod`. Therefore, given the following function:
```c
void dynamicMethodIMP(id self, SEL _cmd) {
// implementation ....
}
```
you can dynamically add it to a class as a method (called `resolveThisMethodDynamically`) using `resolveInstanceMethod:` like this:
```objectivec
@implementation MyClass
+ (BOOL)resolveInstanceMethod:(SEL)aSEL {
if (aSEL == @selector(resolveThisMethodDynamically)) {
class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:aSEL];
}
@end
```
A class has the opportunity to dynamically resolve a method before the forwarding mechanism kicks in. If `respondsToSelector:` or `instancesRespondToSelector:` is invoked, the dynamic method resolver is given the opportunity to provide an `IMP` for the selector first. If you implement `resolveInstanceMethod:` but want particular selectors to actually be forwarded via the forwarding mechanism, you return `NO` for those selectors.

__Dynamic Loading__

An Objective-C program can load and link new classes and categories while it’s running. The new code is incorporated into the program and treated identically to classes and categories loaded at the start. Dynamic loading can be used to do a lot of different things. For example, the various modules in the System Preferences application are dynamically loaded. In the Cocoa environment, dynamic loading is commonly used to allow applications to be customized. Others can write modules that your program loads at runtime—much as Interface Builder loads custom palettes and the OS X System Preferences application loads custom preference modules. The loadable modules extend what your application can do. They contribute to it in ways that you permit but could not have anticipated or defined yourself. You provide the framework, but others provide the code.
43 changes: 41 additions & 2 deletions Main/14_data.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- [Какие типы хранилищ поддерживает CoreData?](#типы-хранилищ)
- [Что такое ленивая загрузка? Что ее связывает с Core Data? Опишите ситуация когда она может быть полезной? Что такое faulting?](#ленивая-загрузка-core-data)
- [Что такое fetch result controller?](#fetch-result-controller)
- [Как сделать миграцию БД?](#db-migration)

<a name="data"></a>
# Data
Expand Down Expand Up @@ -175,8 +176,13 @@ __Пример SQLite__

<a name="core-data-в-разных-потоках"></a>
## Какие есть нюансы при использовании Core Data в разных потоках? Как синхронизировать данные между потоками?
1. Create a separate managed object context for each thread and share a single persistent store coordinator. This is the typically-recommended approach.
2. Create a separate managed object context and persistent store coordinator for each thread. This approach provides for greater concurrency at the expense of greater complexity (particularly if you need to communicate changes between different contexts) and increased memory usage.
In Core Data, the managed object context can be used with two concurrency patterns, defined by `NSMainQueueConcurrencyType` and `NSPrivateQueueConcurrencyType`.

* `NSMainQueueConcurrencyType` is specifically for use with your application interface and can only be used on the main queue of an application.

* The `NSPrivateQueueConcurrencyType` configuration creates its own queue upon initialization and can be used only on that queue. Because the queue is private and internal to the `NSManagedObjectContext` instance, it can only be accessed through the `performBlock:` and the `performBlockAndWait:` methods.

`NSManagedObject` instances are not intended to be passed between queues. Doing so can result in corruption of the data and termination of the application. When it is necessary to hand off a managed object reference from one queue to another, it must be done through `NSManagedObjectID` instances. You retrieve the managed object ID of a managed object by calling the `objectID` method on the `NSManagedObject` instance.

<a name="типы-хранилищ"></a>
# Какие типы хранилищ поддерживает CoreData?
Expand All @@ -201,3 +207,36 @@ Faulting isn't unique to Core Data. A similar technique is used in many other fr
## Что такое fetch result controller?
Данные сами по себе может быть и представляют какую-либо ценность, но, обычно их нужно использовать. Одним из элементов представления данных в iOS служат таблицы (объекты класса `UITableView`), которые через объект класса `NSFetchedResultsController` можно привязать к CoreData. После этого при изменении данных в CoreData будет актуализироваться информация в таблице. Так же, с помощью таблицы можно управлять данными в хранилище.
`NSFetchedResultsController` — контроллер результатов выборки. Создается, обычно один экземпляр на `ViewController`, но вполне может работать и без оного, внутрь которого помещается исключительно для того, что бы было проще привязать данные к виду.

<a name="db-migration"></a>
## Как сделать миграцию БД?

You can only open a Core Data store using the managed object model used to create it. Changing a model will therefore make it incompatible with (and so unable to open) the stores it previously created. If you change your model, you therefore need to change the data in existing stores to new version—changing the store format is known as migration. To migrate a store, you need both the version of the model used to create it, and the current version of the model you want to migrate to. You can create a versioned model that contains more than one version of a managed object model. Within the versioned model you mark one version as being the current version. Core Data can then use this model to open persistent stores created using any of the model versions, and migrate the stores to the current version. To help Core Data perform the migration, though, you may have to provide information about how to map from one version of the model to another. This information may be in the form of hints within the versioned model itself, or in a separate mapping model file that you create.

The migration process itself is in three stages. It uses a copy of the source and destination models in which the validation rules are disabled and the class of all entities is changed to `NSManagedObject`.

To perform the migration, Core Data sets up two stacks, one for the source store and one for the destination store. Core Data then processes each entity mapping in the mapping model in turn. It fetches objects of the current entity into the source stack, creates the corresponding objects in the destination stack, then recreates relationships between destination objects in a second stage, before finally applying validation constraints in the final stage.

Before a cycle starts, the entity migration policy responsible for the current entity is sent a `beginEntityMapping:manager:error:` message. You can override this method to perform any initialization the policy requires. The process then proceeds as follows:

1. Create destination instances based on source instances.

At the beginning of this phase, the entity migration policy is sent a `createDestinationInstancesForSourceInstance:entityMapping:manager:error:` message; at the end it is sent a `endInstanceCreationForEntityMapping:manager:error:` message. In this stage, only attributes (not relationships) are set in the destination objects. Instances of the source entity are fetched. For each instance, appropriate instances of the destination entity are created (typically there is only one) and their attributes populated (for trivial cases, `name = $source.name`). A record is kept of the instances per entity mapping since this may be useful in the second stage.

2. Recreate relationships.

At the beginning of this phase, the entity migration policy is sent a `createRelationshipsForDestinationInstance:entityMapping:manager:error:` message; at the end it is sent a `endRelationshipCreationForEntityMapping:manager:error:` message. For each entity mapping (in order), for each destination instance created in the first step any relationships are recreated.

3. Validate and save.

In this phase, the entity migration policy is sent a `performCustomValidationForEntityMapping:manager:error:` message. Validation rules in the destination model are applied to ensure data integrity and consistency, and then the store is saved.

At the end of the cycle, the entity migration policy is sent an endEntityMapping:manager:error: message. You can override this method to perform any clean-up the policy needs to do. Note that Core Data cannot simply fetch objects into the source stack and insert them into the destination stack, the objects must be re-created in the new stack. Core Data maintains “association tables” which tell it which object in the destination store is the migrated version of which object in the source store, and vice-versa. Moreover, because it doesn't have a means to flush the contexts it is working with, you may accumulate many objects in the migration manager as the migration progresses. If this presents a significant memory overhead and hence gives rise to performance problems, you can customize the process as described in Multiple Passes—Dealing With Large Datasets.

__Lightweight Migration__

If you just make simple changes to your model (such as adding a new attribute to an entity), Core Data can perform automatic data migration, referred to as lightweight migration. Lightweight migration is fundamentally the same as ordinary migration, except that instead of you providing a mapping model (as described in Mapping Overview), Core Data infers one from differences between the source and destination managed object models.

Lightweight migration is especially convenient during early stages of application development, when you may be changing your managed object model frequently, but you don’t want to have to keep regenerating test data. You can migrate existing data without having to create a custom mapping model for every model version used to create a store that would need to be migrated.

A further advantage of using lightweight migration—beyond the fact that you don’t need to create the mapping model yourself—is that if you use an inferred model and you use the SQLite store, then Core Data can perform the migration in situ (solely by issuing SQL statements). This can represent a significant performance benefit as Core Data doesn’t have to load any of your data. Because of this, you are encouraged to use inferred migration where possible, even if the mapping model you might create yourself would be trivial.
37 changes: 37 additions & 0 deletions Main/17_testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- [Functional Tests](#functional-tests)
- [Acceptance Tests](#acceptance-tests)
- [Как тестировать асинхронные методы?](#async-testing)
- [Dependency injection](#dependency-injection)

<a name="тестирование"></a>
# Тестирование
Expand Down Expand Up @@ -112,3 +113,39 @@ func onPostExecute(transferItem:WmTransferItem) {
delegate?.onDone("finished")// call callback
}
```

<a name="dependency-injection"></a>
## Dependency injection

Dependency injection (DI) is a popular design pattern in many languages, such as Java and C#, but it hasn’t seen wide adoption in Objective-C. The concept of dependency injection is very simple: __an object should require you to pass in any dependencies rather than creating them itself.__ Dependencies can be passed to an object via the initializer (or “constructor”), or via properties (or “setters”). These are commonly referred to as “constructor injection” and “setter injection.”

Constructor Injection:
```objectivec
- (instancetype)initWithDependency1:(Dependency1 *)d1 dependency2:(Dependency2 *)d2;
```
Setter Injection:
```objectivec
@property (nonatomic, retain) Dependency1 *dependency1;
@property (nonatomic, retain) Dependency2 *dependency2;
```

Constructor injection is preferred, and as a general rule you should only fall back to setter injection if constructor injection is not possible. With constructor injection, you’ll likely still have `@property` definitions for these dependencies, but you can make them read only to simplify your object’s API.

Benefits:

* Clear declaration of dependencies

It becomes obvious what an object needs in order to operate, and dangerous hidden dependencies — like globals — disappear.

* Composition

DI encourages composition over inheritance, which improves the reusability of your code.

* Easy customization

When creating an object, it’s easy to customize parts of the object for specific scenarios.

* Clear ownership

Particularly when using constructor injection, the object ownership rules are strictly enforced — helping to build a directed acyclic object graph.
Testability More than anything else, dependency injection improves the testability of your objects. Because they can be created simply by filling in the initializer, no hidden dependencies need to be managed. Furthermore, it becomes simple to mock out the dependencies to focus your tests on the object being tested.
16 changes: 16 additions & 0 deletions Main/19_general_questions.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
- [Отличие view от layer?](#view-layer-difference)
- [Как передвинуть все subviews?](#moving-subviews)
- [Swift optionals, Objective-C new modifiers (`nullable`, etc.). что это и как реализовано?](#optionals-swift-objc)
- [Можно ли скачать что-нибудь, пока приложение выполняется в фоне?](#download-in-background)

<a name="общие-вопросы-и-задачи"></a>
# Общие вопросы и задачи
Expand Down Expand Up @@ -898,3 +899,18 @@ If the block has a return value, then you're forced into one of the underscore v
// the method accepts a nullable block that returns a nonnull value
// there are some more combinations here, you get the idea
```

<a name="download-in-background"></a>
## Можно ли скачать что-нибудь, пока приложение выполняется в фоне?

When downloading files, apps should use an `NSURLSession` object to start the downloads so that the system can take control of the download process in case the app is suspended or terminated. When you configure an `NSURLSession` object for background transfers, the system manages those transfers in a separate process and reports status back to your app in the usual way. If your app is terminated while transfers are ongoing, the system continues the transfers in the background and launches your app (as appropriate) when the transfers finish or when one or more tasks need your app’s attention. To support background transfers, you must configure your `NSURLSession` object appropriately. To configure the session, you must first create a `NSURLSessionConfiguration` object and set several properties to appropriate values. You then pass that configuration object to the appropriate initialization method of `NSURLSession` when creating your session. The process for creating a configuration object that supports background downloads is as follows:

1. Create the configuration object using the `backgroundSessionConfigurationWithIdentifier:` method of `NSURLSessionConfiguration`.
2. Set the value of the configuration object’s `sessionSendsLaunchEvents` property to `YES`.
3. If your app starts transfers while it is in the foreground, it is recommend that you also set the discretionary property of the configuration object to `YES`.
4. Configure any other properties of the configuration object as appropriate.
5. Use the configuration object to create your `NSURLSession` object.

Once configured, your `NSURLSession` object seamlessly hands off upload and download tasks to the system at appropriate times. If tasks finish while your app is still running (either in the foreground or the background), the session object notifies its delegate in the usual way. If tasks have not yet finished and the system terminates your app, the system automatically continues managing the tasks in the background. If the user terminates your app, the system cancels any pending tasks.

When all of the tasks associated with a background session are complete, the system relaunches a terminated app (assuming that the `sessionSendsLaunchEvents` property was set to `YES` and that the user did not force quit the app) and calls the app delegate’s `application:handleEventsForBackgroundURLSession:completionHandler:` method. (The system may also relaunch the app to handle authentication challenges or other task-related events that require your app’s attention.) In your implementation of that delegate method, use the provided identifier to create a new `NSURLSessionConfiguration` and `NSURLSession` object with the same configuration as before. The system reconnects your new session object to the previous tasks and reports their status to the session object’s delegate.
Loading

0 comments on commit 51b02fe

Please sign in to comment.