سؤال

أنا غير قادر على العثور على وثائق جيدة حول كيفية الفئة الفرعية NSOperation أن تكون متزامنة وأيضًا لدعم الإلغاء. قرأت مستندات Apple ، لكنني غير قادر على العثور على مثال "رسمي".

ها هو رمز المصدر الخاص بي:

@synthesize isExecuting = _isExecuting;
@synthesize isFinished = _isFinished;
@synthesize isCancelled = _isCancelled;

- (BOOL)isConcurrent
{
    return YES;
}

- (void)start
{
/* WHY SHOULD I PUT THIS ?
    if (![NSThread isMainThread])
    {
        [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
        return;
    }
*/

    [self willChangeValueForKey:@"isExecuting"];
    _isExecuting = YES;
    [self didChangeValueForKey:@"isExecuting"];


    if (_isCancelled == YES)
    {
        NSLog(@"** OPERATION CANCELED **");
    }
    else
    {
        NSLog(@"Operation started.");
        sleep(1);
        [self finish];
    }
}

- (void)finish
{
    NSLog(@"operationfinished.");

    [self willChangeValueForKey:@"isExecuting"];
    [self willChangeValueForKey:@"isFinished"];

    _isExecuting = NO;
    _isFinished = YES;

    [self didChangeValueForKey:@"isExecuting"];
    [self didChangeValueForKey:@"isFinished"];

    if (_isCancelled == YES)
    {
        NSLog(@"** OPERATION CANCELED **");
    }
}

في المثال الذي وجدته ، لا أفهم لماذا يتم استخدام SelectionOnMainThread: يستخدم. من شأنه أن يمنع عملي من الركض في وقت واحد.

أيضًا ، عندما أعلق هذا الخط ، أقوم بتشغيل عملياتي بشكل متزامن. ومع ذلك ، فإن isCancelled لم يتم تعديل العلم ، على الرغم من أنني اتصلت cancelAllOperations.

هل كانت مفيدة؟

المحلول

حسنًا ، كما أفهمها ، لديك سؤالان:

  1. هل تحتاج performSelectorOnMainThread: الجزء الذي يظهر في التعليقات في الكود الخاص بك؟ ماذا يفعل هذا الرمز؟

  2. لماذا هو _isCancelled لا يتم تعديل العلم عند الاتصال cancelAllOperations على ال NSOperationQueue التي تحتوي على هذه العملية؟

دعونا نتعامل مع هذه بالترتيب. سأفترض أن فئتك الفرعية NSOperation يسمى MyOperation, ، فقط لسهولة التفسير. سأشرح ما سوء الفهم ثم أعطي مثالًا مصححًا.

1. تشغيل NSOPERations بشكل متزامن

معظم الوقت ، ستستخدم NSOperationS مع NSOperationQueue, ، ومن التعليمات البرمجية الخاصة بك ، يبدو أن هذا ما تفعله. في هذه الحالة ، الخاص بك MyOperation سيتم تشغيله دائمًا على موضوع خلفية ، بغض النظر عن ما -(BOOL)isConcurrent تعود الطريقة منذ ذلك الحين NSOperationQueueتم تصميم S بشكل صريح لتشغيل العمليات في الخلفية.

على هذا النحو ، لا تحتاج عمومًا إلى تجاوز -[NSOperation start] الطريقة ، لأنه افتراضيًا ، فإنه يستدعي ببساطة -main طريقة. هذه هي الطريقة التي يجب أن تكون فيها تجاوز. الافتراضي -start الطريقة تتعامل بالفعل مع الإعداد isExecuting و isFinished لك في الأوقات المناسبة.

لذلك إذا كنت تريد NSOperation لتشغيل في الخلفية ، ببساطة تجاوز -main الطريقة ووضعها على NSOperationQueue.

