2015-09-02 11 views
6

मैं क्लाउडकिट के साथ काम करना शुरू कर रहा हूं, इसलिए मेरे साथ भालू।क्लाउडकिट - निर्भरता के साथ CKQueryOperation

पृष्ठभूमि की जानकारी

WWDC 2015 में, सेब CloudKit https://developer.apple.com/videos/wwdc/2015/?id=715

के बारे में बात करते हैं इस बात में दिया था, वे चेनिंग प्रश्नों बनाने के खिलाफ चेतावनी दी है और इसके बजाय इस रणनीति की सिफारिश:

let firstFetch = CKFetchRecordsOperation(...) 
let secondFetch = CKFetchRecordsOperation(...) 
... 
secondFetch.addDependency(firstFetch) 

letQueue = NSOperationQueue() 
queue.addOperations([firstFetch, secondFetch], waitUntilFinished: false) 

उदाहरण संरचना

परीक्षण परियोजना डेटाबेस जानवरों और उनके मालिकों में शामिल है, यह इस तरह दिखता है:

|Pets    | |Owners  | 
|-name    | |-firstName | 
|-birthdate   | |-lastName | 
|-owner (Reference) | |   | 

मेरे प्रश्न

मैं सभी पालतू जानवरों है कि एक स्वामी का होना खोजने की कोशिश कर रहा हूँ, और मैं चिंतित हूँ मैं मैं चेन सेब बना रहा हूँ चेतावनी देता है। दो विधियों के लिए नीचे देखें जो एक ही काम करते हैं, लेकिन दो तरीकों से। कौन सा सही है या दोनों गलत हैं? मुझे लगता है कि मैं वही काम कर रहा हूं लेकिन इसके बजाय पूर्णता ब्लॉक का उपयोग कर रहा हूं।

मैं अन्य खोज BtnClick को बदलने के बारे में उलझन में हूं: निर्भरता का उपयोग करने के लिए। मैं कहाँ otherSearchBtnClick में

ownerQueryOp.addDependency(queryOp) 

से जोड़ना होगा :?

@IBAction func searchBtnClick(sender: AnyObject) { 
    var petString = "" 
    let container = CKContainer.defaultContainer() 
    let publicDatabase = container.publicCloudDatabase 
    let privateDatabase = container.privateCloudDatabase 

    let predicate = NSPredicate(format: "lastName == '\(ownerLastNameTxt.text)'") 
    let ckQuery = CKQuery(recordType: "Owner", predicate: predicate) 
    publicDatabase.performQuery(ckQuery, inZoneWithID: nil) { 
     record, error in 
     if error != nil { 
      println(error.localizedDescription) 
     } else { 
      if record != nil { 
       for owner in record { 
        let myRecord = owner as! CKRecord 
        let myReference = CKReference(record: myRecord, action: CKReferenceAction.None) 

        let myPredicate = NSPredicate(format: "owner == %@", myReference) 
        let petQuery = CKQuery(recordType: "Pet", predicate: myPredicate) 
        publicDatabase.performQuery(petQuery, inZoneWithID: nil) { 
         record, error in 
         if error != nil { 
          println(error.localizedDescription) 
         } else { 
          if record != nil { 
           for pet in record { 
            println(pet.objectForKey("name") as! String) 

           } 

          } 
         } 
        } 
       } 
      } 
     } 
    } 
} 

