Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 国产视频大全,日韩中文在线视频,好男人的最好的影院在线观看

          整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          文件上傳總結

          文件上傳總結

          戶端

          js檢查

          一般都是在網頁上寫一段javascript腳本,校驗上傳文件的后綴名,有白名單形式也有黑名單形式。

          查看源代碼可以看到有如下代碼對上傳文件類型進行了限制:

          <script type="text/javascript"> function checkFile() {
                  var file=document.getElementsByName('upload_file')[0].value;
                  if (file==null || file=="") {
                      alert("請選擇要上傳的文件!");
                      return false;
                  }
                  //定義允許上傳的文件類型
                  var allow_ext=".jpg|.png|.gif";
                  //提取上傳文件的類型
                  var ext_name=file.substring(file.lastIndexOf("."));
                  //判斷上傳文件類型是否允許上傳
                  if (allow_ext.indexOf(ext_name)==-1) {
                      var errMsg="該文件不允許上傳,請上傳" + allow_ext + "類型的文件,當前文件類型為:" + ext_name;
                      alert(errMsg);
                      return false;
                  }
              } </script>

          我們可以看到對上傳文件類型進行了限制。

          繞過方法

          1. 我們直接刪除代碼中onsubmit事件中關于文件上傳時驗證上傳文件的相關代碼即可。

          或者可以不加載所有js,還可以將html源碼copy一份到本地,然后對相應代碼進行修改,本地提交即可。

          1. burp改包,由于是js驗證,我們可以先將文件重命名為js允許的后綴名,在用burp發送數據包時候改成我們想要的后綴。

          即可上傳成功:

          服務端

          黑名單

          特殊可解析后綴

              if (file_exists(UPLOAD_PATH)) {
                  $deny_ext=array('.asp','.aspx','.php','.jsp');
                  $file_name=trim($_FILES['upload_file']['name']);
                  $file_name=deldot($file_name);//刪除文件名末尾的點
                  $file_ext=strrchr($file_name, '.');
                  $file_ext=strtolower($file_ext); //轉換為小寫
                  $file_ext=str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
                  $file_ext=trim($file_ext); //收尾去空
          
                  if(!in_array($file_ext, $deny_ext)) {
                      $temp_file=$_FILES['upload_file']['tmp_name'];
                      $img_path=UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;            
                      if (move_uploaded_file($temp_file,$img_path)) {
                           $is_upload=true;
                      } else {
                          $msg='上傳出錯!';
                      }
                  } else {
                      $msg='不允許上傳.asp,.aspx,.php,.jsp后綴文件!';
                  }
              } else {
                  $msg=UPLOAD_PATH . '文件夾不存在,請手工創建!';
              }

          這里做了黑名單處理,我們可以通過特殊可解析后綴進行繞過。

          繞過方法

          之前在https://www.jianshu.com/p/1ccbab572974中總結過,這里不再贅述,可以使用php3,phtml等繞過。

          上傳.htaccess

          if (isset($_POST['submit'])) {
              if (file_exists(UPLOAD_PATH)) {
                  $deny_ext=array(".php",".php5",".php4",".php3",".php2","php1",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2","pHp1",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf");
                  $file_name=trim($_FILES['upload_file']['name']);
                  $file_name=deldot($file_name);//刪除文件名末尾的點
                  $file_ext=strrchr($file_name, '.');
                  $file_ext=strtolower($file_ext); //轉換為小寫
                  $file_ext=str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
                  $file_ext=trim($file_ext); //收尾去空
          
                  if (!in_array($file_ext, $deny_ext)) {
                      $temp_file=$_FILES['upload_file']['tmp_name'];
                      $img_path=UPLOAD_PATH.'/'.$file_name;
                      if (move_uploaded_file($temp_file, $img_path)) {
                          $is_upload=true;
                      } else {
                          $msg='上傳出錯!';
                      }
                  } else {
                      $msg='此文件不允許上傳!';
                  }
              } else {
                  $msg=UPLOAD_PATH . '文件夾不存在,請手工創建!';
              }
          }
          ?>

          我們發現黑名單限制了很多后綴名,但是沒有限制.htaccess

          .htaccess文件是Apache服務器中的一個配置文件,它負責相關目錄下的網頁配置.通過htaccess文件,可以實現:網頁301重定向、自定義404頁面、改變文件擴展名、允許/阻止特定的用戶或者目錄的訪問、禁止目錄列表、配置默認文檔等功能。

          繞過方法

          我們需要上傳一個.htaccess文件,內容為:

          SetHandler application/x-httpd-php
          

          這樣所有的文件都會解析為php,接下來上傳圖片馬即可

          后綴大小寫繞過

          if (isset($_POST['submit'])) {
              if (file_exists(UPLOAD_PATH)) {
                  $deny_ext=array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
                  $file_name=trim($_FILES['upload_file']['name']);
                  $file_name=deldot($file_name);//刪除文件名末尾的點
                  $file_ext=strrchr($file_name, '.');
                  $file_ext=str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
                  $file_ext=trim($file_ext); //首尾去空
          
                  if (!in_array($file_ext, $deny_ext)) {
                      $temp_file=$_FILES['upload_file']['tmp_name'];
                      $img_path=UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
                      if (move_uploaded_file($temp_file, $img_path)) {
                          $is_upload=true;
                      } else {
                          $msg='上傳出錯!';
                      }
                  } else {
                      $msg='此文件類型不允許上傳!';
                  }
              } else {
                  $msg=UPLOAD_PATH . '文件夾不存在,請手工創建!';
              }
          }

          我們發現對.htaccess也進行了檢測,但是沒有對大小寫進行統一。

          繞過方法

          后綴名改為PHP即可

          空格繞過

          if (isset($_POST['submit'])) {
              if (file_exists(UPLOAD_PATH)) {
                  $deny_ext=array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
                  $file_name=$_FILES['upload_file']['name'];
                  $file_name=deldot($file_name);//刪除文件名末尾的點
                  $file_ext=strrchr($file_name, '.');
                  $file_ext=strtolower($file_ext); //轉換為小寫
                  $file_ext=str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
                  
                  if (!in_array($file_ext, $deny_ext)) {
                      $temp_file=$_FILES['upload_file']['tmp_name'];
                      $img_path=UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
                      if (move_uploaded_file($temp_file,$img_path)) {
                          $is_upload=true;
                      } else {
                          $msg='上傳出錯!';
                      }
                  } else {
                      $msg='此文件不允許上傳';
                  }
              } else {
                  $msg=UPLOAD_PATH . '文件夾不存在,請手工創建!';
              }

          黑名單沒有對文件中的空格進行處理,可在后綴名中加空格繞過。

          繞過方法

          點繞過

          if (isset($_POST['submit'])) {
              if (file_exists(UPLOAD_PATH)) {
                  $deny_ext=array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
                  $file_name=trim($_FILES['upload_file']['name']);
                  $file_ext=strrchr($file_name, '.');
                  $file_ext=strtolower($file_ext); //轉換為小寫
                  $file_ext=str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
                  $file_ext=trim($file_ext); //首尾去空
                  
                  if (!in_array($file_ext, $deny_ext)) {
                      $temp_file=$_FILES['upload_file']['tmp_name'];
                      $img_path=UPLOAD_PATH.'/'.$file_name;
                      if (move_uploaded_file($temp_file, $img_path)) {
                          $is_upload=true;
                      } else {
                          $msg='上傳出錯!';
                      }
                  } else {
                      $msg='此文件類型不允許上傳!';
                  }
              } else {
                  $msg=UPLOAD_PATH . '文件夾不存在,請手工創建!';
              }

          windows會對文件中的點進行自動去除,所以可以在文件末尾加點繞過,不再贅述

          ::$DATA繞過

          同windows特性,可在后綴名中加” ::$DATA”繞過,不再贅述

          路徑拼接繞過

          if (isset($_POST['submit'])) {
              if (file_exists(UPLOAD_PATH)) {
                  $deny_ext=array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
                  $file_name=trim($_FILES['upload_file']['name']);
                  $file_name=deldot($file_name);//刪除文件名末尾的點
                  $file_ext=strrchr($file_name, '.');
                  $file_ext=strtolower($file_ext); //轉換為小寫
                  $file_ext=str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
                  $file_ext=trim($file_ext); //首尾去空
                  
                  if (!in_array($file_ext, $deny_ext)) {
                      $temp_file=$_FILES['upload_file']['tmp_name'];
                      $img_path=UPLOAD_PATH.'/'.$file_name;
                      if (move_uploaded_file($temp_file, $img_path)) {
                          $is_upload=true;
                      } else {
                          $msg='上傳出錯!';
                      }
                  } else {
                      $msg='此文件類型不允許上傳!';
                  }
              } else {
                  $msg=UPLOAD_PATH . '文件夾不存在,請手工創建!';
              }
          }

          這里對文件名進行了處理,刪除了文件名末尾的點,并且把處理過的文件名拼接到路徑中。

          繞過方法

          這里我們可以構造文件名1.PHP. . (點+空格+點),經過處理后,文件名變成1.PHP.,即可繞過。

          雙寫繞過

              if (file_exists(UPLOAD_PATH)) {
                  $deny_ext=array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");
          
                  $file_name=trim($_FILES['upload_file']['name']);
                  $file_name=str_ireplace($deny_ext,"", $file_name);
                  $temp_file=$_FILES['upload_file']['tmp_name'];
                  $img_path=UPLOAD_PATH.'/'.$file_name;        
                  if (move_uploaded_file($temp_file, $img_path)) {
                      $is_upload=true;
                  } else {
                      $msg='上傳出錯!';
                  }
              } else {
                  $msg=UPLOAD_PATH . '文件夾不存在,請手工創建!';
              }
          }

          繞過方法

          這里我們可以看到將文件名替換為空,我們可以采用雙寫繞過:1.pphphp

          白名單

          MIME檢查

          if (isset($_POST['submit'])) {
              if (file_exists(UPLOAD_PATH)) {
                  if (($_FILES['upload_file']['type']=='image/jpeg') || ($_FILES['upload_file']['type']=='image/png') || ($_FILES['upload_file']['type']=='image/gif')) {
                      $temp_file=$_FILES['upload_file']['tmp_name'];
                      $img_path=UPLOAD_PATH . '/' . $_FILES['upload_file']['name'];          
                      if (move_uploaded_file($temp_file, $img_path)) {
                          $is_upload=true;
                      } else {
                          $msg='上傳出錯!';
                      }
                  } else {
                      $msg='文件類型不正確,請重新上傳!';
                  }
              } else {
                  $msg=UPLOAD_PATH.'文件夾不存在,請手工創建!';
              }

          繞過方法

          這里檢查Content-type,我們burp抓包修改即可繞過:

          %00 截斷

          if(isset($_POST['submit'])){
              $ext_arr=array('jpg','png','gif');
              $file_ext=substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
              if(in_array($file_ext,$ext_arr)){
                  $temp_file=$_FILES['upload_file']['tmp_name'];
                  $img_path=$_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
          
                  if(move_uploaded_file($temp_file,$img_path)){
                      $is_upload=true;
                  } else {
                      $msg='上傳出錯!';
                  }
              } else{
                  $msg="只允許上傳.jpg|.png|.gif類型文件!";
              }
          }

          $img_path直接拼接,因此可以利用%00截斷繞過

          繞過方法

          然后直接訪問/upload/1.php即可


          00截斷(post)

          if(isset($_POST['submit'])){
              $ext_arr=array('jpg','png','gif');
              $file_ext=substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
              if(in_array($file_ext,$ext_arr)){
                  $temp_file=$_FILES['upload_file']['tmp_name'];
                  $img_path=$_POST['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
          
                  if(move_uploaded_file($temp_file,$img_path)){
                      $is_upload=true;
                  } else {
                      $msg="上傳失敗";
                  }
              } else {
                  $msg="只允許上傳.jpg|.png|.gif類型文件!";
              }
          }
          ?>

          save_path是通過post傳進來的,還是利用00截斷,但這次需要在二進制中進行修改,因為post不會像get對%00進行自動解碼。

          繞過方法



          接下來訪問1.php即可

          文件內容檢查

          文件幻數檢測

          主要是檢測文件內容開始處的文件幻數,比如圖片類型的文件幻數如下,
          要繞過jpg 文件幻數檢測就要在文件開頭寫上下圖的值:


          Value=FF D8 FF E0 00 10 4A 46 49 46

          要繞過gif 文件幻數檢測就要在文件開頭寫上下圖的值



          Value=47 49 46 38 39 61
          要繞過png 文件幻數檢測就要在文件開頭寫上下面的值



          Value=89 50 4E 47

          然后在文件幻數后面加上自己的一句話木馬代碼就行了

          文件相關信息檢測

          圖像文件相關信息檢測常用的就是getimagesize()函數

          只需要把文件頭部分偽造好就ok 了,就是在幻數的基礎上還加了一些文件信息

          有點像下面的結構

          GIF89a
          (...some binary data for image...)
          <?php phpinfo(); ?>
          (... skipping the rest of binary data ...)

          本次環境中的文件頭檢測,getimagesize,php_exif都可以用圖片馬繞過:

          copy normal.jpg /b + shell.php /a webshell.jpg
          

          文件加載檢測

          一般是調用API 或函數去進行文件加載測試,常見的是圖像渲染測試,甚至是進行二次渲染(過濾效果幾乎最強)。對渲染/加載測試的攻擊方式是代碼注入繞過,對二次渲染的攻擊方式是攻擊文件加載器自身。

          對渲染/加載測試攻擊- 代碼注入繞過

          可以用圖像處理軟件對一張圖片進行代碼注入

          用winhex 看數據可以分析出這類工具的原理是

          在不破壞文件本身的渲染情況下找一個空白區進行填充代碼,一般會是圖片的注釋區

          對于渲染測試基本上都能繞過,畢竟本身的文件結構是完整的

          二次渲染

          imagecreatefromjpeg二次渲染它相當于是把原本屬于圖像數據的部分抓了出來,再用自己的API 或函數進行重新渲染在這個過程中非圖像數據的部分直接就隔離開了

          if (isset($_POST['submit'])){
              // 獲得上傳文件的基本信息,文件名,類型,大小,臨時文件路徑
              $filename=$_FILES['upload_file']['name'];
              $filetype=$_FILES['upload_file']['type'];
              $tmpname=$_FILES['upload_file']['tmp_name'];
          
              $target_path=UPLOAD_PATH.basename($filename);
          
              // 獲得上傳文件的擴展名
              $fileext=substr(strrchr($filename,"."),1);
          
              //判斷文件后綴與類型,合法才進行上傳操作
              if(($fileext=="jpg") && ($filetype=="image/jpeg")){
                  if(move_uploaded_file($tmpname,$target_path))
                  {
                      //使用上傳的圖片生成新的圖片
                      $im=imagecreatefromjpeg($target_path);
          
                      if($im==false){
                          $msg="該文件不是jpg格式的圖片!";
                          @unlink($target_path);
                      }else{
                          //給新圖片指定文件名
                          srand(time());
                          $newfilename=strval(rand()).".jpg";
                          $newimagepath=UPLOAD_PATH.$newfilename;
                          imagejpeg($im,$newimagepath);
                          //顯示二次渲染后的圖片(使用用戶上傳圖片生成的新圖片)
                          $img_path=UPLOAD_PATH.$newfilename;
                          @unlink($target_path);
                          $is_upload=true;
                      }
                  } else {
                      $msg="上傳出錯!";
                  }
          
              }else if(($fileext=="png") && ($filetype=="image/png")){
                  if(move_uploaded_file($tmpname,$target_path))
                  {
                      //使用上傳的圖片生成新的圖片
                      $im=imagecreatefrompng($target_path);
          
                      if($im==false){
                          $msg="該文件不是png格式的圖片!";
                          @unlink($target_path);
                      }else{
                           //給新圖片指定文件名
                          srand(time());
                          $newfilename=strval(rand()).".png";
                          $newimagepath=UPLOAD_PATH.$newfilename;
                          imagepng($im,$newimagepath);
                          //顯示二次渲染后的圖片(使用用戶上傳圖片生成的新圖片)
                          $img_path=UPLOAD_PATH.$newfilename;
                          @unlink($target_path);
                          $is_upload=true;               
                      }
                  } else {
                      $msg="上傳出錯!";
                  }
          
              }else if(($fileext=="gif") && ($filetype=="image/gif")){
                  if(move_uploaded_file($tmpname,$target_path))
                  {
                      //使用上傳的圖片生成新的圖片
                      $im=imagecreatefromgif($target_path);
                      if($im==false){
                          $msg="該文件不是gif格式的圖片!";
                          @unlink($target_path);
                      }else{
                          //給新圖片指定文件名
                          srand(time());
                          $newfilename=strval(rand()).".gif";
                          $newimagepath=UPLOAD_PATH.$newfilename;
                          imagegif($im,$newimagepath);
                          //顯示二次渲染后的圖片(使用用戶上傳圖片生成的新圖片)
                          $img_path=UPLOAD_PATH.$newfilename;
                          @unlink($target_path);
                          $is_upload=true;
                      }
                  } else {
                      $msg="上傳出錯!";
                  }
              }else{
                  $msg="只允許上傳后綴為.jpg|.png|.gif的圖片文件!";
              }
          }

          本關綜合判斷了后綴名、content-type,以及利用imagecreatefromgif判斷是否為gif圖片,最后再做了一次二次渲染。

          繞過方法

          得去找圖片經過GD庫轉化后沒有改變的部分,再將未改變的部分修改為相應的php代碼。

          條件競爭

          if(isset($_POST['submit'])){
              $ext_arr=array('jpg','png','gif');
              $file_name=$_FILES['upload_file']['name'];
              $temp_file=$_FILES['upload_file']['tmp_name'];
              $file_ext=substr($file_name,strrpos($file_name,".")+1);
              $upload_file=UPLOAD_PATH . '/' . $file_name;
          
              if(move_uploaded_file($temp_file, $upload_file)){
                  if(in_array($file_ext,$ext_arr)){
                       $img_path=UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext;
                       rename($upload_file, $img_path);
                       $is_upload=true;
                  }else{
                      $msg="只允許上傳.jpg|.png|.gif類型文件!";
                      unlink($upload_file);
                  }
              }else{
                  $msg='上傳出錯!';
              }
          }

          這里先將文件上傳到服務器,然后通過rename修改名稱,再通過unlink刪除文件,因此可以通過條件競爭的方式在unlink之前,訪問webshell。

          繞過方法


          然后不斷訪問webshell:

          上傳成功。

          參考鏈接:

          1. https://blog.csdn.net/Kevinhanser/article/details/81613003
          2. https://secgeek.net/bookfresh-vulnerability/
          3. https://xz.aliyun.com/t/2435

          2人點贊

          知識歸納

          introduction

          Swift 是一種適用于iOS/macOS應用開發、服務器端的編程語言。自2014年蘋果發布 Swift 語言以來,Swift5 實現了 ABI 穩定性、Module 穩定性和Library Evolution,與Objective-C(下文簡稱“OC”)相比,Swift 在開發效率、安全、編譯優化、運行性能和內存管理方面具有顯著優勢。(官方博客:https://www.swift.org/about/)

          百度App 已在工程和環境上支持 Swift 開發,百度搜索大前端團隊負責搜索服務的穩定落地,我們積極探索 Swift的應用,希望能大幅提升開發效率和靈活性、提升端用戶的搜索體驗。然而,在實施過程中可能會遇到各種問題,例如代碼陳舊且不支持Swift,人員對Swift掌握不夠熟練、意識不足,協作方對Swift的支持不足等。

          對于其他語言來說,Swift相對年輕,我們在實踐過程中整理一些常見問題及其解決方法,希望能幫助讀者更順利地使用Swift進行編程,提高研發效率。


          全文6947字,預計閱讀時間18分鐘。

          GEEK TALK

          01

          Swift 適用場景

          在決定是否引入Swift前,我們需要判斷場景是否適合。通常情況下,可以用OC的場景均適合使用Swift,但也有一些不太適合直接替換的場景,需要慎重,比如:

          1、涉及OC動態性,頻繁在runtime時操作屬性和方法;

          2、核心基礎功能,出現問題影響面較大的邏輯;

          3、調用C++(目前Swift不能直接調用C++);

          4、繼承不支持Swift組件的類。

          此外,對使用OC比較久遠的工程,使用Swift前也應注意:

          1、能在工程環境和單獨模塊上支持Swift;

          2、模塊較多的工程,可以內外OC和Swift混編;

          3、為了避免Swift Waring帶來的潛在問題,可以把SWIFT_TREAT_WARNINGS_AS_ERRORS設置為YES,這樣警告會作為錯誤,輔助程序員更好的規范代碼;

          4、模塊Module化后,要注意維護 umbrella header 中的公開頭文件。

          △注:本文中的“組件”均指代工程中的“Target”。


          GEEK TALK

          02

          Swift的基本用法

          2.1 Swift 的字符串為什么這么難用?

          如:字符串不能通過索引取字符

          • 原因:Swift認為字符串是由一個個字形群集 (grapheme clusters)組成的,字形群集的大小不固定所以不能用整數去索引 (字形群集其實就是Swift中的Character(字符)類)。
          • 解決方案:如要通過下標取字符可以為String添加擴展在下標subscript實現通過傳入Int索引,在subscript轉為String.index獲取對應字符的方式。

          2.2 try try? try! 的區別

          當你進行文件操作時,可能會遇到需要使用try、try?和try!的情況。它們在異常處理方面有所不同。

          1、使用try時,如果出現異常,程序會進入異常處理流程,你可以在catch語句塊中處理這個異常。

          2、使用try?時,如果發生異常,它不會進入異常處理流程,而是返回一個可選值類型。也就是說,如果出現異常,它將返回nil。

          3、使用try!時,它不允許異常繼續傳播。一旦出現異常,程序會立即停止執行。

          因此,在文件操作中,你可以根據需要選擇合適的異常處理方式。在百度App中一般推薦使用try?。


          2.3 public 和 open 的區別

          在Swift語言中,public和open都是用于在模塊中聲明需要對外界暴露的函數的關鍵字,但它們在繼承和公開程度上有所不同。

          1、public關鍵字修飾的類在模塊外部無法被繼承。這意味著,如果其他模塊試圖繼承這個類,編譯器會報錯。這樣的限制可以保護類的完整性,但也可能限制了其在其他模塊中的可重用性。

          2、open關鍵字則允許任意繼承。如果一個類被open關鍵字修飾,那么其他模塊中的類可以自由地繼承這個類,不受任何限制。這樣的公開程度使得open關鍵字修飾的類在模塊間的重用性和擴展性更加靈活。

          從公開程度上來說,public的限制比open更嚴格,所以可以說public < open,即public的公開程度比open要低。

          2.4 解析JSON情況

          在Swift中解析JSON的情況,如果自行將JSON轉換為字典,需要涉及到類型判斷、轉換等操作,代碼比較復雜。這時可以使用第三方庫SwiftyJSON、ObjectMapper或者系統庫JSONEncoder來簡化操作,提高開發效率。

          2.5 UIView子類必須添加init?(coder decoder: NSCoder)的原因

          1、這是NSCoding protocol定義的,遵守了NSCoding protocol的所有類必須繼承。只是有的情況會隱式繼承,而有的情況下需要顯示實現。

          2、當我們在子類定義了指定初始化器(包括自定義和重寫父類指定初始化器),那么必須顯示實現required init?(coder aDecoder: NSCoder),而其他情況下則會隱式繼承,我們可以不用理會。

          3、當我們使用storyboard實現界面的時候,程序會調用這個初始化器。

          4、注意要去掉fatalError,fatalError的意思是無條件停止執行并打印。

          2.6 Swift類和子類的初始化

          Swift的類和子類初始化涉及到兩個關鍵階段。首先,確保所有的存儲屬性被賦予初始值,然后,在實例準備使用之前,可以自定義存儲屬性的值。為了確保這兩個階段成功,實施了四步安全檢查,詳細如下:

          1、在完成本類所有存儲屬性賦值之后,指定構造器才能向上代理到父類的構造器。

          2、在為繼承的屬性設置新值之前,指定構造器必須向上代理調用父類構造器。

          3、便利構造器必須先調用其他構造器,再為任意屬性(包括所有同類中定義的)賦新值。

          4、在第一階段構造完成之前,構造器不能調用任何實例方法,不能讀取任何實例屬性的值,不能引用self作為一個值。

          總之,類初始化必須完成的一個任務就是讓所有的存儲屬性都有初始值(optional 除外)。如果父類有指定初始化,子類必須也有指定初始化,并且必須調用父類的其中一個指定初始化(如果是必須初始化,就是重載),并遵循兩段式初始化的規則。一個便利初始化必須調用同一類中的初始化方法(可以是另一個便利初始化,也可以是指定初始化),但最終一定會調用到一個指定初始化。便利初始化不遵循兩段式初始化的規則,不能被子類調用或者重載。


          GEEK TALK

          03

          OC與Swift的互相調用及跳轉

          3.1 組件內Swift文件調用公開OC頭文件

          • 將公開OC頭文件(如:xyz.h)添加到組件(如:ABC)umbrella header中(如:#import);
          • Swift文件中直接調用公開OC頭文件內容。

          3.2 組件內Swift文件調用非公開(私有)的OC文件

          組件應該盡可能少的公開暴露頭文件,但Swift和OC混編不可避免使用OC非公開頭文件,因此我們可以采取以下措施:將Framework 中將私有頭文件聲明為一個私有 module(modulemap內聲明),由組件內的 Swift 源碼 import 該私有 module 即可。

          1、創建Private.modulemap文件,以NewModule做為組件名為例,可以命名為NewModule.private.modulemap,內容為下,module后面加_Private

          • 羅列頭文件的形式
          framework module NewModule_Private {
            header "xxxxx.h"
          }
          • 使用根頭文件的形式,添加頭文件NewModule_Private.h
          framework module NewModule_Private {
            umbrella header "NewModule_Private.h"
          
          
            export *
            module * { export * }
          }

          2、在組件build settings中配置MODULEMAP_PRIVATE_FILE路徑,MODULEMAP_PRIVATE_FILE='NewModule.private.modulemap';百度App中在NewModule.boxspec中如下代碼設置路徑;

          s.xcconfig={
              'MODULEMAP_PRIVATE_FILE'=> '${BOX_ROOT}/NewModule.private.modulemap'
          }

          3、將NewModule.private.modulemap添加到工程目錄;百度App中在NewModule.boxspec中如下代碼設置路徑;

          s.refer_files=[
                "NewModule.private.modulemap",
          ]

          4、將xxxxx.h設置為Private header,百度App中在NewModule.boxspec中如下代碼設置xxxxx.h到Private header

          s.private_headers=[
              "Sources/xxxxx.h"
            ]

          5、調用方式

          import NewModule_Private
          let objectX=xxxxx()
          print(objectX)

          注意:

          • 添加的Private頭文件可能存在傳遞頭文件的情況,即import其他頭文件,也需要將傳遞的頭文件添加到NewModule_Private中,同時import需要使用尖括號;
          • Private Header也會暴露在framework中,所以可以約定外部組件使用Public Header,而避免使用Private Header,因為隨著業務發展和Swift&OC混編,Private Header是不穩定的。

          3.3 組件內OC文件如何調用Swift文件?

          • Swift 類需要繼承 NSObject,方法前面加上@objc 標識,并且是 public 或者 open 的;
          • 引入方式 #import"

          3.4 OC中的向前聲明,被Swift文件引用該組件會報錯

          如error: cannot find protocol definition for 'xxxProtocol'

          • 原因:此報錯在OC中是代碼警告,百度App中默認情況Swift中SWIFT_TREAT_WARNINGS_AS_ERRORS 設置為 YES,導致OC中的Warning視為Error;
          • 解決方案:三選一

          1、暫時設置 SWIFT_TREAT_WARNINGS_AS_ERRORS 為 NO

          2、import xxxProtocol 不要向前聲明

          3、使用 pragma 忽略警告

          3.8 Swift怎么用OC定義的宏?

          • 在Swift中,能直接使用定義為常量的宏,不能使用帶有方法調用的宏,也不能使用靜態常量。
          下面這種定義為常量的宏可以使用
          #define APP_LANGUAGE_EN @"en" 
          #define kNavigationBarHeight 44.0
          
          
          下面帶有方法調用的宏不可以使用
          #define kScreenHeight [[UIScreen mainScreen] bounds].size.height
          #define kScreenWidth [[UIScreen mainScreen] bounds].size.width
          
          
          下面帶有靜態常量swift不能使用,可以改成宏
          static NSString *const StopTabRefreshNotifyNameHtml=@"TabRefreshNotifyNameHtml";

          3.6 Swift與OC泛型的混編

          • 在我分們基礎框架中,有一個使用了OC泛型的類,如:
          @interface BBAXYZ<T> : NSObject <BBAXYZEventProtocol>  
          @property (nonatomic, weak) T page;  
          @end

          這個泛型的使用導致無法使用Swift來繼承和開發BBAXYZ的子類。然而,這個基礎框架是業務的核心部分,因此,我們需要在未來支持Swift的開發。

          • 經過仔細觀察和分析,我們發現泛型主要被用于指定page屬性的類型。因此,我們可以考慮去掉泛型,改為提供一個返回適當類型的方法。這樣,我們就可以在Swift中順利地繼承和使用這個基礎框架。修改后的代碼如下:
          @interface BBAXYZ : NSObject <BBAXYZEventProtocol>  
          - (id<BBAXYZEventProtocol>)page;  
          @end

          然后,我們可以創建一個OC類來實現這個基礎框架,并讓所有的子類繼承這個OC類并實現 page 方法,以返回適當類型的對象。這樣,我們就可以在Swift中順利地繼承和使用這個基礎框架。

          例如:

          @interface BBAABC : BBAXYZ  
          - (UIViewController<BBAXYZEventProtocol> *)page; 
          @end

          需要注意的是,雖然這樣的修改增加了輕量級的中間OC類,但它仍然實現了Swift與OC的混編,并允許我們在Swift中開發新的子類。這種方式既保證了代碼的兼容性,又使得我們可以繼續利用OC的優點。

          • 使用方式
          class BBAEFG: BBAABC {
              
          }

          3.7 Swift調用OC接口,OC的nullability標注使用時的注意事項

          問題場景:

          1、OC 接口定義為 nonnull,swfit 調用時正常是當做不可選類型使用,這時如果 OC 接口不規范返回 nil,則出現運行時崩潰。

          2、OC 接口未定義 nonnull 或 nullable,在這種情況下,編譯器會將 OC 的指針類型當成是隱式解析可選類型(例如 String!)導入到 Swift 中。swift 調用時,OC接口如果返回 nil,將會因為隱式解析一個為 nil 的可選值導致運行時崩潰。

          解決方式:

          1、Swift 調用 OC 接口時,如果 OC 的接口聲明為 nonnull 或未指定 nullability 時,只有明確 OC 接口不為空的情況下才可調用

          2、在 OC 環境下,將 nil 賦值給 nonnull 指針也沒有關系,編譯器只會產生警告。這就需要程序員按規范編寫 OC 代碼,正確使用 nullability 標注,并增加運行時判空的斷言,以支持向后兼容。


          GEEK TALK

          04

          其他常見問題

          4.1 Xcode編譯只提示編譯錯誤,提示信息非常少

          • 原因:使用Swift語言開發的組件,依賴了不支持Module化的組件,導致組件都能編譯成功,但整個工程卻編譯失敗了;
          • 解決方案:二選一

          1、檢查并保障所有依賴的組件都已經Module化了,如配置build settings;

          2、在組件中新增Swift文件(空文件也行)。

          4.2 由于組件開啟了Library Evolution 導致的編譯報錯

          錯誤顯示:@objc' instance method in extension of subclass of 'xxxxx' requires iOS 13.0.0

          這是由于組件開啟了Library Evolution導致,開關BUILD_LIBRARY_FOR_DISTRIBUTION 控制的。

          一個庫開啟了Library Evolution,在依賴鏈下游的庫中將:

          1、對它的類實現 @objc 子類。

          2、對它的類使用 extension 實現 @objc 的方法(這在 UIKit 的 protocol 中經常會遇到)。

          3、對它的類實現子類,并添加 @objc 方法,且方法中使用父類的類型作為參數。

          這些功能是實現的局限。估計是需要在 Swift 運行時有一些對應的更改,所以只在 swift 5.1 (iOS 13)運行時里才可以運行。

          除此之外,還會有一些編譯時沒有報錯,但運行時 crash 或結果不正確的情況。

          百度App中默認開啟Library Evolution,一個組件關閉Library Evolution會導致二進制存在不兼容的情況,暫時無解決方案。

          4.3 暴露的Private頭文件如果使用雙引號import,會報警告,需要修改為尖括號

          如果使用import <xxxx.h>,Project下其他Target就引用不到了,如百度App中Debug模塊引用此Private頭文件時,會報錯 not found with <angled> include, use "quotes" instead。

          • 原因:這是由于其他Target對主模塊引用時默認是從當前項目下引用頭文件,而尖括號方式從系統庫或用戶庫中引用;
          • 解決方案:其他Target將Private Header 配置到其 HEADER_SEARCH_PATHS,使用雙引號和尖括號均可。


          GEEK TALK

          05

          總結

          以上是我們在Swift開發過程中所遇到的一些常見問題及其相應的解決方案。然而,隨著我們不斷深入Swift開發這片浩渺的海洋,更多獨特的問題將會逐漸浮現。我們會持續將這些新問題以及其對應的解決方案整理并發布出來,為廣大的開發者們提供有價值的參考。歡迎大家留言探討。

          作者:路濤、艷紅

          來源:微信公眾號:百度Geek說

          出處:https://mp.weixin.qq.com/s/hHH4a8tOukqhd9A5SxbzYw

          家都說這世上最美的就是玫瑰和女人,當這兩者“碰撞”在一起的時候,一切就變得美不可言了。想必大家也知道很多名人的美都與玫瑰結下了不解之緣,比如超模娜塔莉亞·沃迪亞諾娃。

          可能是現實版的灰姑娘變成公主的故事,娜塔莉亞小時候靠擺水果攤維生,14歲那年在自家水果攤忙活時,被模特星探發掘,飛往法國巴黎,開啟傳奇超模生涯。

          之后的她也經歷過離婚,執意一個人帶著三個孩子生活。但最后和貝爾納·阿爾諾(頂級奢侈品公司LVMH )的兒子安托萬.阿爾諾結婚,目前兩人育有兩個孩子,帶著五個孩子一起生活。當然,于我們而言,她是幸運的。但如果沒有足夠的勤奮和自律,她可能還是一個水果攤的老板娘。

          還記得當時剛生孩子的她,產后第三個星期她就回T臺上了。反正從她出道到現在,肌膚狀態一直都非常的好。看到她為某時尚雜志拍攝的getready with me 視頻之后就發現原來皮膚好,很多原因是在于選對了產品呀。

          可以說她的“本命“就是Fresh了吧,視頻里所有的推薦幾乎都是Fresh的單品呢~而今天小編就要來推薦一下Fresh家的東西了,感覺最近超多仙女急需一瓶好用的水哦,畢竟換季像清倉一樣,是時候買瓶新的水給自己啦。

          推薦產品:Fresh玫瑰花瓣水

          今天小編就是要推薦這款超美的Fresh馥蕾詩玫瑰花瓣水,真真實實的玫瑰花哦,搶先用過的小編表示看到這么多玫瑰花心情都變好了!所以Fresh玫瑰花瓣水怎么樣?

          答案當然是肯定的,光不含酒精這一點就能讓小仙女們愛上了好嘛!而且小編還想說連續使用Fresh馥蕾詩玫瑰花瓣水兩周,肌膚的水潤度真的得到了極大提升哦。連續早晚使用,涂抹瞬間,都能馬上感受到水潤修復的舒適感。

          這瓶玫瑰水中含有超多真實的玫瑰花瓣,也正是因為如此,每天涂的時候都有一股天然的玫瑰花的香味,可以說是每天都在期待著這股玫瑰花的味道,恨不得一天涂N次,讓肌膚和自己都一直在真正的花海中一樣。

          想必有盆友就要問了,Fresh玫瑰花瓣水功效是什么呢?別急,小編這就告訴你們哦。它不僅可以幫助肌膚補充水分,還能細致毛孔,對紅腫的痘痘有消炎的效果哦。同時還能舒緩肌膚,改善膚質。讓肌膚如花瓣一般,再現水潤舒緩、細致柔嫩的肌膚,并為后續的護膚步驟做好準備。

          使用方法也很簡單哦!每日早晚潔凈肌膚后,用化妝棉或指尖沾取適量玫瑰花瓣水,輕柔掃過面部與頸部就可以啦!所以經過小編的一番“講解“,是不是覺得Fresh玫瑰花瓣水好用了呢?在這里小編還想說搭配同系列的Fresh產品效果會更好哦。

          比如,玫瑰潤澤密集保濕面霜可以為肌膚帶來持續潤養,深徹補水。當然再搭配玫瑰潤澤保濕舒緩面膜,用完之后能讓大家的肌膚看上去更加光彩柔嫩哦。大家可以根據自己的肌膚狀況選擇購買哦~這里小編就給你帶來點福利啦,Fresh玫瑰花瓣水體驗裝免費試用申領 ~趕緊點擊鏈接領取吧~

          http://freshrose.comeyes.cn/index.html?utm_source=Onlylady&utm_medium=Onlyladyeditorial&utm_campaign=fresh2018


          主站蜘蛛池模板: 日韩精品一区二区三区中文精品| 后入内射国产一区二区| 国产精品无码一区二区三区在| 国产一区二区三区内射高清| 夜夜嗨AV一区二区三区| 国产精品99精品一区二区三区 | 激情久久av一区av二区av三区| 人妻少妇精品一区二区三区| 久久中文字幕无码一区二区| 自慰无码一区二区三区| 狠狠做深爱婷婷久久综合一区| 中文字幕av一区| 亚洲视频在线一区| 国产一区二区三区久久| 一区二区不卡久久精品| 色系一区二区三区四区五区 | 亚洲福利视频一区二区| 国产第一区二区三区在线观看 | 痴汉中文字幕视频一区| 日本一区二区三区四区视频| 黑巨人与欧美精品一区| 亚洲欧美一区二区三区日产| 在线欧美精品一区二区三区| 91午夜精品亚洲一区二区三区 | 国产一区二区不卡在线播放| 丝袜美腿高跟呻吟高潮一区| 国产在线不卡一区| 人妻体内射精一区二区| 高清一区二区三区视频| 精品视频在线观看你懂的一区 | 亚洲视频在线观看一区| 日韩免费无码视频一区二区三区| 一区高清大胆人体| 国产成人久久精品区一区二区 | 精品视频一区二区三区四区| 国产精品分类视频分类一区 | 亚洲国产精品第一区二区| 亚洲V无码一区二区三区四区观看| 国产一区二区在线视频| 亚洲国产福利精品一区二区| 人妻夜夜爽天天爽一区|