ال performSelectorOnMainThread: في الكود الخاص بك سوف يتسبب في كل مثيل MyOperation لأداء مهمتها دائمًا على الموضوع الرئيسي. نظرًا لأنه يمكن تشغيل قطعة واحدة فقط من الكود على مؤشر ترابط في وقت واحد ، فهذا يعني أنه لا يوجد آخر MyOperationS يمكن أن يكون الجري. الغرض كله من NSOperation و NSOperationQueue هو القيام بشيء في الخلفية.

المرة الوحيدة التي ترغب في فرض الأشياء على الموضوع الرئيسي هي عندما تقوم بتحديث واجهة المستخدم. إذا كنت بحاجة إلى تحديث واجهة المستخدم عند MyOperation التشطيبات ، الذي - التي هو متى يجب أن تستخدم performSelectorOnMainThread:. سأوضح كيفية القيام بذلك في مثالي أدناه.

2. إلغاء nsoperation

-[NSOperationQueue cancelAllOperations] يدعو -[NSOperation cancel] الطريقة التي تسبب مكالمات لاحقة -[NSOperation isCancelled] لكي ترجع YES. لكن, ، لقد فعلت شيئين لجعل هذا غير فعال.

  1. انت تستخدم @synthesize isCancelled لتجاوز nsoperation -isCancelled طريقة. لا يوجد سبب للقيام بذلك. NSOperation تنفذ بالفعل -isCancelled بطريقة مقبولة تماما.

  2. أنت تتحقق _isCancelled متغير مثيل لتحديد ما إذا كانت العملية قد تم إلغاؤها. NSOperation يضمن ذلك [self isCancelled] سيعود YES إذا تم إلغاء العملية. نعم هو كذلك ليس ضمان أنه سيتم استدعاء طريقة Setter المخصصة ، ولا أن متغير المثيل الخاص بك محدث. يجب عليك التحقق [self isCancelled]

ماذا يجب أن تفعل

رأس:

// MyOperation.h
@interface MyOperation : NSOperation {
}
@end

والتنفيذ:

// MyOperation.m
@implementation MyOperation

- (void)main {
    if ([self isCancelled]) {
        NSLog(@"** operation cancelled **");
    }

    // Do some work here
    NSLog(@"Working... working....")

    if ([self isCancelled]) {
        NSLog(@"** operation cancelled **");
    }
    // Do any clean-up work here...

    // If you need to update some UI when the operation is complete, do this:
    [self performSelectorOnMainThread:@selector(updateButton) withObject:nil waitUntilDone:NO];

    NSLog(@"Operation finished");
}

- (void)updateButton {
    // Update the button here
}
@end

لاحظ أنك لا تحتاج إلى فعل أي شيء isExecuting, isCancelled, ، أو isFinished. يتم التعامل معها تلقائيًا من أجلك. ببساطة تجاوز -main طريقة. انه من السهل.

(ملاحظة: من الناحية الفنية ، هذا ليس "متزامنًا" NSOperation, ، بمعنى أن -[MyOperation isConcurrent] سيعود NO كما تم تنفيذها أعلاه. ومع ذلك ، هو إرادة يتم تشغيلها على موضوع خلفية. ال isConcurrent يجب تسمية الطريقة حقًا -willCreateOwnThread, ، لأن هذا هو وصف أكثر دقة لقصد الطريقة.)

نصائح أخرى

الجواب الممتاز لـ @bjhomer تستحق تحديثًا.

يجب أن تتجاوز العمليات المتزامنة start الطريقة بدلا من main.

كما ذكر في وثائق Apple:

إذا كنت تقوم بإنشاء عملية متزامنة ، فأنت بحاجة إلى تجاوز الأساليب والخصائص التالية كحد أدنى:

  • start
  • asynchronous
  • executing
  • finished

التنفيذ المناسب أيضا يستوجب لتجاوز cancel كذلك. صنع فئة فرعية آمن الخيط والحصول على الدلالات المطلوبة بشكل صحيح أيضا أمر صعب للغاية.

وبالتالي ، لقد وضعت فئة فرعية كاملة وعمل كملف الاقتراح المنفذ في Swift في مراجعة الكود. التعليقات والاقتراح موضع ترحيب.