@IBAction func otherSearchBtnClick (sender: AnyObject) { 
    let container = CKContainer.defaultContainer() 
    let publicDatabase = container.publicCloudDatabase 
    let privateDatabase = container.privateCloudDatabase 

    let queue = NSOperationQueue() 
    let petPredicate = NSPredicate(format: "lastName == '\(ownerLastNameTxt.text)'") 
    let petQuery = CKQuery(recordType: "Owner", predicate: petPredicate) 
    let queryOp = CKQueryOperation(query: petQuery) 
    queryOp.recordFetchedBlock = { (record: CKRecord!) in 
     println("recordFetchedBlock: \(record)") 
     self.matchingOwners.append(record) 
    } 

    queryOp.queryCompletionBlock = { (cursor: CKQueryCursor!, error: NSError!) in 
     if error != nil { 
      println(error.localizedDescription) 
     } else { 
      println("queryCompletionBlock: \(cursor)") 
      println("ALL RECORDS ARE: \(self.matchingOwners)") 
      for owner in self.matchingOwners { 
       let ownerReference = CKReference(record: owner, action: CKReferenceAction.None) 
       let ownerPredicate = NSPredicate(format: "owner == %@", ownerReference) 
       let ownerQuery = CKQuery(recordType: "Pet", predicate: ownerPredicate) 
       let ownerQueryOp = CKQueryOperation(query: ownerQuery) 
       ownerQueryOp.recordFetchedBlock = { (record: CKRecord!) in 
        println("recordFetchedBlock (pet values): \(record)") 
        self.matchingPets.append(record) 
       } 
       ownerQueryOp.queryCompletionBlock = { (cursor: CKQueryCursor!, error: NSError!) in 
        if error != nil { 
         println(error.localizedDescription) 
        } else { 
         println("queryCompletionBlock (pet values)") 
         for pet in self.matchingPets { 
          println(pet.objectForKey("name") as! String) 
         } 
        } 
       } 
      publicDatabase.addOperation(ownerQueryOp) 
      } 
     } 


    } 
    publicDatabase.addOperation(queryOp) 
} 
+0

क्या कोई जवाब नहीं है क्योंकि मैं अब तक बेस से दूर हूं, मैं निराश हूं ?! – Charlie

उत्तर

1

सिद्धांत में आपके पास एकाधिक मालिक हो सकते हैं और इसलिए कई निर्भरताएं हो सकती हैं। बाहरी क्वेरी पहले ही निष्पादित होने के बाद भी आंतरिक प्रश्न बनाए जाएंगे। निर्भरता बनाने के लिए आपको बहुत देर हो जाएगी। आपके मामले में यह इस तरह एक अलग कतार में आंतरिक प्रश्नों के निष्पादन के लिए मजबूर करने की शायद आसान है:

