2014-12-18 6 views
4

Я следую за книгой, разработанной G. Lee, с помощью Test-Driven iOS и наткнулся на этот модульный тест, который я не понимаю. Прежде всего, если вам нужно больше кода, пожалуйста, сообщите мне сразу.Объект NSError, уже заполненный первым вызовом метода

-(void)testDelegateNotifiedOfErrorWhenNewsBuilderFails 
{ 
    MockNewsBuilder *builder = [MockNewsBuilder new]; 
    builder.arrayToReturn = nil; 
    builder.errorToSet = underlyingError; 
    newsManager.newsBuilder = builder; 
    [newsManager receivedNewsJSON:@"Fake Json"]; 
    ... 
} 

-(void)receivedNewsJSON:(NSString *)objectNotation 
{ 
    NSError *error = nil; 
    // As you see error is nil and I am passing in a nil error. 
    NSArray *news = [_newsBuilder newsFromJSON:objectNotation error:&error]; 
    ... 
} 

@implementation MockNewsBuilder 

-(NSArray *)newsFromJSON:(NSString *)objectNotation error:(NSError **)error 
{ 
    // But once I arrive here, error is no longer nil. 
    // (NSError **) error = 0x00007fff5cb887f0 domain: @"Fake Json" - code: 0 
    ... 
} 

Как устанавливается автоматическая магическая ошибка?

UPDATE:

Спасибо всем за активное обсуждение и консультации. Ответы объясняют, как сторона-получатель получает экземпляр ошибки из-за &, я это четко понимаю. Мой вопрос остается, хотя почему вызываемая сторона указывает на заполненный экземпляр NSError, хотя он должен был быть nil. Я не установил экземпляр ошибки в newsFromJSON:error:, так как он уже заселен там?

Я только что изменил [newsManager receivedNewsJSON:@"Fake Json1"]; и экземпляр ошибки в newsFromJSON:error: отражает сразу (NSError **) error = 0x00007fff5b9b27f0 domain: @"Fake Json1" - code: 0. Его очень запутанная ...

+0

Этот http://stackoverflow.com/a/833124/790842 отвечает на ваш вопрос. – iphonic

+1

Нет причин, по которым «NSError» уже будет заполнен, если это не первый раз, когда был вызван «newsFromJSON» или что-то еще вызвано заранее. В коде, который вы показываете, он должен быть «nil». – Droppy

+0

@ Droppy благодарит за ведение дискуссии. :) Я уточнил вопрос с более подробной информацией. Но для меня это остается загадкой. – Houman

ответ

3

Это просто указатель на концепцию указателя. Вы передаете ссылку на объект ссылки ошибки & ошибки методу -(NSArray *)newsFromJSON:(NSString *)objectNotation error:(NSError **)error;

И это обновит объект ошибки в указателю памяти вы прошли.

См. Это понятие указателя на указатель.

enter image description here

Update:

Ваш объект ошибка равна нулю, да его право. Но вы не передаете этот объект ошибки методу newsFromJSON, а адресу памяти объекта ошибки (&error). Это адрес памяти объекта ошибки.
Это почему вы получаете ненулевое значение внутри вашего метода newsFromJSON.

И еще одна вещь, вы можете получить доступ к исходному объекту в методе newsFromJSON с использованием контента оператора (* оператора) как **error = something;

Это обновит исходный объект (NSError *error) Вы заявили в своем вызывающего способ.
В C или CPP или Objective-C, & является адресом оператора и * является содержимым оператора.

& OBJ -> указать адрес памяти в OBJ
* OBJ -> передать содержание адреса памяти в OBJ.

+0

Я не думаю, что ОП спрашивает, что означает '**' (несмотря на название). Он хочет знать, как уже заполнен «NSError». – Droppy

+0

'NSError' заселяется на вызываемой стороне, __because__ вы передаете адрес указателя (двойная косвенность). –

+0

@NicolasMiari Итак, объясните, почему объект NSError кажется заполненным. – Droppy

0

error является переменной типа NSError*, то есть «указатель на NSError» (в объективном-C, все объекты обрабатываются как ссылки, в отличие от, например, C++).

Это означает, что error является (локальной) переменной, которая хранит адрес фактического объекта NSError, изначально nil.

Метод, который вы вызываете, создает (автореализованный) экземпляр NSError. Чтобы получить ссылку на этот экземпляр, вам необходимо передать методу адрес указателя, или &error, который, в свою очередь, имеет тип «указатель на указатель на NSError» (обратите внимание на двухуровневую косвенность).

Вы делаете это, потому что аргументы для функций из C и методов в Objective-C передаются по значению: если вы только что прошли error, то только что скопированное значение (nil) копируется и независимо от того, что делает вызываемый метод, содержимое переменной error с вашей стороны (вызывающего абонента) изменить нельзя. Для этого вам необходимо пройти адрес с ошибкой, или &error.

Таким образом, вызываемый метод может «изменить» содержимое error (адрес, хранящийся там), чтобы он указывал на вновь созданный экземпляр NSError.

Это имеет смысл?

ДОПОЛНЕНИЕ: Это очень распространенный вид очень часто встречается в какао: Вызываемый метод потенциально может потерпеть неудачу, и вместо того, чтобы просто использовать возвращаемое значение сигнала успеха/неудачи, а также дополнительные «в/из параметра» передается для получения подробной информации об ошибке в случае сбоя. При сбое метод может возвращать false ( NO, 0 и т. Д.), Но дополнительно может предоставить более подробный отчет об ошибке (например, причину сбоя) внутри экземпляра NSError.

EDITED: Как сказал @Droppy, и видя, что весь код участвует ваш собственный (то есть, не какой-то первая или третья структура партии), это невозможно, что error установлен на что-либо, кроме nil, если вы явно выделите его где-нибудь. Возможно, вам следует «посмотреть» его в отладчике, чтобы увидеть, когда/где он установлен. поскольку сообщение, похоже, установлено в @"Fake JSON", первое, что вы могли бы сделать, это поиск этой строки в вашем проекте (все файлы).

0

** является указателем на указатель. Это означает, что вам нужно передать адрес указателя функции или метода. Objective-C является строгим надмножеством C. Это означает, что, как и в C, функции и методы могут возвращать только одно значение. Есть два пути. Одним из них является перенос всех ваших возвратов в структуры или NS-словари или другие коллекции. Этот способ называется outParameter . Он передает адрес указателя. C является копирующим языком. Но указатели - это переносные черные дыры, которые позволяют делать дикие вещи в C. Objective-C и C++ дают вам такую ​​же дикость.

Ошибка установлена ​​кодом ядра Apple. Паттерн Какао обычно возвращает BOOL и передает адрес указателя NSError. Если BOOL NO, проверьте NSError. Рамка Apple разместит несколько подарков в вашем адресном поле указателя NSError.

Иногда они не используют BOOL и вместо этого возвращают объект или ноль.

Основы Core Foundation C работают очень точно и используют параметры ввода и вывода.

+2

Почему «NSObject» кажется заполненным (см. Значение 'domain'' @ "Fake Json" ')? – Droppy

+0

Вам следует позаботиться или проверить NSError, если ваше возвращаемое значение не было равно нулю или не равно NO. (В зависимости от документально вызванного метода) NSError не даст ценного значения, если ваш метод возврата не указывает, что вы должны проверить ошибку – uchuugaka

+0

Это соглашение о какао, согласно которому NSError следует считать бессмысленным, если вы получите ожидаемый доход. Под капотом фреймворки, вероятно, сохраняют значения ошибок в привычном месте в памяти для оптимизации и отладки фреймов. – uchuugaka