يمكن استخدام هذه الفئة بسهولة كفئة أساسية لفئة التشغيل المخصصة.

أعلم أن هذا سؤال قديم ، لكنني كنت أقوم بالتحقيق في هذا مؤخرًا وواجهت نفس الأمثلة ولدي الشكوك نفسها.

إذا كان من الممكن تشغيل كل عملك بشكل متزامن داخل الطريقة الرئيسية ، فلن تحتاج إلى عملية متزامنة ، ولا بداية مبتكرة ، فقط قم بعملك والعودة من الرئيسية عند الانتهاء.

ومع ذلك ، إذا كان عبء العمل الخاص بك غير متزامن بطبيعته - أي تحميل nsurlconnection ، فيجب أن تبدأ الفئة الفرعية. عند عودة طريقة البدء الخاصة بك ، لم تنتهي العملية بعد. لن يتم اعتباره منتهيًا إلا من قبل NSOperationQueue عندما ترسل إخطارات KVO يدويًا إلى أعلام isfinishized و isexecuting (على سبيل المثال ، بمجرد الانتهاء من تحميل URL Async).

أخيرًا ، قد يرغب المرء في إرسال البداية إلى مؤشر الترابط الرئيسي عندما يتطلب عبء عمل Async الذي تريد البدء فيه استمع إلى حلقة التشغيل على الخيط الرئيسي. نظرًا لأن العمل نفسه غير متزامن ، فإنه لن يحد من تزامنك ، ولكن قد لا يكون لبدء العمل في مؤشر ترابط العامل جاهزًا.

ألق نظرة على asihtprequest. إنها فئة Wrapper HTTP مبنية على رأسها NSOperation كطابق فرعي ويبدو أن تنفيذ هذه. لاحظ أنه اعتبارًا من منتصف عام 2011 ، يوصي المطور بعدم استخدام ASI للمشاريع الجديدة.

بخصوص تعريف "ألغيت"خاصية (أو تعريف"_ألغيت"IVAR) داخل الفئة الفرعية NSOPeration ، عادةً ما يكون ذلك ضروريًا. وذلك ببساطة لأنه عندما يقوم المستخدم بإلغاء ، يجب على الرمز المخصص دائمًا إخطار مراقبي KVO بأن عمليتك الآن تم الانتهاء من مع عملها. بعبارات أخرى، iscancelled => isfinished.

على وجه الخصوص ، عندما يعتمد كائن NSOPeration على الانتهاء من كائنات التشغيل الأخرى ، فإنه يراقب المسار الرئيسي المسموح به لتلك الكائنات. عدم توليد إشعار النهاية (في حالة الإلغاء) وبالتالي يمكن أن تمنع تنفيذ العمليات الأخرى في التطبيق الخاص بك.


راجع للشغل ، إجابة هوميروس: "يجب أن تكون طريقة isConcurrent حقًا اسمه -willcreateWrathread "منطقي كثيرًا!

لأنه إذا لم تقم بتجاوز طريقة بدء التشغيل ، فما عليك سوى الاتصال يدويًا بالتشغيل الافتراضي لـ NSOPeration-Object ، فإن الاتصال نفسه هو ، بشكل افتراضي ، متزامن ؛ لذلك ، فإن nsoperation-object ليست سوى عملية غير متطورة.

ومع ذلك ، إذا قمت بتجاوز طريقة البدء ، فإن تنفيذ Method Inside Inside ، رمز مخصص يجب أن تفرخ خيط منفصل... إلخ ، ثم تقوم بنجاح بخرق تقييد "التزامن المتزامن" ، مما يجعل الكائنات غير المتزامنة ، يمكن أن تعمل بشكل غير متزامن بعد ذلك.

منشور المدونة هذا:

http://www.dribin.org/dave/blog/archives/2009/09/13/snowy_concurrent_operations/

يشرح لماذا قد تحتاج:

if (![NSThread isMainThread])
{
    [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
    return;
}

في الخاص بك start طريقة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top