if record != nil { 
    for owner in record { 
     NSOperationQueue.mainQueue().addOperationWithBlock { 

इस तरह से आप यह सुनिश्चित करें कि हर भीतरी क्वेरी एक नई पंक्ति पर और मतलब समय में निष्पादित किया जाएगा कर देगा वह मूल प्रश्न खत्म हो सकता है।

कुछ और: अपना कोड क्लीनर बनाने के लिए, यह बेहतर होगा अगर लूप के अंदर का सभी कोड एक पैरामीटर के रूप में सीकेआरफरेंस के साथ एक अलग कार्य में था।

0

मैं हाल ही में एक ही समस्या थी और एक NSBlockOperation का उपयोग कर दूसरी क्वेरी तैयार करने के लिए समाप्त हो गया और यह सब काम करने के लिए एक निर्भरता कहा:

let container = CKContainer.defaultContainer() 
    let publicDB = container.publicCloudDatabase 
    let operationqueue = NSOperationQueue.mainQueue() 

    let familyPredicate = NSPredicate(format: "name == %@", argumentArray: [familyName]) 
    let familyQuery = CKQuery(recordType: "Familias", predicate: familyPredicate) 
    let fetchFamilyRecordOp = CKQueryOperation(query: familyQuery) 


    fetchFamilyRecordOp.recordFetchedBlock = { record in 

     familyRecord = record 
    } 
    let fetchMembersOP = CKQueryOperation() 

    // Once we have the familyRecord, we prepare the PersonsFetch 
    let prepareFamilyRef = NSBlockOperation() { 
     let familyRef = CKReference(record: familyRecord!, action: CKReferenceAction.None) 
     let familyRecordID = familyRef?.recordID 

     let membersPredicate = NSPredicate(format: "familia == %@", argumentArray: [familyRecordID!]) 
     let membersQuery = CKQuery(recordType: "Personas", predicate: membersPredicate) 
     fetchMembersOP.query = membersQuery 

    } 
    prepareFamilyRef.addDependency(fetchFamilyRecordOp) 
    fetchMembersOP.recordFetchedBlock = { record in 
     members.append(record) 
    } 

    fetchMembersOP.addDependency(prepareFamilyRef) 
    fetchMembersOP.database = publicDB 
    fetchFamilyRecordOp.database = publicDB 
    operationqueue.addOperations([fetchFamilyRecordOp, fetchMembersOP, prepareFamilyRef], waitUntilFinished: false) 

और जैसा कि मैं उम्मीद अब यह काम कर रहा है, क्योंकि आप सेट कर सकते हैं अपने परिचालन को बहुत ही गहन तरीके से बढ़ाएं और वे सही क्रम में निष्पादित करें ^।^

अपने मामले में

मैं इस तरह यह संरचना होगा:

let predicate = NSPredicate(format: "lastName == '\(ownerLastNameTxt.text)'") 
let ckQuery = CKQuery(recordType: "Owner", predicate: predicate) 
let getOwnerOperation = CKQueryOperation(query: ckQuery) 
getOwnerOperation.recordFetchedBlock = { record in 
let name = record.valueForKey("name") as! String 
if name == myOwnerName { 
     ownerRecord = record 
    } 
} 
//now we have and operation that will save in our var OwnerRecord the record that is exactly our owner 
//now we create another that will fetch our pets 
let queryPetsForOurOwner = CKQueryOperation() 
queryPetsForOurOwner.recordFetchedBlock = { record in 
    results.append(record) 
} 
//That's all this op has to do, BUT it needs the owner operation to be completed first, but not inmediately, we need to prepare it's query first so: 
var fetchPetsQuery : CKQuery? 
let preparePetsForOwnerQuery = NSBlockOperation() { 
let myOwnerRecord = ownerRecord! 
let ownerRef = CKReference(record: myOwnerRecord, action: CKReferenceAction.None) 
       let myPredicate = NSPredicate(format: "owner == %@", myReference) 
       fetchPetsQuery = CKQuery(recordType: "Pet", predicate: myPredicate) 

    } 
    queryPetsForOurOwner.query = fetchPetsQuery 
preparePetsForOwnerQuery.addDependency(getOwnerOperation) 
    queryPetsForOurOwner.addDependency(preparePetsForOwnerQuery) 

और अब यह सब किया जाना चाहिए उन्हें नव निर्मित आपरेशन कतार में जोड़ने के लिए के बाद हम उन्हें हमारे डेटाबेस

करने के लिए प्रत्यक्ष है
getOwnerOperation.database = publicDB 
queryPetsForOurOwner.database = publicDB 
let operationqueue = NSOperationQueue.mainQueue() 
operationqueue.addOperations([getOwnerOperation, queryPetsForOurOwner, preparePetsForOwnerQuery], waitUntilFinished: false) 

पुनश्च: मैं मैंने कहा परिवार और व्यक्ति और नाम उस तरह नहीं कर रहे हैं पता है, लेकिन मैं स्पेनिश और कुछ cloudkit संचालन का परीक्षण कर रहा हूँ, इसलिए मैं अंग्रेजी recor प्रकार के नाम करने के लिए अभी तक मानकीकृत नहीं है;)

+0

मुझे लगता है कि आप queryPwnOurOwner पर fetchPetsQuery को सेट करने के लिए getOwnerOperation पर queryCompletionBlock का उपयोग कर सकते हैं। डब्ल्यूडब्ल्यूडीसी 2015 की बातचीत में उन्होंने कहा कि ऑपरेशन समाप्त होने से पहले उन कस्टम समापन ब्लॉक होते हैं और उन्होंने विशेष रूप से कहा कि अगली ऑपरेशन पर डेटा सेट करने का समय है। तो इससे आपको ब्लॉक ऑपरेशन बनाने में मदद मिलेगी। यह सब सिद्धांत है मैंने अभी तक कोशिश नहीं की है। – malhal

2

यदि आपको रद्दीकरण की आवश्यकता नहीं है और नेटवर्क त्रुटि पर पुनः प्रयास करने के बारे में परेशान नहीं हैं तो मुझे लगता है कि आप प्रश्नों को ठीक कर रहे हैं।

मुझे पता है कि मुझे पता है, डब्ल्यूडब्ल्यूडीसी 2015 में निहार शर्मा ने एड निर्भरता दृष्टिकोण की सिफारिश की लेकिन ऐसा लगता है कि उन्होंने बिना किसी विचार के अंत में फेंक दिया। आप देखते हैं कि एनएसओपरेशन को पुनः प्रयास करना संभव नहीं है क्योंकि वे वैसे भी एक शॉट हैं, और उन्होंने कतार में पहले से ही ऑपरेशन रद्द करने के लिए कोई उदाहरण नहीं दिया है, या अगले ऑपरेशन से डेटा को कैसे पास किया जाए। इन 3 जटिलताओं को देखते हुए आपको हल करने में सप्ताह लग सकते हैं, बस जो भी आप काम कर रहे हैं उसके साथ चिपके रहें और अपने समाधान के लिए अगले डब्ल्यूडब्ल्यूडीसी की प्रतीक्षा करें। इसके अलावा ब्लॉक का पूरा बिंदु आपको इनलाइन विधियों को कॉल करने और उपर्युक्त विधि में पैराम तक पहुंचने में सक्षम होना है, इसलिए यदि आप संचालन में जाते हैं तो आपको उस लाभ का पूर्ण लाभ नहीं मिलता है।

चेनिंग का उपयोग न करने का उनका मुख्य कारण हास्यास्पद है कि वह यह नहीं बता सकता कि किस त्रुटि के लिए वह त्रुटि है, उसने अपनी त्रुटियों को नाम दिया था, फिर अन्य त्रुटि इत्यादि। किसी भी उनके दिमाग में कोई भी ब्लॉक ब्लॉक के अंदर त्रुटि पैरा तो बस उन सभी के लिए एक ही नाम का उपयोग करें और फिर आप उस ब्लॉक के अंदर जानते हैं जो आप हमेशा सही त्रुटि का उपयोग कर रहे हैं। इस प्रकार वह वह था जिसने अपना गन्दा परिदृश्य बनाया और इसके लिए एक समाधान की पेशकश की, हालांकि सबसे अच्छा समाधान सिर्फ पहले त्रुटि पैरामीटर नामों के गन्दा परिदृश्य को नहीं बना रहा है!

कि सभी के साथ कहा जा रहा है, के मामले में आप अभी भी आपरेशन निर्भरता का उपयोग करने की कोशिश करना चाहते यहाँ यह कैसे किया जा सकता का एक उदाहरण है:

__block CKRecord* venueRecord; 
CKRecordID* venueRecordID = [[CKRecordID alloc] initWithRecordName:@"4c31ee5416adc9282343c19c"]; 
CKFetchRecordsOperation* fetchVenue = [[CKFetchRecordsOperation alloc] initWithRecordIDs:@[venueRecordID]]; 
fetchVenue.database = [CKContainer defaultContainer].publicCloudDatabase; 

// init a fetch for the category, it's just a placeholder just now to go in the operation queue and will be configured once we have the venue. 
CKFetchRecordsOperation* fetchCategory = [[CKFetchRecordsOperation alloc] init]; 

[fetchVenue setFetchRecordsCompletionBlock:^(NSDictionary<CKRecordID *,CKRecord *> * _Nullable recordsByRecordID, NSError * _Nullable error) { 
    venueRecord = recordsByRecordID.allValues.firstObject; 
    CKReference* ref = [venueRecord valueForKey:@"category"]; 

    // configure the category fetch 
    fetchCategory.recordIDs = @[ref.recordID]; 
    fetchCategory.database = [CKContainer defaultContainer].publicCloudDatabase; 
}]; 

[fetchCategory setFetchRecordsCompletionBlock:^(NSDictionary<CKRecordID *,CKRecord *> * _Nullable recordsByRecordID, NSError * _Nullable error) { 
    CKRecord* categoryRecord = recordsByRecordID.allValues.firstObject; 

    // here we have a venue and a category so we could call a completion handler with both. 
}]; 

NSOperationQueue* queue = [[NSOperationQueue alloc] init]; 
[fetchCategory addDependency:fetchVenue]; 
[queue addOperations:@[fetchVenue, fetchCategory] waitUntilFinished:NO]; 

यह कैसे काम करता है यह एक स्थान रिकॉर्ड vetches पहला है, तो यह अपनी श्रेणी लाता है।

क्षमा करें कोई त्रुटि प्रबंधन नहीं है, लेकिन जैसा कि आप देख सकते हैं कि यह पहले से ही कोड का एक टन था जो कुछ लाइनों में चेनिंग के साथ किया जा सकता है। और व्यक्तिगत रूप से मुझे यह विकल्प आसानी से सुविधा विधियों को एक साथ जोड़कर अधिक दृढ़ और भ्रमित लगता है।

संबंधित मुद्दे